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 *not_editable_tag;
24 GtkTextTag *found_text_tag;
25 GtkTextTag *custom_tabs_tag;
32 GtkAccelGroup *accel_group;
33 GtkItemFactory *item_factory;
37 static void push_active_window (GtkWindow *window);
38 static void pop_active_window (void);
39 static GtkWindow *get_active_window (void);
41 static Buffer * create_buffer (void);
42 static gboolean check_buffer_saved (Buffer *buffer);
43 static gboolean save_buffer (Buffer *buffer);
44 static gboolean save_as_buffer (Buffer *buffer);
45 static char * buffer_pretty_name (Buffer *buffer);
46 static void buffer_filename_set (Buffer *buffer);
47 static void buffer_search_forward (Buffer *buffer,
51 static View *view_from_widget (GtkWidget *widget);
53 static View *create_view (Buffer *buffer);
54 static void check_close_view (View *view);
55 static void close_view (View *view);
56 static void view_set_title (View *view);
57 static void view_init_menus (View *view);
59 GSList *buffers = NULL;
63 push_active_window (GtkWindow *window)
65 gtk_object_ref (GTK_OBJECT (window));
66 active_window_stack = g_slist_prepend (active_window_stack, window);
70 pop_active_window (void)
72 gtk_object_unref (active_window_stack->data);
73 active_window_stack = g_slist_delete_link (active_window_stack, active_window_stack);
77 get_active_window (void)
79 if (active_window_stack)
80 return active_window_stack->data;
86 * Filesel utility function
89 typedef gboolean (*FileselOKFunc) (const char *filename, gpointer data);
92 filesel_ok_cb (GtkWidget *button, GtkWidget *filesel)
94 FileselOKFunc ok_func = gtk_object_get_data (GTK_OBJECT (filesel), "ok-func");
95 gpointer data = gtk_object_get_data (GTK_OBJECT (filesel), "ok-data");
96 gint *result = gtk_object_get_data (GTK_OBJECT (filesel), "ok-result");
98 gtk_widget_hide (filesel);
100 if ((*ok_func) (gtk_file_selection_get_filename (GTK_FILE_SELECTION (filesel)), data))
102 gtk_widget_destroy (filesel);
106 gtk_widget_show (filesel);
110 filesel_run (GtkWindow *parent,
112 const char *start_file,
116 GtkWidget *filesel = gtk_file_selection_new (title);
117 gboolean result = FALSE;
120 parent = get_active_window ();
123 gtk_window_set_transient_for (GTK_WINDOW (filesel), parent);
126 gtk_file_selection_set_filename (GTK_FILE_SELECTION (filesel), start_file);
129 gtk_object_set_data (GTK_OBJECT (filesel), "ok-func", func);
130 gtk_object_set_data (GTK_OBJECT (filesel), "ok-data", data);
131 gtk_object_set_data (GTK_OBJECT (filesel), "ok-result", &result);
133 gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION (filesel)->ok_button),
135 GTK_SIGNAL_FUNC (filesel_ok_cb), filesel);
136 gtk_signal_connect_object (GTK_OBJECT (GTK_FILE_SELECTION (filesel)->cancel_button),
138 GTK_SIGNAL_FUNC (gtk_widget_destroy), GTK_OBJECT (filesel));
140 gtk_signal_connect (GTK_OBJECT (filesel), "destroy",
141 GTK_SIGNAL_FUNC (gtk_main_quit), NULL);
142 gtk_window_set_modal (GTK_WINDOW (filesel), TRUE);
144 gtk_widget_show (filesel);
151 * MsgBox utility functions
155 msgbox_yes_cb (GtkWidget *widget, gboolean *result)
158 gtk_object_destroy (GTK_OBJECT (gtk_widget_get_toplevel (widget)));
162 msgbox_no_cb (GtkWidget *widget, gboolean *result)
165 gtk_object_destroy (GTK_OBJECT (gtk_widget_get_toplevel (widget)));
169 msgbox_key_press_cb (GtkWidget *widget, GdkEventKey *event, gpointer data)
171 if (event->keyval == GDK_Escape)
173 gtk_signal_emit_stop_by_name (GTK_OBJECT (widget), "key_press_event");
174 gtk_object_destroy (GTK_OBJECT (widget));
182 msgbox_run (GtkWindow *parent,
184 const char *yes_button,
185 const char *no_button,
186 const char *cancel_button,
189 gboolean result = -1;
194 GtkWidget *button_box;
195 GtkWidget *separator;
197 g_return_val_if_fail (message != NULL, FALSE);
198 g_return_val_if_fail (default_index >= 0 && default_index <= 1, FALSE);
201 parent = get_active_window ();
205 dialog = gtk_window_new (GTK_WINDOW_TOPLEVEL);
206 gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
208 gtk_window_set_transient_for (GTK_WINDOW (dialog), parent);
209 gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
211 /* Quit our recursive main loop when the dialog is destroyed.
213 gtk_signal_connect (GTK_OBJECT (dialog), "destroy",
214 GTK_SIGNAL_FUNC (gtk_main_quit), NULL);
216 /* Catch Escape key presses and have them destroy the dialog
218 gtk_signal_connect (GTK_OBJECT (dialog), "key_press_event",
219 GTK_SIGNAL_FUNC (msgbox_key_press_cb), NULL);
221 /* Fill in the contents of the widget
223 vbox = gtk_vbox_new (FALSE, 0);
224 gtk_container_add (GTK_CONTAINER (dialog), vbox);
226 label = gtk_label_new (message);
227 gtk_misc_set_padding (GTK_MISC (label), 12, 12);
228 gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
229 gtk_box_pack_start (GTK_BOX (vbox), label, TRUE, TRUE, 0);
231 separator = gtk_hseparator_new ();
232 gtk_box_pack_start (GTK_BOX (vbox), separator, FALSE, FALSE, 0);
234 button_box = gtk_hbutton_box_new ();
235 gtk_box_pack_start (GTK_BOX (vbox), button_box, FALSE, FALSE, 0);
236 gtk_container_set_border_width (GTK_CONTAINER (button_box), 8);
239 /* When Yes is clicked, call the msgbox_yes_cb
240 * This sets the result variable and destroys the dialog
244 button = gtk_button_new_with_label (yes_button);
245 GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
246 gtk_container_add (GTK_CONTAINER (button_box), button);
248 if (default_index == 0)
249 gtk_widget_grab_default (button);
251 gtk_signal_connect (GTK_OBJECT (button), "clicked",
252 GTK_SIGNAL_FUNC (msgbox_yes_cb), &result);
255 /* When No is clicked, call the msgbox_no_cb
256 * This sets the result variable and destroys the dialog
260 button = gtk_button_new_with_label (no_button);
261 GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
262 gtk_container_add (GTK_CONTAINER (button_box), button);
264 if (default_index == 0)
265 gtk_widget_grab_default (button);
267 gtk_signal_connect (GTK_OBJECT (button), "clicked",
268 GTK_SIGNAL_FUNC (msgbox_no_cb), &result);
271 /* When Cancel is clicked, destroy the dialog
275 button = gtk_button_new_with_label (cancel_button);
276 GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
277 gtk_container_add (GTK_CONTAINER (button_box), button);
279 if (default_index == 1)
280 gtk_widget_grab_default (button);
282 gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
283 GTK_SIGNAL_FUNC (gtk_object_destroy), GTK_OBJECT (dialog));
286 gtk_widget_show_all (dialog);
288 /* Run a recursive main loop until a button is clicked
289 * or the user destroys the dialog through the window mananger */
296 * Example buffer filling code
299 blink_timeout (gpointer data)
302 static gboolean flip = FALSE;
304 tag = GTK_TEXT_TAG (data);
306 gtk_object_set (GTK_OBJECT (tag),
307 "foreground", flip ? "blue" : "purple",
316 tag_event_handler (GtkTextTag *tag, GtkWidget *widget, GdkEvent *event,
317 const GtkTextIter *iter, gpointer user_data)
321 char_index = gtk_text_iter_get_offset (iter);
325 case GDK_MOTION_NOTIFY:
326 printf ("Motion event at char %d tag `%s'\n",
327 char_index, tag->name);
330 case GDK_BUTTON_PRESS:
331 printf ("Button press at char %d tag `%s'\n",
332 char_index, tag->name);
335 case GDK_2BUTTON_PRESS:
336 printf ("Double click at char %d tag `%s'\n",
337 char_index, tag->name);
340 case GDK_3BUTTON_PRESS:
341 printf ("Triple click at char %d tag `%s'\n",
342 char_index, tag->name);
345 case GDK_BUTTON_RELEASE:
346 printf ("Button release at char %d tag `%s'\n",
347 char_index, tag->name);
351 case GDK_KEY_RELEASE:
352 printf ("Key event at char %d tag `%s'\n",
353 char_index, tag->name);
356 case GDK_ENTER_NOTIFY:
357 case GDK_LEAVE_NOTIFY:
358 case GDK_PROPERTY_NOTIFY:
359 case GDK_SELECTION_CLEAR:
360 case GDK_SELECTION_REQUEST:
361 case GDK_SELECTION_NOTIFY:
362 case GDK_PROXIMITY_IN:
363 case GDK_PROXIMITY_OUT:
366 case GDK_DRAG_MOTION:
367 case GDK_DRAG_STATUS:
369 case GDK_DROP_FINISHED:
378 setup_tag (GtkTextTag *tag)
381 gtk_signal_connect (GTK_OBJECT (tag),
383 GTK_SIGNAL_FUNC (tag_event_handler),
387 static char *book_closed_xpm[] = {
415 fill_example_buffer (GtkTextBuffer *buffer)
417 GtkTextIter iter, iter2;
425 tag = gtk_text_buffer_create_tag (buffer, "fg_blue");
427 /* gtk_timeout_add (1000, blink_timeout, tag); */
431 color.red = color.green = 0;
436 gtk_object_set (GTK_OBJECT (tag),
437 "foreground_gdk", &color,
438 "background_gdk", &color2,
442 tag = gtk_text_buffer_create_tag (buffer, "fg_red");
446 color.blue = color.green = 0;
448 gtk_object_set (GTK_OBJECT (tag),
450 "foreground_gdk", &color,
453 tag = gtk_text_buffer_create_tag (buffer, "bg_green");
457 color.blue = color.red = 0;
458 color.green = 0xffff;
459 gtk_object_set (GTK_OBJECT (tag),
460 "background_gdk", &color,
464 tag = gtk_text_buffer_create_tag (buffer, "strikethrough");
468 gtk_object_set (GTK_OBJECT (tag),
469 "strikethrough", TRUE,
473 tag = gtk_text_buffer_create_tag (buffer, "underline");
477 gtk_object_set (GTK_OBJECT (tag),
478 "underline", PANGO_UNDERLINE_SINGLE,
483 gtk_object_set (GTK_OBJECT (tag),
484 "underline", PANGO_UNDERLINE_SINGLE,
487 tag = gtk_text_buffer_create_tag (buffer, "centered");
489 gtk_object_set (GTK_OBJECT (tag),
490 "justify", GTK_JUSTIFY_CENTER,
493 tag = gtk_text_buffer_create_tag (buffer, "rtl_quote");
495 gtk_object_set (GTK_OBJECT (tag),
496 "wrap_mode", GTK_WRAPMODE_WORD,
497 "direction", GTK_TEXT_DIR_RTL,
498 "left_wrapped_line_margin", 20,
505 pixbuf = gdk_pixbuf_new_from_xpm_data (book_closed_xpm);
510 GtkTextMark * temp_mark;
512 gtk_text_buffer_get_iter_at_offset (buffer, &iter, 0);
514 gtk_text_buffer_insert_pixbuf (buffer, &iter, pixbuf);
516 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",
519 gtk_text_buffer_insert (buffer, &iter, str, -1);
523 gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, 0, 5);
525 gtk_text_buffer_insert (buffer, &iter,
526 "(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"
527 /* This is UTF8 stuff, Emacs doesn't
528 really know how to display it */
529 "German (Deutsch Süd) Grüß Gott Greek (Ελληνικά) Γειά σας Hebrew שלום Japanese (日本語)\n", -1);
532 gtk_text_buffer_create_mark (buffer, "tmp_mark", &iter, TRUE);
535 gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, 0, 6);
536 gtk_text_buffer_get_iter_at_line_offset (buffer, &iter2, 0, 13);
538 gtk_text_buffer_apply_tag_by_name (buffer, "fg_blue", &iter, &iter2);
540 gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, 1, 10);
541 gtk_text_buffer_get_iter_at_line_offset (buffer, &iter2, 1, 16);
543 gtk_text_buffer_apply_tag_by_name (buffer, "underline", &iter, &iter2);
545 gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, 1, 14);
546 gtk_text_buffer_get_iter_at_line_offset (buffer, &iter2, 1, 24);
548 gtk_text_buffer_apply_tag_by_name (buffer, "strikethrough", &iter, &iter2);
550 gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, 0, 9);
551 gtk_text_buffer_get_iter_at_line_offset (buffer, &iter2, 0, 16);
553 gtk_text_buffer_apply_tag_by_name (buffer, "bg_green", &iter, &iter2);
555 gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, 4, 2);
556 gtk_text_buffer_get_iter_at_line_offset (buffer, &iter2, 4, 10);
558 gtk_text_buffer_apply_tag_by_name (buffer, "bg_green", &iter, &iter2);
560 gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, 4, 8);
561 gtk_text_buffer_get_iter_at_line_offset (buffer, &iter2, 4, 15);
563 gtk_text_buffer_apply_tag_by_name (buffer, "fg_red", &iter, &iter2);
566 gtk_text_buffer_get_iter_at_mark (buffer, &iter, temp_mark);
567 gtk_text_buffer_insert (buffer, &iter, "Centered text!\n", -1);
569 gtk_text_buffer_get_iter_at_mark (buffer, &iter2, temp_mark);
570 gtk_text_buffer_apply_tag_by_name (buffer, "centered", &iter2, &iter);
572 gtk_text_buffer_move_mark (buffer, temp_mark, &iter);
573 gtk_text_buffer_insert (buffer, &iter, "Word wrapped, Right-to-left Quote\n", -1);
574 gtk_text_buffer_insert (buffer, &iter, "وقد بدأ ثلاث من أكثر المؤسسات تقدما في شبكة اكسيون برامجها كمنظمات لا تسعى للربح، ثم تحولت في السنوات الخمس الماضية إلى مؤسسات مالية منظمة، وباتت جزءا من النظام المالي في بلدانها، ولكنها تتخصص في خدمة قطاع المشروعات الصغيرة. وأحد أكثر هذه المؤسسات نجاحا هو »بانكوسول« في بوليفيا.\n", -1);
575 gtk_text_buffer_get_iter_at_mark (buffer, &iter2, temp_mark);
576 gtk_text_buffer_apply_tag_by_name (buffer, "rtl_quote", &iter2, &iter);
581 g_object_unref (G_OBJECT (pixbuf));
583 printf ("%d lines %d chars\n",
584 gtk_text_buffer_get_line_count (buffer),
585 gtk_text_buffer_get_char_count (buffer));
587 gtk_text_buffer_set_modified (buffer, FALSE);
591 fill_file_buffer (GtkTextBuffer *buffer, const char *filename)
596 GtkTextIter iter, end;
598 f = fopen (filename, "r");
602 gchar *err = g_strdup_printf ("Cannot open file '%s': %s",
603 filename, g_strerror (errno));
604 msgbox_run (NULL, err, "OK", NULL, NULL, 0);
609 gtk_text_buffer_get_iter_at_offset (buffer, &iter, 0);
613 char *leftover, *next;
614 int to_read = 2047 - remaining;
616 count = fread (buf + remaining, 1, to_read, f);
617 buf[count + remaining] = '\0';
619 leftover = next = buf;
626 next = g_utf8_next_char (next);
629 gtk_text_buffer_insert (buffer, &iter, buf, leftover - buf);
631 remaining = buf + remaining + count - leftover;
632 g_memmove (buf, leftover, remaining);
634 if (remaining > 6 || count < to_read)
640 gchar *err = g_strdup_printf ("Invalid UTF-8 data encountered reading file '%s'", filename);
641 msgbox_run (NULL, err, "OK", NULL, NULL, 0);
645 /* We had a newline in the buffer to begin with. (The buffer always contains
646 * a newline, so we delete to the end of the buffer to clean up.
648 gtk_text_buffer_get_last_iter (buffer, &end);
649 gtk_text_buffer_delete (buffer, &iter, &end);
651 gtk_text_buffer_set_modified (buffer, FALSE);
657 delete_event_cb (GtkWidget *window, GdkEventAny *event, gpointer data)
659 View *view = view_from_widget (window);
661 push_active_window (GTK_WINDOW (window));
662 check_close_view (view);
663 pop_active_window ();
673 get_empty_view (View *view)
675 if (!view->buffer->filename &&
676 !gtk_text_buffer_modified (view->buffer->buffer))
679 return create_view (create_buffer ());
683 view_from_widget (GtkWidget *widget)
687 if (GTK_IS_MENU_ITEM (widget))
689 GtkItemFactory *item_factory = gtk_item_factory_from_widget (widget);
690 return gtk_object_get_data (GTK_OBJECT (item_factory), "view");
694 GtkWidget *app = gtk_widget_get_toplevel (widget);
695 return gtk_object_get_data (GTK_OBJECT (app), "view");
700 do_new (gpointer callback_data,
701 guint callback_action,
704 create_view (create_buffer ());
708 do_new_view (gpointer callback_data,
709 guint callback_action,
712 View *view = view_from_widget (widget);
714 create_view (view->buffer);
718 open_ok_func (const char *filename, gpointer data)
721 View *new_view = get_empty_view (view);
723 if (!fill_file_buffer (new_view->buffer->buffer, filename))
725 if (new_view != view)
726 close_view (new_view);
731 g_free (new_view->buffer->filename);
732 new_view->buffer->filename = g_strdup (filename);
733 buffer_filename_set (new_view->buffer);
740 do_open (gpointer callback_data,
741 guint callback_action,
744 View *view = view_from_widget (widget);
746 push_active_window (GTK_WINDOW (view->window));
747 filesel_run (NULL, "Open File", NULL, open_ok_func, view);
748 pop_active_window ();
752 do_save_as (gpointer callback_data,
753 guint callback_action,
756 View *view = view_from_widget (widget);
758 push_active_window (GTK_WINDOW (view->window));
759 save_as_buffer (view->buffer);
760 pop_active_window ();
764 do_save (gpointer callback_data,
765 guint callback_action,
768 View *view = view_from_widget (widget);
770 push_active_window (GTK_WINDOW (view->window));
771 if (!view->buffer->filename)
772 do_save_as (callback_data, callback_action, widget);
774 save_buffer (view->buffer);
775 pop_active_window ();
779 do_close (gpointer callback_data,
780 guint callback_action,
783 View *view = view_from_widget (widget);
785 push_active_window (GTK_WINDOW (view->window));
786 check_close_view (view);
787 pop_active_window ();
791 do_exit (gpointer callback_data,
792 guint callback_action,
795 View *view = view_from_widget (widget);
797 GSList *tmp_list = buffers;
799 push_active_window (GTK_WINDOW (view->window));
802 if (!check_buffer_saved (tmp_list->data))
805 tmp_list = tmp_list->next;
809 pop_active_window ();
813 do_example (gpointer callback_data,
814 guint callback_action,
817 View *view = view_from_widget (widget);
820 new_view = get_empty_view (view);
822 fill_example_buffer (new_view->buffer->buffer);
826 do_wrap_changed (gpointer callback_data,
827 guint callback_action,
830 View *view = view_from_widget (widget);
832 gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (view->text_view), callback_action);
836 do_direction_changed (gpointer callback_data,
837 guint callback_action,
840 View *view = view_from_widget (widget);
842 gtk_widget_set_direction (view->text_view, callback_action);
843 gtk_widget_queue_resize (view->text_view);
847 do_editable_changed (gpointer callback_data,
848 guint callback_action,
851 View *view = view_from_widget (widget);
853 gtk_text_view_set_editable (GTK_TEXT_VIEW (view->text_view), callback_action);
857 do_cursor_visible_changed (gpointer callback_data,
858 guint callback_action,
861 View *view = view_from_widget (widget);
863 gtk_text_view_set_cursor_visible (GTK_TEXT_VIEW (view->text_view), callback_action);
867 do_apply_editable (gpointer callback_data,
868 guint callback_action,
871 View *view = view_from_widget (widget);
875 if (gtk_text_buffer_get_selection_bounds (view->buffer->buffer,
880 gtk_text_buffer_remove_tag (view->buffer->buffer,
881 view->buffer->not_editable_tag,
886 gtk_text_buffer_apply_tag (view->buffer->buffer,
887 view->buffer->not_editable_tag,
895 do_apply_tabs (gpointer callback_data,
896 guint callback_action,
899 View *view = view_from_widget (widget);
903 if (gtk_text_buffer_get_selection_bounds (view->buffer->buffer,
908 gtk_text_buffer_remove_tag (view->buffer->buffer,
909 view->buffer->custom_tabs_tag,
914 gtk_text_buffer_apply_tag (view->buffer->buffer,
915 view->buffer->custom_tabs_tag,
922 dialog_response_callback (GtkWidget *dialog, gint response_id, gpointer data)
924 GtkTextBuffer *buffer;
926 GtkTextIter start, end;
927 gchar *search_string;
929 buffer = gtk_object_get_data (GTK_OBJECT (dialog), "buffer");
931 gtk_text_buffer_get_bounds (buffer, &start, &end);
933 /* Remove trailing newline */
934 gtk_text_iter_prev_char (&end);
936 search_string = gtk_text_iter_get_text (&start, &end);
938 printf ("Searching for `%s'\n", search_string);
940 buffer_search_forward (view->buffer, search_string, view);
942 g_free (search_string);
944 gtk_widget_destroy (dialog);
948 do_search (gpointer callback_data,
949 guint callback_action,
952 View *view = view_from_widget (widget);
954 GtkWidget *search_text;
955 GtkTextBuffer *buffer;
959 dialog = gtk_dialog_new_with_buttons ("Search",
960 GTK_WINDOW (view->window),
961 GTK_DIALOG_DESTROY_WITH_PARENT,
962 GTK_STOCK_BUTTON_CLOSE,
963 GTK_RESPONSE_NONE, NULL);
965 buffer = gtk_text_buffer_new (NULL);
967 /* FIXME memory leak once buffer is a GObject */
968 search_text = gtk_text_view_new_with_buffer (buffer);
970 gtk_box_pack_end (GTK_BOX (GTK_DIALOG (dialog)->vbox),
974 gtk_object_set_data (GTK_OBJECT (dialog), "buffer", buffer);
976 gtk_signal_connect (GTK_OBJECT (dialog),
978 GTK_SIGNAL_FUNC (dialog_response_callback),
981 gtk_widget_show (search_text);
983 gtk_widget_grab_focus (search_text);
985 gtk_widget_show_all (dialog);
990 view_init_menus (View *view)
992 GtkTextDirection direction = gtk_widget_get_direction (view->text_view);
993 GtkWrapMode wrap_mode = gtk_text_view_get_wrap_mode (GTK_TEXT_VIEW (view->text_view));
994 GtkWidget *menu_item = NULL;
998 case GTK_TEXT_DIR_LTR:
999 menu_item = gtk_item_factory_get_widget (view->item_factory, "/Settings/Left-to-Right");
1001 case GTK_TEXT_DIR_RTL:
1002 menu_item = gtk_item_factory_get_widget (view->item_factory, "/Settings/Right-to-Left");
1009 gtk_menu_item_activate (GTK_MENU_ITEM (menu_item));
1013 case GTK_WRAPMODE_NONE:
1014 menu_item = gtk_item_factory_get_widget (view->item_factory, "/Settings/Wrap Off");
1016 case GTK_WRAPMODE_WORD:
1017 menu_item = gtk_item_factory_get_widget (view->item_factory, "/Settings/Wrap Words");
1024 gtk_menu_item_activate (GTK_MENU_ITEM (menu_item));
1027 static GtkItemFactoryEntry menu_items[] =
1029 { "/_File", NULL, 0, 0, "<Branch>" },
1030 { "/File/_New", "<control>N", do_new, 0, NULL },
1031 { "/File/New _View", NULL, do_new_view, 0, NULL },
1032 { "/File/_Open", "<control>O", do_open, 0, NULL },
1033 { "/File/_Save", "<control>S", do_save, 0, NULL },
1034 { "/File/Save _As...", NULL, do_save_as, 0, NULL },
1035 { "/File/sep1", NULL, 0, 0, "<Separator>" },
1036 { "/File/_Close", "<control>W" , do_close, 0, NULL },
1037 { "/File/E_xit", "<control>Q" , do_exit, 0, NULL },
1039 { "/_Edit", NULL, 0, 0, "<Branch>" },
1040 { "/Edit/Find...", NULL, do_search, 0, NULL },
1042 { "/_Settings", NULL, 0, 0, "<Branch>" },
1043 { "/Settings/Wrap _Off", NULL, do_wrap_changed, GTK_WRAPMODE_NONE, "<RadioItem>" },
1044 { "/Settings/Wrap _Words", NULL, do_wrap_changed, GTK_WRAPMODE_WORD, "/Settings/Wrap Off" },
1045 { "/Settings/sep1", NULL, 0, 0, "<Separator>" },
1046 { "/Settings/Editable", NULL, do_editable_changed, TRUE, "<RadioItem>" },
1047 { "/Settings/Not editable", NULL, do_editable_changed, FALSE, "/Settings/Editable" },
1048 { "/Settings/sep1", NULL, 0, 0, "<Separator>" },
1050 { "/Settings/Cursor visible", NULL, do_cursor_visible_changed, TRUE, "<RadioItem>" },
1051 { "/Settings/Cursor not visible", NULL, do_cursor_visible_changed, FALSE, "/Settings/Cursor visible" },
1052 { "/Settings/sep1", NULL, 0, 0, "<Separator>" },
1054 { "/Settings/Left-to-Right", NULL, do_direction_changed, GTK_TEXT_DIR_LTR, "<RadioItem>" },
1055 { "/Settings/Right-to-Left", NULL, do_direction_changed, GTK_TEXT_DIR_RTL, "/Settings/Left-to-Right" },
1056 { "/_Attributes", NULL, 0, 0, "<Branch>" },
1057 { "/Attributes/Editable", NULL, do_apply_editable, TRUE, NULL },
1058 { "/Attributes/Not editable", NULL, do_apply_editable, FALSE, NULL },
1059 { "/Attributes/Custom tabs", NULL, do_apply_tabs, FALSE, NULL },
1060 { "/Attributes/Default tabs", NULL, do_apply_tabs, TRUE, NULL },
1061 { "/_Test", NULL, 0, 0, "<Branch>" },
1062 { "/Test/_Example", NULL, do_example, 0, NULL },
1066 save_buffer (Buffer *buffer)
1068 GtkTextIter start, end;
1070 gboolean result = FALSE;
1071 gboolean have_backup = FALSE;
1072 gchar *bak_filename;
1075 g_return_val_if_fail (buffer->filename != NULL, FALSE);
1077 bak_filename = g_strconcat (buffer->filename, "~", NULL);
1079 if (rename (buffer->filename, bak_filename) != 0)
1081 if (errno != ENOENT)
1083 gchar *err = g_strdup_printf ("Cannot back up '%s' to '%s': %s",
1084 buffer->filename, bak_filename, g_strerror (errno));
1085 msgbox_run (NULL, err, "OK", NULL, NULL, 0);
1094 file = fopen (buffer->filename, "w");
1097 gchar *err = g_strdup_printf ("Cannot back up '%s' to '%s': %s",
1098 buffer->filename, bak_filename, g_strerror (errno));
1099 msgbox_run (NULL, err, "OK", NULL, NULL, 0);
1103 gtk_text_buffer_get_iter_at_offset (buffer->buffer, &start, 0);
1104 gtk_text_buffer_get_last_iter (buffer->buffer, &end);
1106 chars = gtk_text_buffer_get_slice (buffer->buffer, &start, &end, FALSE);
1108 if (fputs (chars, file) == EOF ||
1109 fclose (file) == EOF)
1111 gchar *err = g_strdup_printf ("Error writing to '%s': %s",
1112 buffer->filename, g_strerror (errno));
1113 msgbox_run (NULL, err, "OK", NULL, NULL, 0);
1121 gtk_text_buffer_set_modified (buffer->buffer, FALSE);
1127 if (!result && have_backup)
1129 if (rename (bak_filename, buffer->filename) != 0)
1131 gchar *err = g_strdup_printf ("Error restoring backup file '%s' to '%s': %s\nBackup left as '%s'",
1132 buffer->filename, bak_filename, g_strerror (errno), bak_filename);
1133 msgbox_run (NULL, err, "OK", NULL, NULL, 0);
1138 g_free (bak_filename);
1144 save_as_ok_func (const char *filename, gpointer data)
1146 Buffer *buffer = data;
1147 char *old_filename = buffer->filename;
1149 if (!buffer->filename || strcmp (filename, buffer->filename) != 0)
1151 struct stat statbuf;
1153 if (stat (filename, &statbuf) == 0)
1155 gchar *err = g_strdup_printf ("Ovewrite existing file '%s'?", filename);
1156 gint result = msgbox_run (NULL, err, "Yes", "No", NULL, 1);
1164 buffer->filename = g_strdup (filename);
1166 if (save_buffer (buffer))
1168 g_free (old_filename);
1169 buffer_filename_set (buffer);
1174 g_free (buffer->filename);
1175 buffer->filename = old_filename;
1181 save_as_buffer (Buffer *buffer)
1183 return filesel_run (NULL, "Save File", NULL, save_as_ok_func, buffer);
1187 check_buffer_saved (Buffer *buffer)
1189 if (gtk_text_buffer_modified (buffer->buffer))
1191 char *pretty_name = buffer_pretty_name (buffer);
1192 char *msg = g_strdup_printf ("Save changes to '%s'?", pretty_name);
1195 g_free (pretty_name);
1197 result = msgbox_run (NULL, msg, "Yes", "No", "Cancel", 0);
1201 return save_as_buffer (buffer);
1202 else if (result == 1)
1212 create_buffer (void)
1215 PangoTabArray *tabs;
1217 buffer = g_new (Buffer, 1);
1219 buffer->buffer = gtk_text_buffer_new (NULL);
1220 gtk_object_ref (GTK_OBJECT (buffer->buffer));
1221 gtk_object_sink (GTK_OBJECT (buffer->buffer));
1223 buffer->refcount = 1;
1224 buffer->filename = NULL;
1225 buffer->untitled_serial = -1;
1227 buffer->not_editable_tag = gtk_text_buffer_create_tag (buffer->buffer, NULL);
1228 gtk_object_set (GTK_OBJECT (buffer->not_editable_tag),
1230 "foreground", "purple", NULL);
1232 buffer->found_text_tag = gtk_text_buffer_create_tag (buffer->buffer, NULL);
1233 gtk_object_set (GTK_OBJECT (buffer->found_text_tag),
1234 "foreground", "red", NULL);
1236 tabs = pango_tab_array_new_with_positions (4,
1241 PANGO_TAB_LEFT, 120);
1243 buffer->custom_tabs_tag = gtk_text_buffer_create_tag (buffer->buffer, NULL);
1244 gtk_object_set (GTK_OBJECT (buffer->custom_tabs_tag),
1246 "foreground", "green", NULL);
1248 pango_tab_array_free (tabs);
1250 buffers = g_slist_prepend (buffers, buffer);
1256 buffer_pretty_name (Buffer *buffer)
1258 if (buffer->filename)
1261 char *result = g_path_get_basename (buffer->filename);
1262 p = strchr (result, '/');
1270 if (buffer->untitled_serial == -1)
1271 buffer->untitled_serial = untitled_serial++;
1273 if (buffer->untitled_serial == 1)
1274 return g_strdup ("Untitled");
1276 return g_strdup_printf ("Untitled #%d", buffer->untitled_serial);
1281 buffer_filename_set (Buffer *buffer)
1283 GSList *tmp_list = views;
1287 View *view = tmp_list->data;
1289 if (view->buffer == buffer)
1290 view_set_title (view);
1292 tmp_list = tmp_list->next;
1297 buffer_search_forward (Buffer *buffer, const char *str,
1301 GtkTextIter start, end;
1304 /* remove tag from whole buffer */
1305 gtk_text_buffer_get_bounds (buffer->buffer, &start, &end);
1306 gtk_text_buffer_remove_tag (buffer->buffer, buffer->found_text_tag,
1309 gtk_text_buffer_get_iter_at_mark (buffer->buffer, &iter,
1310 gtk_text_buffer_get_mark (buffer->buffer,
1316 GtkTextIter match_start, match_end;
1318 while (gtk_text_iter_forward_search (&iter, str, TRUE, FALSE,
1319 &match_start, &match_end))
1321 gtk_text_buffer_apply_tag (buffer->buffer, buffer->found_text_tag,
1322 &match_start, &match_end);
1329 dialog = gtk_message_dialog_new (GTK_WINDOW (view->window),
1332 GTK_DIALOG_DESTROY_WITH_PARENT,
1333 "%d strings found and marked in red",
1336 gtk_signal_connect_object (GTK_OBJECT (dialog),
1338 GTK_SIGNAL_FUNC (gtk_widget_destroy),
1339 GTK_OBJECT (dialog));
1341 gtk_widget_show (dialog);
1346 buffer_ref (Buffer *buffer)
1352 buffer_unref (Buffer *buffer)
1355 if (buffer->refcount == 0)
1357 buffers = g_slist_remove (buffers, buffer);
1358 gtk_object_unref (GTK_OBJECT (buffer->buffer));
1359 g_free (buffer->filename);
1365 close_view (View *view)
1367 views = g_slist_remove (views, view);
1368 buffer_unref (view->buffer);
1369 gtk_widget_destroy (view->window);
1370 g_object_unref (G_OBJECT (view->item_factory));
1379 check_close_view (View *view)
1381 if (view->buffer->refcount > 1 ||
1382 check_buffer_saved (view->buffer))
1387 view_set_title (View *view)
1389 char *pretty_name = buffer_pretty_name (view->buffer);
1390 char *title = g_strconcat ("testtext - ", pretty_name, NULL);
1392 gtk_window_set_title (GTK_WINDOW (view->window), title);
1394 g_free (pretty_name);
1399 cursor_set_callback (GtkTextBuffer *buffer,
1400 const GtkTextIter *location,
1404 GtkTextView *text_view;
1406 /* Redraw tab windows if the cursor moves
1407 * on the mapped widget (windows may not exist before realization...
1410 text_view = GTK_TEXT_VIEW (user_data);
1412 if (GTK_WIDGET_MAPPED (text_view) &&
1413 mark == gtk_text_buffer_get_insert (buffer))
1415 GdkWindow *tab_window;
1417 tab_window = gtk_text_view_get_window (text_view,
1418 GTK_TEXT_WINDOW_TOP);
1420 gdk_window_invalidate_rect (tab_window, NULL, FALSE);
1422 tab_window = gtk_text_view_get_window (text_view,
1423 GTK_TEXT_WINDOW_BOTTOM);
1425 gdk_window_invalidate_rect (tab_window, NULL, FALSE);
1431 text_changed_callback (GtkTextBuffer *buffer,
1434 GtkTextView *text_view;
1436 /* Redraw line number windows if the buffer changes
1437 * and the widget is mapped (windows may not exist otherwise)
1440 text_view = GTK_TEXT_VIEW (user_data);
1442 if (GTK_WIDGET_MAPPED (text_view))
1444 GdkWindow *line_window;
1446 line_window = gtk_text_view_get_window (text_view,
1447 GTK_TEXT_WINDOW_LEFT);
1449 gdk_window_invalidate_rect (line_window, NULL, FALSE);
1451 line_window = gtk_text_view_get_window (text_view,
1452 GTK_TEXT_WINDOW_RIGHT);
1454 gdk_window_invalidate_rect (line_window, NULL, FALSE);
1459 tab_stops_expose (GtkWidget *widget,
1460 GdkEventExpose *event,
1467 GdkWindow *bottom_win;
1468 GtkTextView *text_view;
1469 GtkTextWindowType type;
1470 GdkDrawable *target;
1471 gint *positions = NULL;
1473 GtkTextAttributes *attrs;
1475 GtkTextBuffer *buffer;
1478 text_view = GTK_TEXT_VIEW (widget);
1480 /* See if this expose is on the tab stop window */
1481 top_win = gtk_text_view_get_window (text_view,
1482 GTK_TEXT_WINDOW_TOP);
1484 bottom_win = gtk_text_view_get_window (text_view,
1485 GTK_TEXT_WINDOW_BOTTOM);
1487 if (event->window == top_win)
1489 type = GTK_TEXT_WINDOW_TOP;
1492 else if (event->window == bottom_win)
1494 type = GTK_TEXT_WINDOW_BOTTOM;
1495 target = bottom_win;
1500 first_x = event->area.x;
1501 last_x = first_x + event->area.width;
1503 gtk_text_view_window_to_buffer_coords (text_view,
1510 gtk_text_view_window_to_buffer_coords (text_view,
1517 buffer = gtk_text_view_get_buffer (text_view);
1519 gtk_text_buffer_get_iter_at_mark (buffer,
1521 gtk_text_buffer_get_mark (buffer,
1524 attrs = gtk_text_attributes_new ();
1526 gtk_text_iter_get_attributes (&insert, attrs);
1530 size = pango_tab_array_get_size (attrs->tabs);
1532 pango_tab_array_get_tabs (attrs->tabs,
1536 in_pixels = pango_tab_array_get_positions_in_pixels (attrs->tabs);
1544 gtk_text_attributes_unref (attrs);
1552 positions[i] = PANGO_PIXELS (positions[i]);
1554 gtk_text_view_buffer_to_window_coords (text_view,
1561 gdk_draw_line (target,
1562 widget->style->fg_gc [widget->state],
1575 get_lines (GtkTextView *text_view,
1578 GArray *buffer_coords,
1586 g_array_set_size (buffer_coords, 0);
1587 g_array_set_size (numbers, 0);
1589 /* Get iter at first y */
1590 gtk_text_view_get_line_at_y (text_view, &iter, first_y, NULL);
1592 /* For each iter, get its location and add it to the arrays.
1593 * Stop when we pass last_y
1598 while (!gtk_text_iter_is_last (&iter))
1603 gtk_text_view_get_line_yrange (text_view, &iter, &y, &height);
1605 g_array_append_val (buffer_coords, y);
1606 line_num = gtk_text_iter_get_line (&iter);
1607 g_array_append_val (numbers, line_num);
1611 if ((y + height) >= last_y)
1614 gtk_text_iter_forward_line (&iter);
1621 line_numbers_expose (GtkWidget *widget,
1622 GdkEventExpose *event,
1631 GdkWindow *left_win;
1632 GdkWindow *right_win;
1633 PangoLayout *layout;
1634 GtkTextView *text_view;
1635 GtkTextWindowType type;
1636 GdkDrawable *target;
1638 text_view = GTK_TEXT_VIEW (widget);
1640 /* See if this expose is on the line numbers window */
1641 left_win = gtk_text_view_get_window (text_view,
1642 GTK_TEXT_WINDOW_LEFT);
1644 right_win = gtk_text_view_get_window (text_view,
1645 GTK_TEXT_WINDOW_RIGHT);
1647 if (event->window == left_win)
1649 type = GTK_TEXT_WINDOW_LEFT;
1652 else if (event->window == right_win)
1654 type = GTK_TEXT_WINDOW_RIGHT;
1660 first_y = event->area.y;
1661 last_y = first_y + event->area.height;
1663 gtk_text_view_window_to_buffer_coords (text_view,
1670 gtk_text_view_window_to_buffer_coords (text_view,
1677 numbers = g_array_new (FALSE, FALSE, sizeof (gint));
1678 pixels = g_array_new (FALSE, FALSE, sizeof (gint));
1680 get_lines (text_view,
1687 /* Draw fully internationalized numbers! */
1689 layout = gtk_widget_create_pango_layout (widget, "");
1697 gtk_text_view_buffer_to_window_coords (text_view,
1700 g_array_index (pixels, gint, i),
1704 str = g_strdup_printf ("%d", g_array_index (numbers, gint, i));
1706 pango_layout_set_text (layout, str, -1);
1709 gdk_draw_layout (target,
1710 widget->style->fg_gc [widget->state],
1711 /* 2 is just a random padding */
1720 g_array_free (pixels, TRUE);
1721 g_array_free (numbers, TRUE);
1723 g_object_unref (G_OBJECT (layout));
1729 create_view (Buffer *buffer)
1736 view = g_new0 (View, 1);
1737 views = g_slist_prepend (views, view);
1739 view->buffer = buffer;
1740 buffer_ref (buffer);
1742 view->window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
1743 gtk_object_set_data (GTK_OBJECT (view->window), "view", view);
1745 gtk_signal_connect (GTK_OBJECT (view->window), "delete_event",
1746 GTK_SIGNAL_FUNC (delete_event_cb), NULL);
1748 view->accel_group = gtk_accel_group_new ();
1749 view->item_factory = gtk_item_factory_new (GTK_TYPE_MENU_BAR, "<main>", view->accel_group);
1750 gtk_object_set_data (GTK_OBJECT (view->item_factory), "view", view);
1752 gtk_item_factory_create_items (view->item_factory, G_N_ELEMENTS (menu_items), menu_items, view);
1754 gtk_window_add_accel_group (GTK_WINDOW (view->window), view->accel_group);
1756 vbox = gtk_vbox_new (FALSE, 0);
1757 gtk_container_add (GTK_CONTAINER (view->window), vbox);
1759 gtk_box_pack_start (GTK_BOX (vbox),
1760 gtk_item_factory_get_widget (view->item_factory, "<main>"),
1763 sw = gtk_scrolled_window_new (NULL, NULL);
1764 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
1765 GTK_POLICY_AUTOMATIC,
1766 GTK_POLICY_AUTOMATIC);
1768 view->text_view = gtk_text_view_new_with_buffer (buffer->buffer);
1769 gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (view->text_view),
1773 /* Draw tab stops in the top and bottom windows. */
1775 gtk_text_view_set_border_window_size (GTK_TEXT_VIEW (view->text_view),
1776 GTK_TEXT_WINDOW_TOP,
1779 gtk_text_view_set_border_window_size (GTK_TEXT_VIEW (view->text_view),
1780 GTK_TEXT_WINDOW_BOTTOM,
1783 gtk_signal_connect (GTK_OBJECT (view->text_view),
1785 GTK_SIGNAL_FUNC (tab_stops_expose),
1788 gtk_signal_connect (GTK_OBJECT (view->buffer->buffer),
1790 GTK_SIGNAL_FUNC (cursor_set_callback),
1793 /* Draw line numbers in the side windows; we should really be
1794 * more scientific about what width we set them to.
1796 gtk_text_view_set_border_window_size (GTK_TEXT_VIEW (view->text_view),
1797 GTK_TEXT_WINDOW_RIGHT,
1800 gtk_text_view_set_border_window_size (GTK_TEXT_VIEW (view->text_view),
1801 GTK_TEXT_WINDOW_LEFT,
1804 gtk_signal_connect (GTK_OBJECT (view->text_view),
1806 GTK_SIGNAL_FUNC (line_numbers_expose),
1809 gtk_signal_connect (GTK_OBJECT (view->buffer->buffer),
1811 GTK_SIGNAL_FUNC (text_changed_callback),
1814 gtk_box_pack_start (GTK_BOX (vbox), sw, TRUE, TRUE, 0);
1815 gtk_container_add (GTK_CONTAINER (sw), view->text_view);
1817 gtk_window_set_default_size (GTK_WINDOW (view->window), 500, 500);
1819 gtk_widget_grab_focus (view->text_view);
1821 view_set_title (view);
1822 view_init_menus (view);
1824 gtk_widget_show_all (view->window);
1829 main (int argc, char** argv)
1835 gtk_init (&argc, &argv);
1837 buffer = create_buffer ();
1838 view = create_view (buffer);
1839 buffer_unref (buffer);
1841 push_active_window (GTK_WINDOW (view->window));
1842 for (i=1; i < argc; i++)
1846 /* Quick and dirty canonicalization - better should be in GLib
1849 if (!g_path_is_absolute (argv[i]))
1851 char *cwd = g_get_current_dir ();
1852 filename = g_strconcat (cwd, "/", argv[i], NULL);
1858 open_ok_func (filename, view);
1860 if (filename != argv[i])
1863 pop_active_window ();