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;
1306 /* remove tag from whole buffer */
1307 gtk_text_buffer_get_bounds (buffer->buffer, &start, &end);
1308 gtk_text_buffer_remove_tag (buffer->buffer, buffer->found_text_tag,
1311 gtk_text_buffer_get_iter_at_mark (buffer->buffer, &iter,
1312 gtk_text_buffer_get_mark (buffer->buffer,
1316 char_len = g_utf8_strlen (str, -1);
1320 while (gtk_text_iter_forward_search (&iter, str, TRUE, FALSE))
1322 GtkTextIter end = iter;
1324 gtk_text_iter_forward_chars (&end, char_len);
1326 gtk_text_buffer_apply_tag (buffer->buffer, buffer->found_text_tag,
1336 dialog = gtk_message_dialog_new (GTK_WINDOW (view->window),
1339 GTK_DIALOG_DESTROY_WITH_PARENT,
1340 "%d strings found and marked in red",
1343 gtk_signal_connect_object (GTK_OBJECT (dialog),
1345 GTK_SIGNAL_FUNC (gtk_widget_destroy),
1346 GTK_OBJECT (dialog));
1348 gtk_widget_show (dialog);
1353 buffer_ref (Buffer *buffer)
1359 buffer_unref (Buffer *buffer)
1362 if (buffer->refcount == 0)
1364 buffers = g_slist_remove (buffers, buffer);
1365 gtk_object_unref (GTK_OBJECT (buffer->buffer));
1366 g_free (buffer->filename);
1372 close_view (View *view)
1374 views = g_slist_remove (views, view);
1375 buffer_unref (view->buffer);
1376 gtk_widget_destroy (view->window);
1377 g_object_unref (G_OBJECT (view->item_factory));
1386 check_close_view (View *view)
1388 if (view->buffer->refcount > 1 ||
1389 check_buffer_saved (view->buffer))
1394 view_set_title (View *view)
1396 char *pretty_name = buffer_pretty_name (view->buffer);
1397 char *title = g_strconcat ("testtext - ", pretty_name, NULL);
1399 gtk_window_set_title (GTK_WINDOW (view->window), title);
1401 g_free (pretty_name);
1406 cursor_set_callback (GtkTextBuffer *buffer,
1407 const GtkTextIter *location,
1411 GtkTextView *text_view;
1413 /* Redraw tab windows if the cursor moves
1414 * on the mapped widget (windows may not exist before realization...
1417 text_view = GTK_TEXT_VIEW (user_data);
1419 if (GTK_WIDGET_MAPPED (text_view) &&
1420 mark == gtk_text_buffer_get_insert (buffer))
1422 GdkWindow *tab_window;
1424 tab_window = gtk_text_view_get_window (text_view,
1425 GTK_TEXT_WINDOW_TOP);
1427 gdk_window_invalidate_rect (tab_window, NULL, FALSE);
1429 tab_window = gtk_text_view_get_window (text_view,
1430 GTK_TEXT_WINDOW_BOTTOM);
1432 gdk_window_invalidate_rect (tab_window, NULL, FALSE);
1437 tab_stops_expose (GtkWidget *widget,
1438 GdkEventExpose *event,
1445 GdkWindow *bottom_win;
1446 GtkTextView *text_view;
1447 GtkTextWindowType type;
1448 GdkDrawable *target;
1449 gint *positions = NULL;
1451 GtkTextAttributes *attrs;
1453 GtkTextBuffer *buffer;
1456 text_view = GTK_TEXT_VIEW (widget);
1458 /* See if this expose is on the tab stop window */
1459 top_win = gtk_text_view_get_window (text_view,
1460 GTK_TEXT_WINDOW_TOP);
1462 bottom_win = gtk_text_view_get_window (text_view,
1463 GTK_TEXT_WINDOW_BOTTOM);
1465 if (event->window == top_win)
1467 type = GTK_TEXT_WINDOW_TOP;
1470 else if (event->window == bottom_win)
1472 type = GTK_TEXT_WINDOW_BOTTOM;
1473 target = bottom_win;
1478 first_x = event->area.x;
1479 last_x = first_x + event->area.width;
1481 gtk_text_view_window_to_buffer_coords (text_view,
1488 gtk_text_view_window_to_buffer_coords (text_view,
1495 buffer = gtk_text_view_get_buffer (text_view);
1497 gtk_text_buffer_get_iter_at_mark (buffer,
1499 gtk_text_buffer_get_mark (buffer,
1502 attrs = gtk_text_attributes_new ();
1504 gtk_text_iter_get_attributes (&insert, attrs);
1508 size = pango_tab_array_get_size (attrs->tabs);
1510 pango_tab_array_get_tabs (attrs->tabs,
1514 in_pixels = pango_tab_array_get_positions_in_pixels (attrs->tabs);
1522 gtk_text_attributes_unref (attrs);
1530 positions[i] = PANGO_PIXELS (positions[i]);
1532 gtk_text_view_buffer_to_window_coords (text_view,
1539 gdk_draw_line (target,
1540 widget->style->fg_gc [widget->state],
1553 get_lines (GtkTextView *text_view,
1556 GArray *buffer_coords,
1564 g_array_set_size (buffer_coords, 0);
1565 g_array_set_size (numbers, 0);
1567 /* Get iter at first y */
1568 gtk_text_view_get_line_at_y (text_view, &iter, first_y, NULL);
1570 /* For each iter, get its location and add it to the arrays.
1571 * Stop when we pass last_y
1576 while (!gtk_text_iter_is_last (&iter))
1581 gtk_text_view_get_line_yrange (text_view, &iter, &y, &height);
1583 g_array_append_val (buffer_coords, y);
1584 line_num = gtk_text_iter_get_line (&iter);
1585 g_array_append_val (numbers, line_num);
1589 if ((y + height) >= last_y)
1592 gtk_text_iter_forward_line (&iter);
1599 line_numbers_expose (GtkWidget *widget,
1600 GdkEventExpose *event,
1609 GdkWindow *left_win;
1610 GdkWindow *right_win;
1611 PangoLayout *layout;
1612 GtkTextView *text_view;
1613 GtkTextWindowType type;
1614 GdkDrawable *target;
1616 text_view = GTK_TEXT_VIEW (widget);
1618 /* See if this expose is on the line numbers window */
1619 left_win = gtk_text_view_get_window (text_view,
1620 GTK_TEXT_WINDOW_LEFT);
1622 right_win = gtk_text_view_get_window (text_view,
1623 GTK_TEXT_WINDOW_RIGHT);
1625 if (event->window == left_win)
1627 type = GTK_TEXT_WINDOW_LEFT;
1630 else if (event->window == right_win)
1632 type = GTK_TEXT_WINDOW_RIGHT;
1638 first_y = event->area.y;
1639 last_y = first_y + event->area.height;
1641 gtk_text_view_window_to_buffer_coords (text_view,
1648 gtk_text_view_window_to_buffer_coords (text_view,
1655 numbers = g_array_new (FALSE, FALSE, sizeof (gint));
1656 pixels = g_array_new (FALSE, FALSE, sizeof (gint));
1658 get_lines (text_view,
1665 /* Draw fully internationalized numbers! */
1667 layout = gtk_widget_create_pango_layout (widget, "");
1675 gtk_text_view_buffer_to_window_coords (text_view,
1678 g_array_index (pixels, gint, i),
1682 str = g_strdup_printf ("%d", g_array_index (numbers, gint, i));
1684 pango_layout_set_text (layout, str, -1);
1687 gdk_draw_layout (target,
1688 widget->style->fg_gc [widget->state],
1689 /* 2 is just a random padding */
1698 g_array_free (pixels, TRUE);
1699 g_array_free (numbers, TRUE);
1701 g_object_unref (G_OBJECT (layout));
1707 create_view (Buffer *buffer)
1714 view = g_new0 (View, 1);
1715 views = g_slist_prepend (views, view);
1717 view->buffer = buffer;
1718 buffer_ref (buffer);
1720 view->window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
1721 gtk_object_set_data (GTK_OBJECT (view->window), "view", view);
1723 gtk_signal_connect (GTK_OBJECT (view->window), "delete_event",
1724 GTK_SIGNAL_FUNC (delete_event_cb), NULL);
1726 view->accel_group = gtk_accel_group_new ();
1727 view->item_factory = gtk_item_factory_new (GTK_TYPE_MENU_BAR, "<main>", view->accel_group);
1728 gtk_object_set_data (GTK_OBJECT (view->item_factory), "view", view);
1730 gtk_item_factory_create_items (view->item_factory, G_N_ELEMENTS (menu_items), menu_items, view);
1732 gtk_window_add_accel_group (GTK_WINDOW (view->window), view->accel_group);
1734 vbox = gtk_vbox_new (FALSE, 0);
1735 gtk_container_add (GTK_CONTAINER (view->window), vbox);
1737 gtk_box_pack_start (GTK_BOX (vbox),
1738 gtk_item_factory_get_widget (view->item_factory, "<main>"),
1741 sw = gtk_scrolled_window_new (NULL, NULL);
1742 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
1743 GTK_POLICY_AUTOMATIC,
1744 GTK_POLICY_AUTOMATIC);
1746 view->text_view = gtk_text_view_new_with_buffer (buffer->buffer);
1747 gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (view->text_view),
1751 /* Draw tab stops in the top and bottom windows. */
1753 gtk_text_view_set_border_window_size (GTK_TEXT_VIEW (view->text_view),
1754 GTK_TEXT_WINDOW_TOP,
1757 gtk_text_view_set_border_window_size (GTK_TEXT_VIEW (view->text_view),
1758 GTK_TEXT_WINDOW_BOTTOM,
1761 gtk_signal_connect (GTK_OBJECT (view->text_view),
1763 GTK_SIGNAL_FUNC (tab_stops_expose),
1766 gtk_signal_connect (GTK_OBJECT (view->buffer->buffer),
1768 GTK_SIGNAL_FUNC (cursor_set_callback),
1771 /* Draw line numbers in the side windows; we should really be
1772 * more scientific about what width we set them to.
1774 gtk_text_view_set_border_window_size (GTK_TEXT_VIEW (view->text_view),
1775 GTK_TEXT_WINDOW_RIGHT,
1778 gtk_text_view_set_border_window_size (GTK_TEXT_VIEW (view->text_view),
1779 GTK_TEXT_WINDOW_LEFT,
1782 gtk_signal_connect (GTK_OBJECT (view->text_view),
1784 GTK_SIGNAL_FUNC (line_numbers_expose),
1787 gtk_box_pack_start (GTK_BOX (vbox), sw, TRUE, TRUE, 0);
1788 gtk_container_add (GTK_CONTAINER (sw), view->text_view);
1790 gtk_window_set_default_size (GTK_WINDOW (view->window), 500, 500);
1792 gtk_widget_grab_focus (view->text_view);
1794 view_set_title (view);
1795 view_init_menus (view);
1797 gtk_widget_show_all (view->window);
1802 main (int argc, char** argv)
1808 gtk_init (&argc, &argv);
1810 buffer = create_buffer ();
1811 view = create_view (buffer);
1812 buffer_unref (buffer);
1814 push_active_window (GTK_WINDOW (view->window));
1815 for (i=1; i < argc; i++)
1819 /* Quick and dirty canonicalization - better should be in GLib
1822 if (!g_path_is_absolute (argv[i]))
1824 char *cwd = g_get_current_dir ();
1825 filename = g_strconcat (cwd, "/", argv[i], NULL);
1831 open_ok_func (filename, view);
1833 if (filename != argv[i])
1836 pop_active_window ();