8 #include <gdk/gdkkeysyms.h>
10 typedef struct _Buffer Buffer;
11 typedef struct _View View;
13 static gint untitled_serial = 1;
15 GSList *active_window_stack = NULL;
20 GtkTextBuffer *buffer;
23 GtkTextTag *invisible_tag;
24 GtkTextTag *not_editable_tag;
25 GtkTextTag *found_text_tag;
26 GtkTextTag *custom_tabs_tag;
33 GtkAccelGroup *accel_group;
34 GtkItemFactory *item_factory;
38 static void push_active_window (GtkWindow *window);
39 static void pop_active_window (void);
40 static GtkWindow *get_active_window (void);
42 static Buffer * create_buffer (void);
43 static gboolean check_buffer_saved (Buffer *buffer);
44 static gboolean save_buffer (Buffer *buffer);
45 static gboolean save_as_buffer (Buffer *buffer);
46 static char * buffer_pretty_name (Buffer *buffer);
47 static void buffer_filename_set (Buffer *buffer);
48 static void buffer_search_forward (Buffer *buffer,
51 static void buffer_search_backward (Buffer *buffer,
55 static View *view_from_widget (GtkWidget *widget);
57 static View *create_view (Buffer *buffer);
58 static void check_close_view (View *view);
59 static void close_view (View *view);
60 static void view_set_title (View *view);
61 static void view_init_menus (View *view);
63 GSList *buffers = NULL;
67 push_active_window (GtkWindow *window)
69 gtk_object_ref (GTK_OBJECT (window));
70 active_window_stack = g_slist_prepend (active_window_stack, window);
74 pop_active_window (void)
76 gtk_object_unref (active_window_stack->data);
77 active_window_stack = g_slist_delete_link (active_window_stack, active_window_stack);
81 get_active_window (void)
83 if (active_window_stack)
84 return active_window_stack->data;
90 * Filesel utility function
93 typedef gboolean (*FileselOKFunc) (const char *filename, gpointer data);
96 filesel_ok_cb (GtkWidget *button, GtkWidget *filesel)
98 FileselOKFunc ok_func = gtk_object_get_data (GTK_OBJECT (filesel), "ok-func");
99 gpointer data = gtk_object_get_data (GTK_OBJECT (filesel), "ok-data");
100 gint *result = gtk_object_get_data (GTK_OBJECT (filesel), "ok-result");
102 gtk_widget_hide (filesel);
104 if ((*ok_func) (gtk_file_selection_get_filename (GTK_FILE_SELECTION (filesel)), data))
106 gtk_widget_destroy (filesel);
110 gtk_widget_show (filesel);
114 filesel_run (GtkWindow *parent,
116 const char *start_file,
120 GtkWidget *filesel = gtk_file_selection_new (title);
121 gboolean result = FALSE;
124 parent = get_active_window ();
127 gtk_window_set_transient_for (GTK_WINDOW (filesel), parent);
130 gtk_file_selection_set_filename (GTK_FILE_SELECTION (filesel), start_file);
133 gtk_object_set_data (GTK_OBJECT (filesel), "ok-func", func);
134 gtk_object_set_data (GTK_OBJECT (filesel), "ok-data", data);
135 gtk_object_set_data (GTK_OBJECT (filesel), "ok-result", &result);
137 gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION (filesel)->ok_button),
139 GTK_SIGNAL_FUNC (filesel_ok_cb), filesel);
140 gtk_signal_connect_object (GTK_OBJECT (GTK_FILE_SELECTION (filesel)->cancel_button),
142 GTK_SIGNAL_FUNC (gtk_widget_destroy), GTK_OBJECT (filesel));
144 gtk_signal_connect (GTK_OBJECT (filesel), "destroy",
145 GTK_SIGNAL_FUNC (gtk_main_quit), NULL);
146 gtk_window_set_modal (GTK_WINDOW (filesel), TRUE);
148 gtk_widget_show (filesel);
155 * MsgBox utility functions
159 msgbox_yes_cb (GtkWidget *widget, gboolean *result)
162 gtk_object_destroy (GTK_OBJECT (gtk_widget_get_toplevel (widget)));
166 msgbox_no_cb (GtkWidget *widget, gboolean *result)
169 gtk_object_destroy (GTK_OBJECT (gtk_widget_get_toplevel (widget)));
173 msgbox_key_press_cb (GtkWidget *widget, GdkEventKey *event, gpointer data)
175 if (event->keyval == GDK_Escape)
177 gtk_signal_emit_stop_by_name (GTK_OBJECT (widget), "key_press_event");
178 gtk_object_destroy (GTK_OBJECT (widget));
186 msgbox_run (GtkWindow *parent,
188 const char *yes_button,
189 const char *no_button,
190 const char *cancel_button,
193 gboolean result = -1;
198 GtkWidget *button_box;
199 GtkWidget *separator;
201 g_return_val_if_fail (message != NULL, FALSE);
202 g_return_val_if_fail (default_index >= 0 && default_index <= 1, FALSE);
205 parent = get_active_window ();
209 dialog = gtk_window_new (GTK_WINDOW_TOPLEVEL);
210 gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
212 gtk_window_set_transient_for (GTK_WINDOW (dialog), parent);
213 gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
215 /* Quit our recursive main loop when the dialog is destroyed.
217 gtk_signal_connect (GTK_OBJECT (dialog), "destroy",
218 GTK_SIGNAL_FUNC (gtk_main_quit), NULL);
220 /* Catch Escape key presses and have them destroy the dialog
222 gtk_signal_connect (GTK_OBJECT (dialog), "key_press_event",
223 GTK_SIGNAL_FUNC (msgbox_key_press_cb), NULL);
225 /* Fill in the contents of the widget
227 vbox = gtk_vbox_new (FALSE, 0);
228 gtk_container_add (GTK_CONTAINER (dialog), vbox);
230 label = gtk_label_new (message);
231 gtk_misc_set_padding (GTK_MISC (label), 12, 12);
232 gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
233 gtk_box_pack_start (GTK_BOX (vbox), label, TRUE, TRUE, 0);
235 separator = gtk_hseparator_new ();
236 gtk_box_pack_start (GTK_BOX (vbox), separator, FALSE, FALSE, 0);
238 button_box = gtk_hbutton_box_new ();
239 gtk_box_pack_start (GTK_BOX (vbox), button_box, FALSE, FALSE, 0);
240 gtk_container_set_border_width (GTK_CONTAINER (button_box), 8);
243 /* When Yes is clicked, call the msgbox_yes_cb
244 * This sets the result variable and destroys the dialog
248 button = gtk_button_new_with_label (yes_button);
249 GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
250 gtk_container_add (GTK_CONTAINER (button_box), button);
252 if (default_index == 0)
253 gtk_widget_grab_default (button);
255 gtk_signal_connect (GTK_OBJECT (button), "clicked",
256 GTK_SIGNAL_FUNC (msgbox_yes_cb), &result);
259 /* When No is clicked, call the msgbox_no_cb
260 * This sets the result variable and destroys the dialog
264 button = gtk_button_new_with_label (no_button);
265 GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
266 gtk_container_add (GTK_CONTAINER (button_box), button);
268 if (default_index == 0)
269 gtk_widget_grab_default (button);
271 gtk_signal_connect (GTK_OBJECT (button), "clicked",
272 GTK_SIGNAL_FUNC (msgbox_no_cb), &result);
275 /* When Cancel is clicked, destroy the dialog
279 button = gtk_button_new_with_label (cancel_button);
280 GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
281 gtk_container_add (GTK_CONTAINER (button_box), button);
283 if (default_index == 1)
284 gtk_widget_grab_default (button);
286 gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
287 GTK_SIGNAL_FUNC (gtk_object_destroy), GTK_OBJECT (dialog));
290 gtk_widget_show_all (dialog);
292 /* Run a recursive main loop until a button is clicked
293 * or the user destroys the dialog through the window mananger */
300 * Example buffer filling code
303 blink_timeout (gpointer data)
306 static gboolean flip = FALSE;
308 tag = GTK_TEXT_TAG (data);
310 gtk_object_set (GTK_OBJECT (tag),
311 "foreground", flip ? "blue" : "purple",
320 tag_event_handler (GtkTextTag *tag, GtkWidget *widget, GdkEvent *event,
321 const GtkTextIter *iter, gpointer user_data)
325 char_index = gtk_text_iter_get_offset (iter);
329 case GDK_MOTION_NOTIFY:
330 printf ("Motion event at char %d tag `%s'\n",
331 char_index, tag->name);
334 case GDK_BUTTON_PRESS:
335 printf ("Button press at char %d tag `%s'\n",
336 char_index, tag->name);
339 case GDK_2BUTTON_PRESS:
340 printf ("Double click at char %d tag `%s'\n",
341 char_index, tag->name);
344 case GDK_3BUTTON_PRESS:
345 printf ("Triple click at char %d tag `%s'\n",
346 char_index, tag->name);
349 case GDK_BUTTON_RELEASE:
350 printf ("Button release at char %d tag `%s'\n",
351 char_index, tag->name);
355 case GDK_KEY_RELEASE:
356 printf ("Key event at char %d tag `%s'\n",
357 char_index, tag->name);
360 case GDK_ENTER_NOTIFY:
361 case GDK_LEAVE_NOTIFY:
362 case GDK_PROPERTY_NOTIFY:
363 case GDK_SELECTION_CLEAR:
364 case GDK_SELECTION_REQUEST:
365 case GDK_SELECTION_NOTIFY:
366 case GDK_PROXIMITY_IN:
367 case GDK_PROXIMITY_OUT:
370 case GDK_DRAG_MOTION:
371 case GDK_DRAG_STATUS:
373 case GDK_DROP_FINISHED:
382 setup_tag (GtkTextTag *tag)
385 gtk_signal_connect (GTK_OBJECT (tag),
387 GTK_SIGNAL_FUNC (tag_event_handler),
391 static char *book_closed_xpm[] = {
419 fill_example_buffer (GtkTextBuffer *buffer)
421 GtkTextIter iter, iter2;
429 /* FIXME this is broken if called twice on a buffer, since
430 * we try to create tags a second time.
433 tag = gtk_text_buffer_create_tag (buffer, "fg_blue");
435 /* gtk_timeout_add (1000, blink_timeout, tag); */
439 color.red = color.green = 0;
444 gtk_object_set (GTK_OBJECT (tag),
445 "foreground_gdk", &color,
446 "background_gdk", &color2,
450 tag = gtk_text_buffer_create_tag (buffer, "fg_red");
454 color.blue = color.green = 0;
456 gtk_object_set (GTK_OBJECT (tag),
458 "foreground_gdk", &color,
461 tag = gtk_text_buffer_create_tag (buffer, "bg_green");
465 color.blue = color.red = 0;
466 color.green = 0xffff;
467 gtk_object_set (GTK_OBJECT (tag),
468 "background_gdk", &color,
472 tag = gtk_text_buffer_create_tag (buffer, "strikethrough");
476 gtk_object_set (GTK_OBJECT (tag),
477 "strikethrough", TRUE,
481 tag = gtk_text_buffer_create_tag (buffer, "underline");
485 gtk_object_set (GTK_OBJECT (tag),
486 "underline", PANGO_UNDERLINE_SINGLE,
491 gtk_object_set (GTK_OBJECT (tag),
492 "underline", PANGO_UNDERLINE_SINGLE,
495 tag = gtk_text_buffer_create_tag (buffer, "centered");
497 gtk_object_set (GTK_OBJECT (tag),
498 "justify", GTK_JUSTIFY_CENTER,
501 tag = gtk_text_buffer_create_tag (buffer, "rtl_quote");
503 gtk_object_set (GTK_OBJECT (tag),
504 "wrap_mode", GTK_WRAPMODE_WORD,
505 "direction", GTK_TEXT_DIR_RTL,
506 "left_wrapped_line_margin", 20,
513 pixbuf = gdk_pixbuf_new_from_xpm_data (book_closed_xpm);
518 GtkTextMark * temp_mark;
520 gtk_text_buffer_get_iter_at_offset (buffer, &iter, 0);
522 gtk_text_buffer_insert_pixbuf (buffer, &iter, pixbuf);
524 str = g_strdup_printf ("%d Hello World! blah blah blah blah blah blah blah blah blah blah blah blah\nwoo woo woo woo woo woo woo woo woo woo woo woo woo woo woo\n",
527 gtk_text_buffer_insert (buffer, &iter, str, -1);
531 gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, 0, 5);
533 gtk_text_buffer_insert (buffer, &iter,
534 "(Hello World!)\nfoo foo Hello this is some text we are using to text word wrap. It has punctuation! gee; blah - hmm, great.\nnew line with a significant quantity of text on it. This line really does contain some text. More text! More text! More text!\n"
535 /* This is UTF8 stuff, Emacs doesn't
536 really know how to display it */
537 "German (Deutsch Süd) Grüß Gott Greek (Ελληνικά) Γειά σας Hebrew שלום Japanese (日本語)\n", -1);
540 gtk_text_buffer_create_mark (buffer, "tmp_mark", &iter, TRUE);
543 gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, 0, 6);
544 gtk_text_buffer_get_iter_at_line_offset (buffer, &iter2, 0, 13);
546 gtk_text_buffer_apply_tag_by_name (buffer, "fg_blue", &iter, &iter2);
548 gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, 1, 10);
549 gtk_text_buffer_get_iter_at_line_offset (buffer, &iter2, 1, 16);
551 gtk_text_buffer_apply_tag_by_name (buffer, "underline", &iter, &iter2);
553 gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, 1, 14);
554 gtk_text_buffer_get_iter_at_line_offset (buffer, &iter2, 1, 24);
556 gtk_text_buffer_apply_tag_by_name (buffer, "strikethrough", &iter, &iter2);
558 gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, 0, 9);
559 gtk_text_buffer_get_iter_at_line_offset (buffer, &iter2, 0, 16);
561 gtk_text_buffer_apply_tag_by_name (buffer, "bg_green", &iter, &iter2);
563 gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, 4, 2);
564 gtk_text_buffer_get_iter_at_line_offset (buffer, &iter2, 4, 10);
566 gtk_text_buffer_apply_tag_by_name (buffer, "bg_green", &iter, &iter2);
568 gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, 4, 8);
569 gtk_text_buffer_get_iter_at_line_offset (buffer, &iter2, 4, 15);
571 gtk_text_buffer_apply_tag_by_name (buffer, "fg_red", &iter, &iter2);
574 gtk_text_buffer_get_iter_at_mark (buffer, &iter, temp_mark);
575 gtk_text_buffer_insert (buffer, &iter, "Centered text!\n", -1);
577 gtk_text_buffer_get_iter_at_mark (buffer, &iter2, temp_mark);
578 gtk_text_buffer_apply_tag_by_name (buffer, "centered", &iter2, &iter);
580 gtk_text_buffer_move_mark (buffer, temp_mark, &iter);
581 gtk_text_buffer_insert (buffer, &iter, "Word wrapped, Right-to-left Quote\n", -1);
582 gtk_text_buffer_insert (buffer, &iter, "وقد بدأ ثلاث من أكثر المؤسسات تقدما في شبكة اكسيون برامجها كمنظمات لا تسعى للربح، ثم تحولت في السنوات الخمس الماضية إلى مؤسسات مالية منظمة، وباتت جزءا من النظام المالي في بلدانها، ولكنها تتخصص في خدمة قطاع المشروعات الصغيرة. وأحد أكثر هذه المؤسسات نجاحا هو »بانكوسول« في بوليفيا.\n", -1);
583 gtk_text_buffer_get_iter_at_mark (buffer, &iter2, temp_mark);
584 gtk_text_buffer_apply_tag_by_name (buffer, "rtl_quote", &iter2, &iter);
589 g_object_unref (G_OBJECT (pixbuf));
591 printf ("%d lines %d chars\n",
592 gtk_text_buffer_get_line_count (buffer),
593 gtk_text_buffer_get_char_count (buffer));
595 /* Move cursor to start */
596 gtk_text_buffer_get_iter_at_offset (buffer, &iter, 0);
597 gtk_text_buffer_place_cursor (buffer, &iter);
599 gtk_text_buffer_set_modified (buffer, FALSE);
603 fill_file_buffer (GtkTextBuffer *buffer, const char *filename)
608 GtkTextIter iter, end;
610 f = fopen (filename, "r");
614 gchar *err = g_strdup_printf ("Cannot open file '%s': %s",
615 filename, g_strerror (errno));
616 msgbox_run (NULL, err, "OK", NULL, NULL, 0);
621 gtk_text_buffer_get_iter_at_offset (buffer, &iter, 0);
625 char *leftover, *next;
626 int to_read = 2047 - remaining;
628 count = fread (buf + remaining, 1, to_read, f);
629 buf[count + remaining] = '\0';
631 leftover = next = buf;
638 next = g_utf8_next_char (next);
639 if (next > buf+count+remaining) {
645 gtk_text_buffer_insert (buffer, &iter, buf, leftover - buf);
647 remaining = buf + remaining + count - leftover;
648 g_memmove (buf, leftover, remaining);
650 if (remaining > 6 || count < to_read)
656 gchar *err = g_strdup_printf ("Invalid UTF-8 data encountered reading file '%s'", filename);
657 msgbox_run (NULL, err, "OK", NULL, NULL, 0);
661 /* We had a newline in the buffer to begin with. (The buffer always contains
662 * a newline, so we delete to the end of the buffer to clean up.
664 gtk_text_buffer_get_last_iter (buffer, &end);
665 gtk_text_buffer_delete (buffer, &iter, &end);
667 gtk_text_buffer_set_modified (buffer, FALSE);
673 delete_event_cb (GtkWidget *window, GdkEventAny *event, gpointer data)
675 View *view = view_from_widget (window);
677 push_active_window (GTK_WINDOW (window));
678 check_close_view (view);
679 pop_active_window ();
689 get_empty_view (View *view)
691 if (!view->buffer->filename &&
692 !gtk_text_buffer_modified (view->buffer->buffer))
695 return create_view (create_buffer ());
699 view_from_widget (GtkWidget *widget)
703 if (GTK_IS_MENU_ITEM (widget))
705 GtkItemFactory *item_factory = gtk_item_factory_from_widget (widget);
706 return gtk_object_get_data (GTK_OBJECT (item_factory), "view");
710 GtkWidget *app = gtk_widget_get_toplevel (widget);
711 return gtk_object_get_data (GTK_OBJECT (app), "view");
716 do_new (gpointer callback_data,
717 guint callback_action,
720 create_view (create_buffer ());
724 do_new_view (gpointer callback_data,
725 guint callback_action,
728 View *view = view_from_widget (widget);
730 create_view (view->buffer);
734 open_ok_func (const char *filename, gpointer data)
737 View *new_view = get_empty_view (view);
739 if (!fill_file_buffer (new_view->buffer->buffer, filename))
741 if (new_view != view)
742 close_view (new_view);
747 g_free (new_view->buffer->filename);
748 new_view->buffer->filename = g_strdup (filename);
749 buffer_filename_set (new_view->buffer);
756 do_open (gpointer callback_data,
757 guint callback_action,
760 View *view = view_from_widget (widget);
762 push_active_window (GTK_WINDOW (view->window));
763 filesel_run (NULL, "Open File", NULL, open_ok_func, view);
764 pop_active_window ();
768 do_save_as (gpointer callback_data,
769 guint callback_action,
772 View *view = view_from_widget (widget);
774 push_active_window (GTK_WINDOW (view->window));
775 save_as_buffer (view->buffer);
776 pop_active_window ();
780 do_save (gpointer callback_data,
781 guint callback_action,
784 View *view = view_from_widget (widget);
786 push_active_window (GTK_WINDOW (view->window));
787 if (!view->buffer->filename)
788 do_save_as (callback_data, callback_action, widget);
790 save_buffer (view->buffer);
791 pop_active_window ();
795 do_close (gpointer callback_data,
796 guint callback_action,
799 View *view = view_from_widget (widget);
801 push_active_window (GTK_WINDOW (view->window));
802 check_close_view (view);
803 pop_active_window ();
807 do_exit (gpointer callback_data,
808 guint callback_action,
811 View *view = view_from_widget (widget);
813 GSList *tmp_list = buffers;
815 push_active_window (GTK_WINDOW (view->window));
818 if (!check_buffer_saved (tmp_list->data))
821 tmp_list = tmp_list->next;
825 pop_active_window ();
829 do_example (gpointer callback_data,
830 guint callback_action,
833 View *view = view_from_widget (widget);
836 new_view = get_empty_view (view);
838 fill_example_buffer (new_view->buffer->buffer);
842 do_wrap_changed (gpointer callback_data,
843 guint callback_action,
846 View *view = view_from_widget (widget);
848 gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (view->text_view), callback_action);
852 do_direction_changed (gpointer callback_data,
853 guint callback_action,
856 View *view = view_from_widget (widget);
858 gtk_widget_set_direction (view->text_view, callback_action);
859 gtk_widget_queue_resize (view->text_view);
863 do_editable_changed (gpointer callback_data,
864 guint callback_action,
867 View *view = view_from_widget (widget);
869 gtk_text_view_set_editable (GTK_TEXT_VIEW (view->text_view), callback_action);
873 do_cursor_visible_changed (gpointer callback_data,
874 guint callback_action,
877 View *view = view_from_widget (widget);
879 gtk_text_view_set_cursor_visible (GTK_TEXT_VIEW (view->text_view), callback_action);
883 do_apply_editable (gpointer callback_data,
884 guint callback_action,
887 View *view = view_from_widget (widget);
891 if (gtk_text_buffer_get_selection_bounds (view->buffer->buffer,
896 gtk_text_buffer_remove_tag (view->buffer->buffer,
897 view->buffer->not_editable_tag,
902 gtk_text_buffer_apply_tag (view->buffer->buffer,
903 view->buffer->not_editable_tag,
910 do_apply_invisible (gpointer callback_data,
911 guint callback_action,
914 View *view = view_from_widget (widget);
918 if (gtk_text_buffer_get_selection_bounds (view->buffer->buffer,
923 gtk_text_buffer_remove_tag (view->buffer->buffer,
924 view->buffer->invisible_tag,
929 gtk_text_buffer_apply_tag (view->buffer->buffer,
930 view->buffer->invisible_tag,
937 do_apply_tabs (gpointer callback_data,
938 guint callback_action,
941 View *view = view_from_widget (widget);
945 if (gtk_text_buffer_get_selection_bounds (view->buffer->buffer,
950 gtk_text_buffer_remove_tag (view->buffer->buffer,
951 view->buffer->custom_tabs_tag,
956 gtk_text_buffer_apply_tag (view->buffer->buffer,
957 view->buffer->custom_tabs_tag,
970 dialog_response_callback (GtkWidget *dialog, gint response_id, gpointer data)
972 GtkTextBuffer *buffer;
974 GtkTextIter start, end;
975 gchar *search_string;
977 if (response_id != RESPONSE_FORWARD &&
978 response_id != RESPONSE_BACKWARD)
980 gtk_widget_destroy (dialog);
984 buffer = gtk_object_get_data (GTK_OBJECT (dialog), "buffer");
986 gtk_text_buffer_get_bounds (buffer, &start, &end);
988 /* Remove trailing newline */
989 gtk_text_iter_prev_char (&end);
991 search_string = gtk_text_iter_get_text (&start, &end);
993 printf ("Searching for `%s'\n", search_string);
995 if (response_id == RESPONSE_FORWARD)
996 buffer_search_forward (view->buffer, search_string, view);
997 else if (response_id == RESPONSE_BACKWARD)
998 buffer_search_backward (view->buffer, search_string, view);
1000 g_free (search_string);
1002 gtk_widget_destroy (dialog);
1006 do_search (gpointer callback_data,
1007 guint callback_action,
1010 View *view = view_from_widget (widget);
1012 GtkWidget *search_text;
1013 GtkTextBuffer *buffer;
1015 dialog = gtk_dialog_new_with_buttons ("Search",
1016 GTK_WINDOW (view->window),
1017 GTK_DIALOG_DESTROY_WITH_PARENT,
1018 "Forward", RESPONSE_FORWARD,
1019 "Backward", RESPONSE_BACKWARD,
1020 GTK_STOCK_BUTTON_CANCEL,
1021 GTK_RESPONSE_NONE, NULL);
1024 buffer = gtk_text_buffer_new (NULL);
1026 search_text = gtk_text_view_new_with_buffer (buffer);
1028 g_object_unref (G_OBJECT (buffer));
1030 gtk_box_pack_end (GTK_BOX (GTK_DIALOG (dialog)->vbox),
1034 gtk_object_set_data (GTK_OBJECT (dialog), "buffer", buffer);
1036 gtk_signal_connect (GTK_OBJECT (dialog),
1038 GTK_SIGNAL_FUNC (dialog_response_callback),
1041 gtk_widget_show (search_text);
1043 gtk_widget_grab_focus (search_text);
1045 gtk_widget_show_all (dialog);
1049 view_init_menus (View *view)
1051 GtkTextDirection direction = gtk_widget_get_direction (view->text_view);
1052 GtkWrapMode wrap_mode = gtk_text_view_get_wrap_mode (GTK_TEXT_VIEW (view->text_view));
1053 GtkWidget *menu_item = NULL;
1057 case GTK_TEXT_DIR_LTR:
1058 menu_item = gtk_item_factory_get_widget (view->item_factory, "/Settings/Left-to-Right");
1060 case GTK_TEXT_DIR_RTL:
1061 menu_item = gtk_item_factory_get_widget (view->item_factory, "/Settings/Right-to-Left");
1068 gtk_menu_item_activate (GTK_MENU_ITEM (menu_item));
1072 case GTK_WRAPMODE_NONE:
1073 menu_item = gtk_item_factory_get_widget (view->item_factory, "/Settings/Wrap Off");
1075 case GTK_WRAPMODE_WORD:
1076 menu_item = gtk_item_factory_get_widget (view->item_factory, "/Settings/Wrap Words");
1083 gtk_menu_item_activate (GTK_MENU_ITEM (menu_item));
1086 static GtkItemFactoryEntry menu_items[] =
1088 { "/_File", NULL, 0, 0, "<Branch>" },
1089 { "/File/_New", "<control>N", do_new, 0, NULL },
1090 { "/File/New _View", NULL, do_new_view, 0, NULL },
1091 { "/File/_Open", "<control>O", do_open, 0, NULL },
1092 { "/File/_Save", "<control>S", do_save, 0, NULL },
1093 { "/File/Save _As...", NULL, do_save_as, 0, NULL },
1094 { "/File/sep1", NULL, 0, 0, "<Separator>" },
1095 { "/File/_Close", "<control>W" , do_close, 0, NULL },
1096 { "/File/E_xit", "<control>Q" , do_exit, 0, NULL },
1098 { "/_Edit", NULL, 0, 0, "<Branch>" },
1099 { "/Edit/Find...", NULL, do_search, 0, NULL },
1101 { "/_Settings", NULL, 0, 0, "<Branch>" },
1102 { "/Settings/Wrap _Off", NULL, do_wrap_changed, GTK_WRAPMODE_NONE, "<RadioItem>" },
1103 { "/Settings/Wrap _Words", NULL, do_wrap_changed, GTK_WRAPMODE_WORD, "/Settings/Wrap Off" },
1104 { "/Settings/sep1", NULL, 0, 0, "<Separator>" },
1105 { "/Settings/Editable", NULL, do_editable_changed, TRUE, "<RadioItem>" },
1106 { "/Settings/Not editable", NULL, do_editable_changed, FALSE, "/Settings/Editable" },
1107 { "/Settings/sep1", NULL, 0, 0, "<Separator>" },
1109 { "/Settings/Cursor visible", NULL, do_cursor_visible_changed, TRUE, "<RadioItem>" },
1110 { "/Settings/Cursor not visible", NULL, do_cursor_visible_changed, FALSE, "/Settings/Cursor visible" },
1111 { "/Settings/sep1", NULL, 0, 0, "<Separator>" },
1113 { "/Settings/Left-to-Right", NULL, do_direction_changed, GTK_TEXT_DIR_LTR, "<RadioItem>" },
1114 { "/Settings/Right-to-Left", NULL, do_direction_changed, GTK_TEXT_DIR_RTL, "/Settings/Left-to-Right" },
1115 { "/_Attributes", NULL, 0, 0, "<Branch>" },
1116 { "/Attributes/Editable", NULL, do_apply_editable, TRUE, NULL },
1117 { "/Attributes/Not editable", NULL, do_apply_editable, FALSE, NULL },
1118 { "/Attributes/Invisible", NULL, do_apply_invisible, FALSE, NULL },
1119 { "/Attributes/Visible", NULL, do_apply_invisible, TRUE, NULL },
1120 { "/Attributes/Custom tabs", NULL, do_apply_tabs, FALSE, NULL },
1121 { "/Attributes/Default tabs", NULL, do_apply_tabs, TRUE, NULL },
1122 { "/_Test", NULL, 0, 0, "<Branch>" },
1123 { "/Test/_Example", NULL, do_example, 0, NULL },
1127 save_buffer (Buffer *buffer)
1129 GtkTextIter start, end;
1131 gboolean result = FALSE;
1132 gboolean have_backup = FALSE;
1133 gchar *bak_filename;
1136 g_return_val_if_fail (buffer->filename != NULL, FALSE);
1138 bak_filename = g_strconcat (buffer->filename, "~", NULL);
1140 if (rename (buffer->filename, bak_filename) != 0)
1142 if (errno != ENOENT)
1144 gchar *err = g_strdup_printf ("Cannot back up '%s' to '%s': %s",
1145 buffer->filename, bak_filename, g_strerror (errno));
1146 msgbox_run (NULL, err, "OK", NULL, NULL, 0);
1154 file = fopen (buffer->filename, "w");
1157 gchar *err = g_strdup_printf ("Cannot back up '%s' to '%s': %s",
1158 buffer->filename, bak_filename, g_strerror (errno));
1159 msgbox_run (NULL, err, "OK", NULL, NULL, 0);
1163 gtk_text_buffer_get_iter_at_offset (buffer->buffer, &start, 0);
1164 gtk_text_buffer_get_last_iter (buffer->buffer, &end);
1166 chars = gtk_text_buffer_get_slice (buffer->buffer, &start, &end, FALSE);
1168 if (fputs (chars, file) == EOF ||
1169 fclose (file) == EOF)
1171 gchar *err = g_strdup_printf ("Error writing to '%s': %s",
1172 buffer->filename, g_strerror (errno));
1173 msgbox_run (NULL, err, "OK", NULL, NULL, 0);
1181 gtk_text_buffer_set_modified (buffer->buffer, FALSE);
1187 if (!result && have_backup)
1189 if (rename (bak_filename, buffer->filename) != 0)
1191 gchar *err = g_strdup_printf ("Error restoring backup file '%s' to '%s': %s\nBackup left as '%s'",
1192 buffer->filename, bak_filename, g_strerror (errno), bak_filename);
1193 msgbox_run (NULL, err, "OK", NULL, NULL, 0);
1198 g_free (bak_filename);
1204 save_as_ok_func (const char *filename, gpointer data)
1206 Buffer *buffer = data;
1207 char *old_filename = buffer->filename;
1209 if (!buffer->filename || strcmp (filename, buffer->filename) != 0)
1211 struct stat statbuf;
1213 if (stat (filename, &statbuf) == 0)
1215 gchar *err = g_strdup_printf ("Ovewrite existing file '%s'?", filename);
1216 gint result = msgbox_run (NULL, err, "Yes", "No", NULL, 1);
1224 buffer->filename = g_strdup (filename);
1226 if (save_buffer (buffer))
1228 g_free (old_filename);
1229 buffer_filename_set (buffer);
1234 g_free (buffer->filename);
1235 buffer->filename = old_filename;
1241 save_as_buffer (Buffer *buffer)
1243 return filesel_run (NULL, "Save File", NULL, save_as_ok_func, buffer);
1247 check_buffer_saved (Buffer *buffer)
1249 if (gtk_text_buffer_modified (buffer->buffer))
1251 char *pretty_name = buffer_pretty_name (buffer);
1252 char *msg = g_strdup_printf ("Save changes to '%s'?", pretty_name);
1255 g_free (pretty_name);
1257 result = msgbox_run (NULL, msg, "Yes", "No", "Cancel", 0);
1261 return save_as_buffer (buffer);
1262 else if (result == 1)
1272 create_buffer (void)
1275 PangoTabArray *tabs;
1277 buffer = g_new (Buffer, 1);
1279 buffer->buffer = gtk_text_buffer_new (NULL);
1281 buffer->refcount = 1;
1282 buffer->filename = NULL;
1283 buffer->untitled_serial = -1;
1285 buffer->invisible_tag = gtk_text_buffer_create_tag (buffer->buffer, NULL);
1286 gtk_object_set (GTK_OBJECT (buffer->invisible_tag),
1290 buffer->not_editable_tag = gtk_text_buffer_create_tag (buffer->buffer, NULL);
1291 gtk_object_set (GTK_OBJECT (buffer->not_editable_tag),
1293 "foreground", "purple", NULL);
1295 buffer->found_text_tag = gtk_text_buffer_create_tag (buffer->buffer, NULL);
1296 gtk_object_set (GTK_OBJECT (buffer->found_text_tag),
1297 "foreground", "red", NULL);
1299 tabs = pango_tab_array_new_with_positions (4,
1304 PANGO_TAB_LEFT, 120);
1306 buffer->custom_tabs_tag = gtk_text_buffer_create_tag (buffer->buffer, NULL);
1307 gtk_object_set (GTK_OBJECT (buffer->custom_tabs_tag),
1309 "foreground", "green", NULL);
1311 pango_tab_array_free (tabs);
1313 buffers = g_slist_prepend (buffers, buffer);
1319 buffer_pretty_name (Buffer *buffer)
1321 if (buffer->filename)
1324 char *result = g_path_get_basename (buffer->filename);
1325 p = strchr (result, '/');
1333 if (buffer->untitled_serial == -1)
1334 buffer->untitled_serial = untitled_serial++;
1336 if (buffer->untitled_serial == 1)
1337 return g_strdup ("Untitled");
1339 return g_strdup_printf ("Untitled #%d", buffer->untitled_serial);
1344 buffer_filename_set (Buffer *buffer)
1346 GSList *tmp_list = views;
1350 View *view = tmp_list->data;
1352 if (view->buffer == buffer)
1353 view_set_title (view);
1355 tmp_list = tmp_list->next;
1360 buffer_search (Buffer *buffer,
1366 GtkTextIter start, end;
1370 /* remove tag from whole buffer */
1371 gtk_text_buffer_get_bounds (buffer->buffer, &start, &end);
1372 gtk_text_buffer_remove_tag (buffer->buffer, buffer->found_text_tag,
1375 gtk_text_buffer_get_iter_at_mark (buffer->buffer, &iter,
1376 gtk_text_buffer_get_mark (buffer->buffer,
1382 GtkTextIter match_start, match_end;
1386 while (gtk_text_iter_forward_search (&iter, str, TRUE, FALSE,
1387 &match_start, &match_end,
1391 gtk_text_buffer_apply_tag (buffer->buffer, buffer->found_text_tag,
1392 &match_start, &match_end);
1399 while (gtk_text_iter_backward_search (&iter, str, TRUE, FALSE,
1400 &match_start, &match_end,
1404 gtk_text_buffer_apply_tag (buffer->buffer, buffer->found_text_tag,
1405 &match_start, &match_end);
1412 dialog = gtk_message_dialog_new (GTK_WINDOW (view->window),
1415 GTK_DIALOG_DESTROY_WITH_PARENT,
1416 "%d strings found and marked in red",
1419 gtk_signal_connect_object (GTK_OBJECT (dialog),
1421 GTK_SIGNAL_FUNC (gtk_widget_destroy),
1422 GTK_OBJECT (dialog));
1424 gtk_widget_show (dialog);
1428 buffer_search_forward (Buffer *buffer, const char *str,
1431 buffer_search (buffer, str, view, TRUE);
1435 buffer_search_backward (Buffer *buffer, const char *str,
1438 buffer_search (buffer, str, view, FALSE);
1442 buffer_ref (Buffer *buffer)
1448 buffer_unref (Buffer *buffer)
1451 if (buffer->refcount == 0)
1453 buffers = g_slist_remove (buffers, buffer);
1454 gtk_object_unref (GTK_OBJECT (buffer->buffer));
1455 g_free (buffer->filename);
1461 close_view (View *view)
1463 views = g_slist_remove (views, view);
1464 buffer_unref (view->buffer);
1465 gtk_widget_destroy (view->window);
1466 g_object_unref (G_OBJECT (view->item_factory));
1475 check_close_view (View *view)
1477 if (view->buffer->refcount > 1 ||
1478 check_buffer_saved (view->buffer))
1483 view_set_title (View *view)
1485 char *pretty_name = buffer_pretty_name (view->buffer);
1486 char *title = g_strconcat ("testtext - ", pretty_name, NULL);
1488 gtk_window_set_title (GTK_WINDOW (view->window), title);
1490 g_free (pretty_name);
1495 cursor_set_callback (GtkTextBuffer *buffer,
1496 const GtkTextIter *location,
1500 GtkTextView *text_view;
1502 /* Redraw tab windows if the cursor moves
1503 * on the mapped widget (windows may not exist before realization...
1506 text_view = GTK_TEXT_VIEW (user_data);
1508 if (GTK_WIDGET_MAPPED (text_view) &&
1509 mark == gtk_text_buffer_get_insert (buffer))
1511 GdkWindow *tab_window;
1513 tab_window = gtk_text_view_get_window (text_view,
1514 GTK_TEXT_WINDOW_TOP);
1516 gdk_window_invalidate_rect (tab_window, NULL, FALSE);
1518 tab_window = gtk_text_view_get_window (text_view,
1519 GTK_TEXT_WINDOW_BOTTOM);
1521 gdk_window_invalidate_rect (tab_window, NULL, FALSE);
1527 text_changed_callback (GtkTextBuffer *buffer,
1530 GtkTextView *text_view;
1532 /* Redraw line number windows if the buffer changes
1533 * and the widget is mapped (windows may not exist otherwise)
1536 text_view = GTK_TEXT_VIEW (user_data);
1538 if (GTK_WIDGET_MAPPED (text_view))
1540 GdkWindow *line_window;
1542 line_window = gtk_text_view_get_window (text_view,
1543 GTK_TEXT_WINDOW_LEFT);
1545 gdk_window_invalidate_rect (line_window, NULL, FALSE);
1547 line_window = gtk_text_view_get_window (text_view,
1548 GTK_TEXT_WINDOW_RIGHT);
1550 gdk_window_invalidate_rect (line_window, NULL, FALSE);
1555 tab_stops_expose (GtkWidget *widget,
1556 GdkEventExpose *event,
1563 GdkWindow *bottom_win;
1564 GtkTextView *text_view;
1565 GtkTextWindowType type;
1566 GdkDrawable *target;
1567 gint *positions = NULL;
1569 GtkTextAttributes *attrs;
1571 GtkTextBuffer *buffer;
1574 text_view = GTK_TEXT_VIEW (widget);
1576 /* See if this expose is on the tab stop window */
1577 top_win = gtk_text_view_get_window (text_view,
1578 GTK_TEXT_WINDOW_TOP);
1580 bottom_win = gtk_text_view_get_window (text_view,
1581 GTK_TEXT_WINDOW_BOTTOM);
1583 if (event->window == top_win)
1585 type = GTK_TEXT_WINDOW_TOP;
1588 else if (event->window == bottom_win)
1590 type = GTK_TEXT_WINDOW_BOTTOM;
1591 target = bottom_win;
1596 first_x = event->area.x;
1597 last_x = first_x + event->area.width;
1599 gtk_text_view_window_to_buffer_coords (text_view,
1606 gtk_text_view_window_to_buffer_coords (text_view,
1613 buffer = gtk_text_view_get_buffer (text_view);
1615 gtk_text_buffer_get_iter_at_mark (buffer,
1617 gtk_text_buffer_get_mark (buffer,
1620 attrs = gtk_text_attributes_new ();
1622 gtk_text_iter_get_attributes (&insert, attrs);
1626 size = pango_tab_array_get_size (attrs->tabs);
1628 pango_tab_array_get_tabs (attrs->tabs,
1632 in_pixels = pango_tab_array_get_positions_in_pixels (attrs->tabs);
1640 gtk_text_attributes_unref (attrs);
1648 positions[i] = PANGO_PIXELS (positions[i]);
1650 gtk_text_view_buffer_to_window_coords (text_view,
1657 gdk_draw_line (target,
1658 widget->style->fg_gc [widget->state],
1671 get_lines (GtkTextView *text_view,
1674 GArray *buffer_coords,
1682 g_array_set_size (buffer_coords, 0);
1683 g_array_set_size (numbers, 0);
1685 /* Get iter at first y */
1686 gtk_text_view_get_line_at_y (text_view, &iter, first_y, NULL);
1688 /* For each iter, get its location and add it to the arrays.
1689 * Stop when we pass last_y
1694 while (!gtk_text_iter_is_last (&iter))
1699 gtk_text_view_get_line_yrange (text_view, &iter, &y, &height);
1701 g_array_append_val (buffer_coords, y);
1702 line_num = gtk_text_iter_get_line (&iter);
1703 g_array_append_val (numbers, line_num);
1707 if ((y + height) >= last_y)
1710 gtk_text_iter_forward_line (&iter);
1717 line_numbers_expose (GtkWidget *widget,
1718 GdkEventExpose *event,
1727 GdkWindow *left_win;
1728 GdkWindow *right_win;
1729 PangoLayout *layout;
1730 GtkTextView *text_view;
1731 GtkTextWindowType type;
1732 GdkDrawable *target;
1734 text_view = GTK_TEXT_VIEW (widget);
1736 /* See if this expose is on the line numbers window */
1737 left_win = gtk_text_view_get_window (text_view,
1738 GTK_TEXT_WINDOW_LEFT);
1740 right_win = gtk_text_view_get_window (text_view,
1741 GTK_TEXT_WINDOW_RIGHT);
1743 if (event->window == left_win)
1745 type = GTK_TEXT_WINDOW_LEFT;
1748 else if (event->window == right_win)
1750 type = GTK_TEXT_WINDOW_RIGHT;
1756 first_y = event->area.y;
1757 last_y = first_y + event->area.height;
1759 gtk_text_view_window_to_buffer_coords (text_view,
1766 gtk_text_view_window_to_buffer_coords (text_view,
1773 numbers = g_array_new (FALSE, FALSE, sizeof (gint));
1774 pixels = g_array_new (FALSE, FALSE, sizeof (gint));
1776 get_lines (text_view,
1783 /* Draw fully internationalized numbers! */
1785 layout = gtk_widget_create_pango_layout (widget, "");
1793 gtk_text_view_buffer_to_window_coords (text_view,
1796 g_array_index (pixels, gint, i),
1800 str = g_strdup_printf ("%d", g_array_index (numbers, gint, i));
1802 pango_layout_set_text (layout, str, -1);
1805 gdk_draw_layout (target,
1806 widget->style->fg_gc [widget->state],
1807 /* 2 is just a random padding */
1816 g_array_free (pixels, TRUE);
1817 g_array_free (numbers, TRUE);
1819 g_object_unref (G_OBJECT (layout));
1825 create_view (Buffer *buffer)
1832 view = g_new0 (View, 1);
1833 views = g_slist_prepend (views, view);
1835 view->buffer = buffer;
1836 buffer_ref (buffer);
1838 view->window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
1839 gtk_object_set_data (GTK_OBJECT (view->window), "view", view);
1841 gtk_signal_connect (GTK_OBJECT (view->window), "delete_event",
1842 GTK_SIGNAL_FUNC (delete_event_cb), NULL);
1844 view->accel_group = gtk_accel_group_new ();
1845 view->item_factory = gtk_item_factory_new (GTK_TYPE_MENU_BAR, "<main>", view->accel_group);
1846 gtk_object_set_data (GTK_OBJECT (view->item_factory), "view", view);
1848 gtk_item_factory_create_items (view->item_factory, G_N_ELEMENTS (menu_items), menu_items, view);
1850 gtk_window_add_accel_group (GTK_WINDOW (view->window), view->accel_group);
1852 vbox = gtk_vbox_new (FALSE, 0);
1853 gtk_container_add (GTK_CONTAINER (view->window), vbox);
1855 gtk_box_pack_start (GTK_BOX (vbox),
1856 gtk_item_factory_get_widget (view->item_factory, "<main>"),
1859 sw = gtk_scrolled_window_new (NULL, NULL);
1860 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
1861 GTK_POLICY_AUTOMATIC,
1862 GTK_POLICY_AUTOMATIC);
1864 view->text_view = gtk_text_view_new_with_buffer (buffer->buffer);
1865 gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (view->text_view),
1869 /* Draw tab stops in the top and bottom windows. */
1871 gtk_text_view_set_border_window_size (GTK_TEXT_VIEW (view->text_view),
1872 GTK_TEXT_WINDOW_TOP,
1875 gtk_text_view_set_border_window_size (GTK_TEXT_VIEW (view->text_view),
1876 GTK_TEXT_WINDOW_BOTTOM,
1879 gtk_signal_connect (GTK_OBJECT (view->text_view),
1881 GTK_SIGNAL_FUNC (tab_stops_expose),
1884 gtk_signal_connect (GTK_OBJECT (view->buffer->buffer),
1886 GTK_SIGNAL_FUNC (cursor_set_callback),
1889 /* Draw line numbers in the side windows; we should really be
1890 * more scientific about what width we set them to.
1892 gtk_text_view_set_border_window_size (GTK_TEXT_VIEW (view->text_view),
1893 GTK_TEXT_WINDOW_RIGHT,
1896 gtk_text_view_set_border_window_size (GTK_TEXT_VIEW (view->text_view),
1897 GTK_TEXT_WINDOW_LEFT,
1900 gtk_signal_connect (GTK_OBJECT (view->text_view),
1902 GTK_SIGNAL_FUNC (line_numbers_expose),
1905 gtk_signal_connect (GTK_OBJECT (view->buffer->buffer),
1907 GTK_SIGNAL_FUNC (text_changed_callback),
1910 gtk_box_pack_start (GTK_BOX (vbox), sw, TRUE, TRUE, 0);
1911 gtk_container_add (GTK_CONTAINER (sw), view->text_view);
1913 gtk_window_set_default_size (GTK_WINDOW (view->window), 500, 500);
1915 gtk_widget_grab_focus (view->text_view);
1917 view_set_title (view);
1918 view_init_menus (view);
1920 gtk_widget_show_all (view->window);
1925 file_exists (const char *filename)
1927 struct stat statbuf;
1929 return stat (filename, &statbuf) == 0;
1934 if (file_exists ("../gdk-pixbuf/.libs/libpixbufloader-pnm.so"))
1936 putenv ("GDK_PIXBUF_MODULEDIR=../gdk-pixbuf/.libs");
1937 putenv ("GTK_IM_MODULE_FILE=./gtk.immodules");
1942 main (int argc, char** argv)
1950 gtk_init (&argc, &argv);
1952 buffer = create_buffer ();
1953 view = create_view (buffer);
1954 buffer_unref (buffer);
1956 push_active_window (GTK_WINDOW (view->window));
1957 for (i=1; i < argc; i++)
1961 /* Quick and dirty canonicalization - better should be in GLib
1964 if (!g_path_is_absolute (argv[i]))
1966 char *cwd = g_get_current_dir ();
1967 filename = g_strconcat (cwd, "/", argv[i], NULL);
1973 open_ok_func (filename, view);
1975 if (filename != argv[i])
1978 pop_active_window ();