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 /* FIXME this is broken if called twice on a buffer, since
426 * we try to create tags a second time.
429 tag = gtk_text_buffer_create_tag (buffer, "fg_blue");
431 /* gtk_timeout_add (1000, blink_timeout, tag); */
435 color.red = color.green = 0;
440 gtk_object_set (GTK_OBJECT (tag),
441 "foreground_gdk", &color,
442 "background_gdk", &color2,
446 tag = gtk_text_buffer_create_tag (buffer, "fg_red");
450 color.blue = color.green = 0;
452 gtk_object_set (GTK_OBJECT (tag),
454 "foreground_gdk", &color,
457 tag = gtk_text_buffer_create_tag (buffer, "bg_green");
461 color.blue = color.red = 0;
462 color.green = 0xffff;
463 gtk_object_set (GTK_OBJECT (tag),
464 "background_gdk", &color,
468 tag = gtk_text_buffer_create_tag (buffer, "strikethrough");
472 gtk_object_set (GTK_OBJECT (tag),
473 "strikethrough", TRUE,
477 tag = gtk_text_buffer_create_tag (buffer, "underline");
481 gtk_object_set (GTK_OBJECT (tag),
482 "underline", PANGO_UNDERLINE_SINGLE,
487 gtk_object_set (GTK_OBJECT (tag),
488 "underline", PANGO_UNDERLINE_SINGLE,
491 tag = gtk_text_buffer_create_tag (buffer, "centered");
493 gtk_object_set (GTK_OBJECT (tag),
494 "justify", GTK_JUSTIFY_CENTER,
497 tag = gtk_text_buffer_create_tag (buffer, "rtl_quote");
499 gtk_object_set (GTK_OBJECT (tag),
500 "wrap_mode", GTK_WRAPMODE_WORD,
501 "direction", GTK_TEXT_DIR_RTL,
502 "left_wrapped_line_margin", 20,
509 pixbuf = gdk_pixbuf_new_from_xpm_data (book_closed_xpm);
514 GtkTextMark * temp_mark;
516 gtk_text_buffer_get_iter_at_offset (buffer, &iter, 0);
518 gtk_text_buffer_insert_pixbuf (buffer, &iter, pixbuf);
520 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",
523 gtk_text_buffer_insert (buffer, &iter, str, -1);
527 gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, 0, 5);
529 gtk_text_buffer_insert (buffer, &iter,
530 "(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"
531 /* This is UTF8 stuff, Emacs doesn't
532 really know how to display it */
533 "German (Deutsch Süd) Grüß Gott Greek (Ελληνικά) Γειά σας Hebrew שלום Japanese (日本語)\n", -1);
536 gtk_text_buffer_create_mark (buffer, "tmp_mark", &iter, TRUE);
539 gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, 0, 6);
540 gtk_text_buffer_get_iter_at_line_offset (buffer, &iter2, 0, 13);
542 gtk_text_buffer_apply_tag_by_name (buffer, "fg_blue", &iter, &iter2);
544 gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, 1, 10);
545 gtk_text_buffer_get_iter_at_line_offset (buffer, &iter2, 1, 16);
547 gtk_text_buffer_apply_tag_by_name (buffer, "underline", &iter, &iter2);
549 gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, 1, 14);
550 gtk_text_buffer_get_iter_at_line_offset (buffer, &iter2, 1, 24);
552 gtk_text_buffer_apply_tag_by_name (buffer, "strikethrough", &iter, &iter2);
554 gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, 0, 9);
555 gtk_text_buffer_get_iter_at_line_offset (buffer, &iter2, 0, 16);
557 gtk_text_buffer_apply_tag_by_name (buffer, "bg_green", &iter, &iter2);
559 gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, 4, 2);
560 gtk_text_buffer_get_iter_at_line_offset (buffer, &iter2, 4, 10);
562 gtk_text_buffer_apply_tag_by_name (buffer, "bg_green", &iter, &iter2);
564 gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, 4, 8);
565 gtk_text_buffer_get_iter_at_line_offset (buffer, &iter2, 4, 15);
567 gtk_text_buffer_apply_tag_by_name (buffer, "fg_red", &iter, &iter2);
570 gtk_text_buffer_get_iter_at_mark (buffer, &iter, temp_mark);
571 gtk_text_buffer_insert (buffer, &iter, "Centered text!\n", -1);
573 gtk_text_buffer_get_iter_at_mark (buffer, &iter2, temp_mark);
574 gtk_text_buffer_apply_tag_by_name (buffer, "centered", &iter2, &iter);
576 gtk_text_buffer_move_mark (buffer, temp_mark, &iter);
577 gtk_text_buffer_insert (buffer, &iter, "Word wrapped, Right-to-left Quote\n", -1);
578 gtk_text_buffer_insert (buffer, &iter, "وقد بدأ ثلاث من أكثر المؤسسات تقدما في شبكة اكسيون برامجها كمنظمات لا تسعى للربح، ثم تحولت في السنوات الخمس الماضية إلى مؤسسات مالية منظمة، وباتت جزءا من النظام المالي في بلدانها، ولكنها تتخصص في خدمة قطاع المشروعات الصغيرة. وأحد أكثر هذه المؤسسات نجاحا هو »بانكوسول« في بوليفيا.\n", -1);
579 gtk_text_buffer_get_iter_at_mark (buffer, &iter2, temp_mark);
580 gtk_text_buffer_apply_tag_by_name (buffer, "rtl_quote", &iter2, &iter);
585 g_object_unref (G_OBJECT (pixbuf));
587 printf ("%d lines %d chars\n",
588 gtk_text_buffer_get_line_count (buffer),
589 gtk_text_buffer_get_char_count (buffer));
591 /* Move cursor to start */
592 gtk_text_buffer_get_iter_at_offset (buffer, &iter, 0);
593 gtk_text_buffer_place_cursor (buffer, &iter);
595 gtk_text_buffer_set_modified (buffer, FALSE);
599 fill_file_buffer (GtkTextBuffer *buffer, const char *filename)
604 GtkTextIter iter, end;
606 f = fopen (filename, "r");
610 gchar *err = g_strdup_printf ("Cannot open file '%s': %s",
611 filename, g_strerror (errno));
612 msgbox_run (NULL, err, "OK", NULL, NULL, 0);
617 gtk_text_buffer_get_iter_at_offset (buffer, &iter, 0);
621 char *leftover, *next;
622 int to_read = 2047 - remaining;
624 count = fread (buf + remaining, 1, to_read, f);
625 buf[count + remaining] = '\0';
627 leftover = next = buf;
634 next = g_utf8_next_char (next);
635 if (next > buf+count+remaining) {
641 gtk_text_buffer_insert (buffer, &iter, buf, leftover - buf);
643 remaining = buf + remaining + count - leftover;
644 g_memmove (buf, leftover, remaining);
646 if (remaining > 6 || count < to_read)
652 gchar *err = g_strdup_printf ("Invalid UTF-8 data encountered reading file '%s'", filename);
653 msgbox_run (NULL, err, "OK", NULL, NULL, 0);
657 /* We had a newline in the buffer to begin with. (The buffer always contains
658 * a newline, so we delete to the end of the buffer to clean up.
660 gtk_text_buffer_get_last_iter (buffer, &end);
661 gtk_text_buffer_delete (buffer, &iter, &end);
663 gtk_text_buffer_set_modified (buffer, FALSE);
669 delete_event_cb (GtkWidget *window, GdkEventAny *event, gpointer data)
671 View *view = view_from_widget (window);
673 push_active_window (GTK_WINDOW (window));
674 check_close_view (view);
675 pop_active_window ();
685 get_empty_view (View *view)
687 if (!view->buffer->filename &&
688 !gtk_text_buffer_modified (view->buffer->buffer))
691 return create_view (create_buffer ());
695 view_from_widget (GtkWidget *widget)
699 if (GTK_IS_MENU_ITEM (widget))
701 GtkItemFactory *item_factory = gtk_item_factory_from_widget (widget);
702 return gtk_object_get_data (GTK_OBJECT (item_factory), "view");
706 GtkWidget *app = gtk_widget_get_toplevel (widget);
707 return gtk_object_get_data (GTK_OBJECT (app), "view");
712 do_new (gpointer callback_data,
713 guint callback_action,
716 create_view (create_buffer ());
720 do_new_view (gpointer callback_data,
721 guint callback_action,
724 View *view = view_from_widget (widget);
726 create_view (view->buffer);
730 open_ok_func (const char *filename, gpointer data)
733 View *new_view = get_empty_view (view);
735 if (!fill_file_buffer (new_view->buffer->buffer, filename))
737 if (new_view != view)
738 close_view (new_view);
743 g_free (new_view->buffer->filename);
744 new_view->buffer->filename = g_strdup (filename);
745 buffer_filename_set (new_view->buffer);
752 do_open (gpointer callback_data,
753 guint callback_action,
756 View *view = view_from_widget (widget);
758 push_active_window (GTK_WINDOW (view->window));
759 filesel_run (NULL, "Open File", NULL, open_ok_func, view);
760 pop_active_window ();
764 do_save_as (gpointer callback_data,
765 guint callback_action,
768 View *view = view_from_widget (widget);
770 push_active_window (GTK_WINDOW (view->window));
771 save_as_buffer (view->buffer);
772 pop_active_window ();
776 do_save (gpointer callback_data,
777 guint callback_action,
780 View *view = view_from_widget (widget);
782 push_active_window (GTK_WINDOW (view->window));
783 if (!view->buffer->filename)
784 do_save_as (callback_data, callback_action, widget);
786 save_buffer (view->buffer);
787 pop_active_window ();
791 do_close (gpointer callback_data,
792 guint callback_action,
795 View *view = view_from_widget (widget);
797 push_active_window (GTK_WINDOW (view->window));
798 check_close_view (view);
799 pop_active_window ();
803 do_exit (gpointer callback_data,
804 guint callback_action,
807 View *view = view_from_widget (widget);
809 GSList *tmp_list = buffers;
811 push_active_window (GTK_WINDOW (view->window));
814 if (!check_buffer_saved (tmp_list->data))
817 tmp_list = tmp_list->next;
821 pop_active_window ();
825 do_example (gpointer callback_data,
826 guint callback_action,
829 View *view = view_from_widget (widget);
832 new_view = get_empty_view (view);
834 fill_example_buffer (new_view->buffer->buffer);
838 do_wrap_changed (gpointer callback_data,
839 guint callback_action,
842 View *view = view_from_widget (widget);
844 gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (view->text_view), callback_action);
848 do_direction_changed (gpointer callback_data,
849 guint callback_action,
852 View *view = view_from_widget (widget);
854 gtk_widget_set_direction (view->text_view, callback_action);
855 gtk_widget_queue_resize (view->text_view);
859 do_editable_changed (gpointer callback_data,
860 guint callback_action,
863 View *view = view_from_widget (widget);
865 gtk_text_view_set_editable (GTK_TEXT_VIEW (view->text_view), callback_action);
869 do_cursor_visible_changed (gpointer callback_data,
870 guint callback_action,
873 View *view = view_from_widget (widget);
875 gtk_text_view_set_cursor_visible (GTK_TEXT_VIEW (view->text_view), callback_action);
879 do_apply_editable (gpointer callback_data,
880 guint callback_action,
883 View *view = view_from_widget (widget);
887 if (gtk_text_buffer_get_selection_bounds (view->buffer->buffer,
892 gtk_text_buffer_remove_tag (view->buffer->buffer,
893 view->buffer->not_editable_tag,
898 gtk_text_buffer_apply_tag (view->buffer->buffer,
899 view->buffer->not_editable_tag,
907 do_apply_tabs (gpointer callback_data,
908 guint callback_action,
911 View *view = view_from_widget (widget);
915 if (gtk_text_buffer_get_selection_bounds (view->buffer->buffer,
920 gtk_text_buffer_remove_tag (view->buffer->buffer,
921 view->buffer->custom_tabs_tag,
926 gtk_text_buffer_apply_tag (view->buffer->buffer,
927 view->buffer->custom_tabs_tag,
934 dialog_response_callback (GtkWidget *dialog, gint response_id, gpointer data)
936 GtkTextBuffer *buffer;
938 GtkTextIter start, end;
939 gchar *search_string;
941 buffer = gtk_object_get_data (GTK_OBJECT (dialog), "buffer");
943 gtk_text_buffer_get_bounds (buffer, &start, &end);
945 /* Remove trailing newline */
946 gtk_text_iter_prev_char (&end);
948 search_string = gtk_text_iter_get_text (&start, &end);
950 printf ("Searching for `%s'\n", search_string);
952 buffer_search_forward (view->buffer, search_string, view);
954 g_free (search_string);
956 gtk_widget_destroy (dialog);
960 do_search (gpointer callback_data,
961 guint callback_action,
964 View *view = view_from_widget (widget);
966 GtkWidget *search_text;
967 GtkTextBuffer *buffer;
969 dialog = gtk_dialog_new_with_buttons ("Search",
970 GTK_WINDOW (view->window),
971 GTK_DIALOG_DESTROY_WITH_PARENT,
972 GTK_STOCK_BUTTON_CLOSE,
973 GTK_RESPONSE_NONE, NULL);
976 buffer = gtk_text_buffer_new (NULL);
978 /* FIXME memory leak once buffer is a GObject */
979 search_text = gtk_text_view_new_with_buffer (buffer);
981 gtk_box_pack_end (GTK_BOX (GTK_DIALOG (dialog)->vbox),
985 gtk_object_set_data (GTK_OBJECT (dialog), "buffer", buffer);
987 gtk_signal_connect (GTK_OBJECT (dialog),
989 GTK_SIGNAL_FUNC (dialog_response_callback),
992 gtk_widget_show (search_text);
994 gtk_widget_grab_focus (search_text);
996 gtk_widget_show_all (dialog);
1000 view_init_menus (View *view)
1002 GtkTextDirection direction = gtk_widget_get_direction (view->text_view);
1003 GtkWrapMode wrap_mode = gtk_text_view_get_wrap_mode (GTK_TEXT_VIEW (view->text_view));
1004 GtkWidget *menu_item = NULL;
1008 case GTK_TEXT_DIR_LTR:
1009 menu_item = gtk_item_factory_get_widget (view->item_factory, "/Settings/Left-to-Right");
1011 case GTK_TEXT_DIR_RTL:
1012 menu_item = gtk_item_factory_get_widget (view->item_factory, "/Settings/Right-to-Left");
1019 gtk_menu_item_activate (GTK_MENU_ITEM (menu_item));
1023 case GTK_WRAPMODE_NONE:
1024 menu_item = gtk_item_factory_get_widget (view->item_factory, "/Settings/Wrap Off");
1026 case GTK_WRAPMODE_WORD:
1027 menu_item = gtk_item_factory_get_widget (view->item_factory, "/Settings/Wrap Words");
1034 gtk_menu_item_activate (GTK_MENU_ITEM (menu_item));
1037 static GtkItemFactoryEntry menu_items[] =
1039 { "/_File", NULL, 0, 0, "<Branch>" },
1040 { "/File/_New", "<control>N", do_new, 0, NULL },
1041 { "/File/New _View", NULL, do_new_view, 0, NULL },
1042 { "/File/_Open", "<control>O", do_open, 0, NULL },
1043 { "/File/_Save", "<control>S", do_save, 0, NULL },
1044 { "/File/Save _As...", NULL, do_save_as, 0, NULL },
1045 { "/File/sep1", NULL, 0, 0, "<Separator>" },
1046 { "/File/_Close", "<control>W" , do_close, 0, NULL },
1047 { "/File/E_xit", "<control>Q" , do_exit, 0, NULL },
1049 { "/_Edit", NULL, 0, 0, "<Branch>" },
1050 { "/Edit/Find...", NULL, do_search, 0, NULL },
1052 { "/_Settings", NULL, 0, 0, "<Branch>" },
1053 { "/Settings/Wrap _Off", NULL, do_wrap_changed, GTK_WRAPMODE_NONE, "<RadioItem>" },
1054 { "/Settings/Wrap _Words", NULL, do_wrap_changed, GTK_WRAPMODE_WORD, "/Settings/Wrap Off" },
1055 { "/Settings/sep1", NULL, 0, 0, "<Separator>" },
1056 { "/Settings/Editable", NULL, do_editable_changed, TRUE, "<RadioItem>" },
1057 { "/Settings/Not editable", NULL, do_editable_changed, FALSE, "/Settings/Editable" },
1058 { "/Settings/sep1", NULL, 0, 0, "<Separator>" },
1060 { "/Settings/Cursor visible", NULL, do_cursor_visible_changed, TRUE, "<RadioItem>" },
1061 { "/Settings/Cursor not visible", NULL, do_cursor_visible_changed, FALSE, "/Settings/Cursor visible" },
1062 { "/Settings/sep1", NULL, 0, 0, "<Separator>" },
1064 { "/Settings/Left-to-Right", NULL, do_direction_changed, GTK_TEXT_DIR_LTR, "<RadioItem>" },
1065 { "/Settings/Right-to-Left", NULL, do_direction_changed, GTK_TEXT_DIR_RTL, "/Settings/Left-to-Right" },
1066 { "/_Attributes", NULL, 0, 0, "<Branch>" },
1067 { "/Attributes/Editable", NULL, do_apply_editable, TRUE, NULL },
1068 { "/Attributes/Not editable", NULL, do_apply_editable, FALSE, NULL },
1069 { "/Attributes/Custom tabs", NULL, do_apply_tabs, FALSE, NULL },
1070 { "/Attributes/Default tabs", NULL, do_apply_tabs, TRUE, NULL },
1071 { "/_Test", NULL, 0, 0, "<Branch>" },
1072 { "/Test/_Example", NULL, do_example, 0, NULL },
1076 save_buffer (Buffer *buffer)
1078 GtkTextIter start, end;
1080 gboolean result = FALSE;
1081 gboolean have_backup = FALSE;
1082 gchar *bak_filename;
1085 g_return_val_if_fail (buffer->filename != NULL, FALSE);
1087 bak_filename = g_strconcat (buffer->filename, "~", NULL);
1089 if (rename (buffer->filename, bak_filename) != 0)
1091 if (errno != ENOENT)
1093 gchar *err = g_strdup_printf ("Cannot back up '%s' to '%s': %s",
1094 buffer->filename, bak_filename, g_strerror (errno));
1095 msgbox_run (NULL, err, "OK", NULL, NULL, 0);
1103 file = fopen (buffer->filename, "w");
1106 gchar *err = g_strdup_printf ("Cannot back up '%s' to '%s': %s",
1107 buffer->filename, bak_filename, g_strerror (errno));
1108 msgbox_run (NULL, err, "OK", NULL, NULL, 0);
1112 gtk_text_buffer_get_iter_at_offset (buffer->buffer, &start, 0);
1113 gtk_text_buffer_get_last_iter (buffer->buffer, &end);
1115 chars = gtk_text_buffer_get_slice (buffer->buffer, &start, &end, FALSE);
1117 if (fputs (chars, file) == EOF ||
1118 fclose (file) == EOF)
1120 gchar *err = g_strdup_printf ("Error writing to '%s': %s",
1121 buffer->filename, g_strerror (errno));
1122 msgbox_run (NULL, err, "OK", NULL, NULL, 0);
1130 gtk_text_buffer_set_modified (buffer->buffer, FALSE);
1136 if (!result && have_backup)
1138 if (rename (bak_filename, buffer->filename) != 0)
1140 gchar *err = g_strdup_printf ("Error restoring backup file '%s' to '%s': %s\nBackup left as '%s'",
1141 buffer->filename, bak_filename, g_strerror (errno), bak_filename);
1142 msgbox_run (NULL, err, "OK", NULL, NULL, 0);
1147 g_free (bak_filename);
1153 save_as_ok_func (const char *filename, gpointer data)
1155 Buffer *buffer = data;
1156 char *old_filename = buffer->filename;
1158 if (!buffer->filename || strcmp (filename, buffer->filename) != 0)
1160 struct stat statbuf;
1162 if (stat (filename, &statbuf) == 0)
1164 gchar *err = g_strdup_printf ("Ovewrite existing file '%s'?", filename);
1165 gint result = msgbox_run (NULL, err, "Yes", "No", NULL, 1);
1173 buffer->filename = g_strdup (filename);
1175 if (save_buffer (buffer))
1177 g_free (old_filename);
1178 buffer_filename_set (buffer);
1183 g_free (buffer->filename);
1184 buffer->filename = old_filename;
1190 save_as_buffer (Buffer *buffer)
1192 return filesel_run (NULL, "Save File", NULL, save_as_ok_func, buffer);
1196 check_buffer_saved (Buffer *buffer)
1198 if (gtk_text_buffer_modified (buffer->buffer))
1200 char *pretty_name = buffer_pretty_name (buffer);
1201 char *msg = g_strdup_printf ("Save changes to '%s'?", pretty_name);
1204 g_free (pretty_name);
1206 result = msgbox_run (NULL, msg, "Yes", "No", "Cancel", 0);
1210 return save_as_buffer (buffer);
1211 else if (result == 1)
1221 create_buffer (void)
1224 PangoTabArray *tabs;
1226 buffer = g_new (Buffer, 1);
1228 buffer->buffer = gtk_text_buffer_new (NULL);
1229 gtk_object_ref (GTK_OBJECT (buffer->buffer));
1230 gtk_object_sink (GTK_OBJECT (buffer->buffer));
1232 buffer->refcount = 1;
1233 buffer->filename = NULL;
1234 buffer->untitled_serial = -1;
1236 buffer->not_editable_tag = gtk_text_buffer_create_tag (buffer->buffer, NULL);
1237 gtk_object_set (GTK_OBJECT (buffer->not_editable_tag),
1239 "foreground", "purple", NULL);
1241 buffer->found_text_tag = gtk_text_buffer_create_tag (buffer->buffer, NULL);
1242 gtk_object_set (GTK_OBJECT (buffer->found_text_tag),
1243 "foreground", "red", NULL);
1245 tabs = pango_tab_array_new_with_positions (4,
1250 PANGO_TAB_LEFT, 120);
1252 buffer->custom_tabs_tag = gtk_text_buffer_create_tag (buffer->buffer, NULL);
1253 gtk_object_set (GTK_OBJECT (buffer->custom_tabs_tag),
1255 "foreground", "green", NULL);
1257 pango_tab_array_free (tabs);
1259 buffers = g_slist_prepend (buffers, buffer);
1265 buffer_pretty_name (Buffer *buffer)
1267 if (buffer->filename)
1270 char *result = g_path_get_basename (buffer->filename);
1271 p = strchr (result, '/');
1279 if (buffer->untitled_serial == -1)
1280 buffer->untitled_serial = untitled_serial++;
1282 if (buffer->untitled_serial == 1)
1283 return g_strdup ("Untitled");
1285 return g_strdup_printf ("Untitled #%d", buffer->untitled_serial);
1290 buffer_filename_set (Buffer *buffer)
1292 GSList *tmp_list = views;
1296 View *view = tmp_list->data;
1298 if (view->buffer == buffer)
1299 view_set_title (view);
1301 tmp_list = tmp_list->next;
1306 buffer_search_forward (Buffer *buffer, const char *str,
1310 GtkTextIter start, end;
1314 /* remove tag from whole buffer */
1315 gtk_text_buffer_get_bounds (buffer->buffer, &start, &end);
1316 gtk_text_buffer_remove_tag (buffer->buffer, buffer->found_text_tag,
1319 gtk_text_buffer_get_iter_at_mark (buffer->buffer, &iter,
1320 gtk_text_buffer_get_mark (buffer->buffer,
1326 GtkTextIter match_start, match_end;
1328 while (gtk_text_iter_forward_search (&iter, str, TRUE, FALSE,
1329 &match_start, &match_end))
1332 gtk_text_buffer_apply_tag (buffer->buffer, buffer->found_text_tag,
1333 &match_start, &match_end);
1339 dialog = gtk_message_dialog_new (GTK_WINDOW (view->window),
1342 GTK_DIALOG_DESTROY_WITH_PARENT,
1343 "%d strings found and marked in red",
1346 gtk_signal_connect_object (GTK_OBJECT (dialog),
1348 GTK_SIGNAL_FUNC (gtk_widget_destroy),
1349 GTK_OBJECT (dialog));
1351 gtk_widget_show (dialog);
1355 buffer_ref (Buffer *buffer)
1361 buffer_unref (Buffer *buffer)
1364 if (buffer->refcount == 0)
1366 buffers = g_slist_remove (buffers, buffer);
1367 gtk_object_unref (GTK_OBJECT (buffer->buffer));
1368 g_free (buffer->filename);
1374 close_view (View *view)
1376 views = g_slist_remove (views, view);
1377 buffer_unref (view->buffer);
1378 gtk_widget_destroy (view->window);
1379 g_object_unref (G_OBJECT (view->item_factory));
1388 check_close_view (View *view)
1390 if (view->buffer->refcount > 1 ||
1391 check_buffer_saved (view->buffer))
1396 view_set_title (View *view)
1398 char *pretty_name = buffer_pretty_name (view->buffer);
1399 char *title = g_strconcat ("testtext - ", pretty_name, NULL);
1401 gtk_window_set_title (GTK_WINDOW (view->window), title);
1403 g_free (pretty_name);
1408 cursor_set_callback (GtkTextBuffer *buffer,
1409 const GtkTextIter *location,
1413 GtkTextView *text_view;
1415 /* Redraw tab windows if the cursor moves
1416 * on the mapped widget (windows may not exist before realization...
1419 text_view = GTK_TEXT_VIEW (user_data);
1421 if (GTK_WIDGET_MAPPED (text_view) &&
1422 mark == gtk_text_buffer_get_insert (buffer))
1424 GdkWindow *tab_window;
1426 tab_window = gtk_text_view_get_window (text_view,
1427 GTK_TEXT_WINDOW_TOP);
1429 gdk_window_invalidate_rect (tab_window, NULL, FALSE);
1431 tab_window = gtk_text_view_get_window (text_view,
1432 GTK_TEXT_WINDOW_BOTTOM);
1434 gdk_window_invalidate_rect (tab_window, NULL, FALSE);
1440 text_changed_callback (GtkTextBuffer *buffer,
1443 GtkTextView *text_view;
1445 /* Redraw line number windows if the buffer changes
1446 * and the widget is mapped (windows may not exist otherwise)
1449 text_view = GTK_TEXT_VIEW (user_data);
1451 if (GTK_WIDGET_MAPPED (text_view))
1453 GdkWindow *line_window;
1455 line_window = gtk_text_view_get_window (text_view,
1456 GTK_TEXT_WINDOW_LEFT);
1458 gdk_window_invalidate_rect (line_window, NULL, FALSE);
1460 line_window = gtk_text_view_get_window (text_view,
1461 GTK_TEXT_WINDOW_RIGHT);
1463 gdk_window_invalidate_rect (line_window, NULL, FALSE);
1468 tab_stops_expose (GtkWidget *widget,
1469 GdkEventExpose *event,
1476 GdkWindow *bottom_win;
1477 GtkTextView *text_view;
1478 GtkTextWindowType type;
1479 GdkDrawable *target;
1480 gint *positions = NULL;
1482 GtkTextAttributes *attrs;
1484 GtkTextBuffer *buffer;
1487 text_view = GTK_TEXT_VIEW (widget);
1489 /* See if this expose is on the tab stop window */
1490 top_win = gtk_text_view_get_window (text_view,
1491 GTK_TEXT_WINDOW_TOP);
1493 bottom_win = gtk_text_view_get_window (text_view,
1494 GTK_TEXT_WINDOW_BOTTOM);
1496 if (event->window == top_win)
1498 type = GTK_TEXT_WINDOW_TOP;
1501 else if (event->window == bottom_win)
1503 type = GTK_TEXT_WINDOW_BOTTOM;
1504 target = bottom_win;
1509 first_x = event->area.x;
1510 last_x = first_x + event->area.width;
1512 gtk_text_view_window_to_buffer_coords (text_view,
1519 gtk_text_view_window_to_buffer_coords (text_view,
1526 buffer = gtk_text_view_get_buffer (text_view);
1528 gtk_text_buffer_get_iter_at_mark (buffer,
1530 gtk_text_buffer_get_mark (buffer,
1533 attrs = gtk_text_attributes_new ();
1535 gtk_text_iter_get_attributes (&insert, attrs);
1539 size = pango_tab_array_get_size (attrs->tabs);
1541 pango_tab_array_get_tabs (attrs->tabs,
1545 in_pixels = pango_tab_array_get_positions_in_pixels (attrs->tabs);
1553 gtk_text_attributes_unref (attrs);
1561 positions[i] = PANGO_PIXELS (positions[i]);
1563 gtk_text_view_buffer_to_window_coords (text_view,
1570 gdk_draw_line (target,
1571 widget->style->fg_gc [widget->state],
1584 get_lines (GtkTextView *text_view,
1587 GArray *buffer_coords,
1595 g_array_set_size (buffer_coords, 0);
1596 g_array_set_size (numbers, 0);
1598 /* Get iter at first y */
1599 gtk_text_view_get_line_at_y (text_view, &iter, first_y, NULL);
1601 /* For each iter, get its location and add it to the arrays.
1602 * Stop when we pass last_y
1607 while (!gtk_text_iter_is_last (&iter))
1612 gtk_text_view_get_line_yrange (text_view, &iter, &y, &height);
1614 g_array_append_val (buffer_coords, y);
1615 line_num = gtk_text_iter_get_line (&iter);
1616 g_array_append_val (numbers, line_num);
1620 if ((y + height) >= last_y)
1623 gtk_text_iter_forward_line (&iter);
1630 line_numbers_expose (GtkWidget *widget,
1631 GdkEventExpose *event,
1640 GdkWindow *left_win;
1641 GdkWindow *right_win;
1642 PangoLayout *layout;
1643 GtkTextView *text_view;
1644 GtkTextWindowType type;
1645 GdkDrawable *target;
1647 text_view = GTK_TEXT_VIEW (widget);
1649 /* See if this expose is on the line numbers window */
1650 left_win = gtk_text_view_get_window (text_view,
1651 GTK_TEXT_WINDOW_LEFT);
1653 right_win = gtk_text_view_get_window (text_view,
1654 GTK_TEXT_WINDOW_RIGHT);
1656 if (event->window == left_win)
1658 type = GTK_TEXT_WINDOW_LEFT;
1661 else if (event->window == right_win)
1663 type = GTK_TEXT_WINDOW_RIGHT;
1669 first_y = event->area.y;
1670 last_y = first_y + event->area.height;
1672 gtk_text_view_window_to_buffer_coords (text_view,
1679 gtk_text_view_window_to_buffer_coords (text_view,
1686 numbers = g_array_new (FALSE, FALSE, sizeof (gint));
1687 pixels = g_array_new (FALSE, FALSE, sizeof (gint));
1689 get_lines (text_view,
1696 /* Draw fully internationalized numbers! */
1698 layout = gtk_widget_create_pango_layout (widget, "");
1706 gtk_text_view_buffer_to_window_coords (text_view,
1709 g_array_index (pixels, gint, i),
1713 str = g_strdup_printf ("%d", g_array_index (numbers, gint, i));
1715 pango_layout_set_text (layout, str, -1);
1718 gdk_draw_layout (target,
1719 widget->style->fg_gc [widget->state],
1720 /* 2 is just a random padding */
1729 g_array_free (pixels, TRUE);
1730 g_array_free (numbers, TRUE);
1732 g_object_unref (G_OBJECT (layout));
1738 create_view (Buffer *buffer)
1745 view = g_new0 (View, 1);
1746 views = g_slist_prepend (views, view);
1748 view->buffer = buffer;
1749 buffer_ref (buffer);
1751 view->window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
1752 gtk_object_set_data (GTK_OBJECT (view->window), "view", view);
1754 gtk_signal_connect (GTK_OBJECT (view->window), "delete_event",
1755 GTK_SIGNAL_FUNC (delete_event_cb), NULL);
1757 view->accel_group = gtk_accel_group_new ();
1758 view->item_factory = gtk_item_factory_new (GTK_TYPE_MENU_BAR, "<main>", view->accel_group);
1759 gtk_object_set_data (GTK_OBJECT (view->item_factory), "view", view);
1761 gtk_item_factory_create_items (view->item_factory, G_N_ELEMENTS (menu_items), menu_items, view);
1763 gtk_window_add_accel_group (GTK_WINDOW (view->window), view->accel_group);
1765 vbox = gtk_vbox_new (FALSE, 0);
1766 gtk_container_add (GTK_CONTAINER (view->window), vbox);
1768 gtk_box_pack_start (GTK_BOX (vbox),
1769 gtk_item_factory_get_widget (view->item_factory, "<main>"),
1772 sw = gtk_scrolled_window_new (NULL, NULL);
1773 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
1774 GTK_POLICY_AUTOMATIC,
1775 GTK_POLICY_AUTOMATIC);
1777 view->text_view = gtk_text_view_new_with_buffer (buffer->buffer);
1778 gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (view->text_view),
1782 /* Draw tab stops in the top and bottom windows. */
1784 gtk_text_view_set_border_window_size (GTK_TEXT_VIEW (view->text_view),
1785 GTK_TEXT_WINDOW_TOP,
1788 gtk_text_view_set_border_window_size (GTK_TEXT_VIEW (view->text_view),
1789 GTK_TEXT_WINDOW_BOTTOM,
1792 gtk_signal_connect (GTK_OBJECT (view->text_view),
1794 GTK_SIGNAL_FUNC (tab_stops_expose),
1797 gtk_signal_connect (GTK_OBJECT (view->buffer->buffer),
1799 GTK_SIGNAL_FUNC (cursor_set_callback),
1802 /* Draw line numbers in the side windows; we should really be
1803 * more scientific about what width we set them to.
1805 gtk_text_view_set_border_window_size (GTK_TEXT_VIEW (view->text_view),
1806 GTK_TEXT_WINDOW_RIGHT,
1809 gtk_text_view_set_border_window_size (GTK_TEXT_VIEW (view->text_view),
1810 GTK_TEXT_WINDOW_LEFT,
1813 gtk_signal_connect (GTK_OBJECT (view->text_view),
1815 GTK_SIGNAL_FUNC (line_numbers_expose),
1818 gtk_signal_connect (GTK_OBJECT (view->buffer->buffer),
1820 GTK_SIGNAL_FUNC (text_changed_callback),
1823 gtk_box_pack_start (GTK_BOX (vbox), sw, TRUE, TRUE, 0);
1824 gtk_container_add (GTK_CONTAINER (sw), view->text_view);
1826 gtk_window_set_default_size (GTK_WINDOW (view->window), 500, 500);
1828 gtk_widget_grab_focus (view->text_view);
1830 view_set_title (view);
1831 view_init_menus (view);
1833 gtk_widget_show_all (view->window);
1838 main (int argc, char** argv)
1844 gtk_init (&argc, &argv);
1846 buffer = create_buffer ();
1847 view = create_view (buffer);
1848 buffer_unref (buffer);
1850 push_active_window (GTK_WINDOW (view->window));
1851 for (i=1; i < argc; i++)
1855 /* Quick and dirty canonicalization - better should be in GLib
1858 if (!g_path_is_absolute (argv[i]))
1860 char *cwd = g_get_current_dir ();
1861 filename = g_strconcat (cwd, "/", argv[i], NULL);
1867 open_ok_func (filename, view);
1869 if (filename != argv[i])
1872 pop_active_window ();