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;
426 tag = gtk_text_buffer_create_tag (buffer, "fg_blue");
428 /* gtk_timeout_add (1000, blink_timeout, tag); */
432 color.red = color.green = 0;
437 gtk_object_set (GTK_OBJECT (tag),
438 "foreground_gdk", &color,
439 "background_gdk", &color2,
443 tag = gtk_text_buffer_create_tag (buffer, "fg_red");
447 color.blue = color.green = 0;
449 gtk_object_set (GTK_OBJECT (tag),
451 "foreground_gdk", &color,
454 tag = gtk_text_buffer_create_tag (buffer, "bg_green");
458 color.blue = color.red = 0;
459 color.green = 0xffff;
460 gtk_object_set (GTK_OBJECT (tag),
461 "background_gdk", &color,
465 tag = gtk_text_buffer_create_tag (buffer, "strikethrough");
469 gtk_object_set (GTK_OBJECT (tag),
470 "strikethrough", TRUE,
474 tag = gtk_text_buffer_create_tag (buffer, "underline");
478 gtk_object_set (GTK_OBJECT (tag),
479 "underline", PANGO_UNDERLINE_SINGLE,
484 gtk_object_set (GTK_OBJECT (tag),
485 "underline", PANGO_UNDERLINE_SINGLE,
488 tag = gtk_text_buffer_create_tag (buffer, "centered");
490 gtk_object_set (GTK_OBJECT (tag),
491 "justify", GTK_JUSTIFY_CENTER,
494 tag = gtk_text_buffer_create_tag (buffer, "rtl_quote");
496 gtk_object_set (GTK_OBJECT (tag),
497 "wrap_mode", GTK_WRAPMODE_WORD,
498 "direction", GTK_TEXT_DIR_RTL,
499 "left_wrapped_line_margin", 20,
504 pixmap = gdk_pixmap_colormap_create_from_xpm_d (NULL,
505 gtk_widget_get_default_colormap (),
507 NULL, book_closed_xpm);
509 g_assert (pixmap != NULL);
514 GtkTextMark * temp_mark;
516 gtk_text_buffer_get_iter_at_offset (buffer, &iter, 0);
518 gtk_text_buffer_insert_pixmap (buffer, &iter, pixmap, mask);
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 gdk_pixmap_unref (pixmap);
587 gdk_bitmap_unref (mask);
589 printf ("%d lines %d chars\n",
590 gtk_text_buffer_get_line_count (buffer),
591 gtk_text_buffer_get_char_count (buffer));
593 gtk_text_buffer_set_modified (buffer, FALSE);
597 fill_file_buffer (GtkTextBuffer *buffer, const char *filename)
602 GtkTextIter iter, end;
604 f = fopen (filename, "r");
608 gchar *err = g_strdup_printf ("Cannot open file '%s': %s",
609 filename, g_strerror (errno));
610 msgbox_run (NULL, err, "OK", NULL, NULL, 0);
615 gtk_text_buffer_get_iter_at_offset (buffer, &iter, 0);
619 char *leftover, *next;
620 int to_read = 2047 - remaining;
622 count = fread (buf + remaining, 1, to_read, f);
623 buf[count + remaining] = '\0';
625 leftover = next = buf;
632 next = g_utf8_next_char (next);
635 gtk_text_buffer_insert (buffer, &iter, buf, leftover - buf);
637 remaining = buf + remaining + count - leftover;
638 g_memmove (buf, leftover, remaining);
640 if (remaining > 6 || count < to_read)
646 gchar *err = g_strdup_printf ("Invalid UTF-8 data encountered reading file '%s'", filename);
647 msgbox_run (NULL, err, "OK", NULL, NULL, 0);
651 /* We had a newline in the buffer to begin with. (The buffer always contains
652 * a newline, so we delete to the end of the buffer to clean up.
654 gtk_text_buffer_get_last_iter (buffer, &end);
655 gtk_text_buffer_delete (buffer, &iter, &end);
657 gtk_text_buffer_set_modified (buffer, FALSE);
663 delete_event_cb (GtkWidget *window, GdkEventAny *event, gpointer data)
665 View *view = view_from_widget (window);
667 push_active_window (GTK_WINDOW (window));
668 check_close_view (view);
669 pop_active_window ();
679 get_empty_view (View *view)
681 if (!view->buffer->filename &&
682 !gtk_text_buffer_modified (view->buffer->buffer))
685 return create_view (create_buffer ());
689 view_from_widget (GtkWidget *widget)
693 if (GTK_IS_MENU_ITEM (widget))
695 GtkItemFactory *item_factory = gtk_item_factory_from_widget (widget);
696 return gtk_object_get_data (GTK_OBJECT (item_factory), "view");
700 GtkWidget *app = gtk_widget_get_toplevel (widget);
701 return gtk_object_get_data (GTK_OBJECT (app), "view");
706 do_new (gpointer callback_data,
707 guint callback_action,
710 create_view (create_buffer ());
714 do_new_view (gpointer callback_data,
715 guint callback_action,
718 View *view = view_from_widget (widget);
720 create_view (view->buffer);
724 open_ok_func (const char *filename, gpointer data)
727 View *new_view = get_empty_view (view);
729 if (!fill_file_buffer (new_view->buffer->buffer, filename))
731 if (new_view != view)
732 close_view (new_view);
737 g_free (new_view->buffer->filename);
738 new_view->buffer->filename = g_strdup (filename);
739 buffer_filename_set (new_view->buffer);
746 do_open (gpointer callback_data,
747 guint callback_action,
750 View *view = view_from_widget (widget);
752 push_active_window (GTK_WINDOW (view->window));
753 filesel_run (NULL, "Open File", NULL, open_ok_func, view);
754 pop_active_window ();
758 do_save_as (gpointer callback_data,
759 guint callback_action,
762 View *view = view_from_widget (widget);
764 push_active_window (GTK_WINDOW (view->window));
765 save_as_buffer (view->buffer);
766 pop_active_window ();
770 do_save (gpointer callback_data,
771 guint callback_action,
774 View *view = view_from_widget (widget);
776 push_active_window (GTK_WINDOW (view->window));
777 if (!view->buffer->filename)
778 do_save_as (callback_data, callback_action, widget);
780 save_buffer (view->buffer);
781 pop_active_window ();
785 do_close (gpointer callback_data,
786 guint callback_action,
789 View *view = view_from_widget (widget);
791 push_active_window (GTK_WINDOW (view->window));
792 check_close_view (view);
793 pop_active_window ();
797 do_exit (gpointer callback_data,
798 guint callback_action,
801 View *view = view_from_widget (widget);
803 GSList *tmp_list = buffers;
805 push_active_window (GTK_WINDOW (view->window));
808 if (!check_buffer_saved (tmp_list->data))
811 tmp_list = tmp_list->next;
815 pop_active_window ();
819 do_example (gpointer callback_data,
820 guint callback_action,
823 View *view = view_from_widget (widget);
826 new_view = get_empty_view (view);
828 fill_example_buffer (new_view->buffer->buffer);
832 do_wrap_changed (gpointer callback_data,
833 guint callback_action,
836 View *view = view_from_widget (widget);
838 gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (view->text_view), callback_action);
842 do_direction_changed (gpointer callback_data,
843 guint callback_action,
846 View *view = view_from_widget (widget);
848 gtk_widget_set_direction (view->text_view, callback_action);
849 gtk_widget_queue_resize (view->text_view);
853 do_editable_changed (gpointer callback_data,
854 guint callback_action,
857 View *view = view_from_widget (widget);
859 gtk_text_view_set_editable (GTK_TEXT_VIEW (view->text_view), callback_action);
863 do_cursor_visible_changed (gpointer callback_data,
864 guint callback_action,
867 View *view = view_from_widget (widget);
869 gtk_text_view_set_cursor_visible (GTK_TEXT_VIEW (view->text_view), callback_action);
873 do_apply_editable (gpointer callback_data,
874 guint callback_action,
877 View *view = view_from_widget (widget);
881 if (gtk_text_buffer_get_selection_bounds (view->buffer->buffer,
886 gtk_text_buffer_remove_tag (view->buffer->buffer,
887 view->buffer->not_editable_tag,
892 gtk_text_buffer_apply_tag (view->buffer->buffer,
893 view->buffer->not_editable_tag,
901 do_apply_tabs (gpointer callback_data,
902 guint callback_action,
905 View *view = view_from_widget (widget);
909 if (gtk_text_buffer_get_selection_bounds (view->buffer->buffer,
914 gtk_text_buffer_remove_tag (view->buffer->buffer,
915 view->buffer->custom_tabs_tag,
920 gtk_text_buffer_apply_tag (view->buffer->buffer,
921 view->buffer->custom_tabs_tag,
928 dialog_response_callback (GtkWidget *dialog, gint response_id, gpointer data)
930 GtkTextBuffer *buffer;
932 GtkTextIter start, end;
933 gchar *search_string;
935 buffer = gtk_object_get_data (GTK_OBJECT (dialog), "buffer");
937 gtk_text_buffer_get_bounds (buffer, &start, &end);
939 /* Remove trailing newline */
940 gtk_text_iter_prev_char (&end);
942 search_string = gtk_text_iter_get_text (&start, &end);
944 printf ("Searching for `%s'\n", search_string);
946 buffer_search_forward (view->buffer, search_string, view);
948 g_free (search_string);
950 gtk_widget_destroy (dialog);
954 do_search (gpointer callback_data,
955 guint callback_action,
958 View *view = view_from_widget (widget);
960 GtkWidget *search_text;
961 GtkTextBuffer *buffer;
965 dialog = gtk_dialog_new_with_buttons ("Search",
966 GTK_WINDOW (view->window),
967 GTK_DIALOG_DESTROY_WITH_PARENT,
968 GTK_STOCK_BUTTON_CLOSE,
969 GTK_RESPONSE_NONE, NULL);
971 buffer = gtk_text_buffer_new (NULL);
973 /* FIXME memory leak once buffer is a GObject */
974 search_text = gtk_text_view_new_with_buffer (buffer);
976 gtk_box_pack_end (GTK_BOX (GTK_DIALOG (dialog)->vbox),
980 gtk_object_set_data (GTK_OBJECT (dialog), "buffer", buffer);
982 gtk_signal_connect (GTK_OBJECT (dialog),
984 GTK_SIGNAL_FUNC (dialog_response_callback),
987 gtk_widget_show (search_text);
989 gtk_widget_grab_focus (search_text);
991 gtk_widget_show_all (dialog);
996 view_init_menus (View *view)
998 GtkTextDirection direction = gtk_widget_get_direction (view->text_view);
999 GtkWrapMode wrap_mode = gtk_text_view_get_wrap_mode (GTK_TEXT_VIEW (view->text_view));
1000 GtkWidget *menu_item = NULL;
1004 case GTK_TEXT_DIR_LTR:
1005 menu_item = gtk_item_factory_get_widget (view->item_factory, "/Settings/Left-to-Right");
1007 case GTK_TEXT_DIR_RTL:
1008 menu_item = gtk_item_factory_get_widget (view->item_factory, "/Settings/Right-to-Left");
1015 gtk_menu_item_activate (GTK_MENU_ITEM (menu_item));
1019 case GTK_WRAPMODE_NONE:
1020 menu_item = gtk_item_factory_get_widget (view->item_factory, "/Settings/Wrap Off");
1022 case GTK_WRAPMODE_WORD:
1023 menu_item = gtk_item_factory_get_widget (view->item_factory, "/Settings/Wrap Words");
1030 gtk_menu_item_activate (GTK_MENU_ITEM (menu_item));
1033 static GtkItemFactoryEntry menu_items[] =
1035 { "/_File", NULL, 0, 0, "<Branch>" },
1036 { "/File/_New", "<control>N", do_new, 0, NULL },
1037 { "/File/New _View", NULL, do_new_view, 0, NULL },
1038 { "/File/_Open", "<control>O", do_open, 0, NULL },
1039 { "/File/_Save", "<control>S", do_save, 0, NULL },
1040 { "/File/Save _As...", NULL, do_save_as, 0, NULL },
1041 { "/File/sep1", NULL, 0, 0, "<Separator>" },
1042 { "/File/_Close", "<control>W" , do_close, 0, NULL },
1043 { "/File/E_xit", "<control>Q" , do_exit, 0, NULL },
1045 { "/_Edit", NULL, 0, 0, "<Branch>" },
1046 { "/Edit/Find...", NULL, do_search, 0, NULL },
1048 { "/_Settings", NULL, 0, 0, "<Branch>" },
1049 { "/Settings/Wrap _Off", NULL, do_wrap_changed, GTK_WRAPMODE_NONE, "<RadioItem>" },
1050 { "/Settings/Wrap _Words", NULL, do_wrap_changed, GTK_WRAPMODE_WORD, "/Settings/Wrap Off" },
1051 { "/Settings/sep1", NULL, 0, 0, "<Separator>" },
1052 { "/Settings/Editable", NULL, do_editable_changed, TRUE, "<RadioItem>" },
1053 { "/Settings/Not editable", NULL, do_editable_changed, FALSE, "/Settings/Editable" },
1054 { "/Settings/sep1", NULL, 0, 0, "<Separator>" },
1056 { "/Settings/Cursor visible", NULL, do_cursor_visible_changed, TRUE, "<RadioItem>" },
1057 { "/Settings/Cursor not visible", NULL, do_cursor_visible_changed, FALSE, "/Settings/Cursor visible" },
1058 { "/Settings/sep1", NULL, 0, 0, "<Separator>" },
1060 { "/Settings/Left-to-Right", NULL, do_direction_changed, GTK_TEXT_DIR_LTR, "<RadioItem>" },
1061 { "/Settings/Right-to-Left", NULL, do_direction_changed, GTK_TEXT_DIR_RTL, "/Settings/Left-to-Right" },
1062 { "/_Attributes", NULL, 0, 0, "<Branch>" },
1063 { "/Attributes/Editable", NULL, do_apply_editable, TRUE, NULL },
1064 { "/Attributes/Not editable", NULL, do_apply_editable, FALSE, NULL },
1065 { "/Attributes/Custom tabs", NULL, do_apply_tabs, FALSE, NULL },
1066 { "/Attributes/Default tabs", NULL, do_apply_tabs, TRUE, NULL },
1067 { "/_Test", NULL, 0, 0, "<Branch>" },
1068 { "/Test/_Example", NULL, do_example, 0, NULL },
1072 save_buffer (Buffer *buffer)
1074 GtkTextIter start, end;
1076 gboolean result = FALSE;
1077 gboolean have_backup = FALSE;
1078 gchar *bak_filename;
1081 g_return_val_if_fail (buffer->filename != NULL, FALSE);
1083 bak_filename = g_strconcat (buffer->filename, "~", NULL);
1085 if (rename (buffer->filename, bak_filename) != 0)
1087 if (errno != ENOENT)
1089 gchar *err = g_strdup_printf ("Cannot back up '%s' to '%s': %s",
1090 buffer->filename, bak_filename, g_strerror (errno));
1091 msgbox_run (NULL, err, "OK", NULL, NULL, 0);
1100 file = fopen (buffer->filename, "w");
1103 gchar *err = g_strdup_printf ("Cannot back up '%s' to '%s': %s",
1104 buffer->filename, bak_filename, g_strerror (errno));
1105 msgbox_run (NULL, err, "OK", NULL, NULL, 0);
1109 gtk_text_buffer_get_iter_at_offset (buffer->buffer, &start, 0);
1110 gtk_text_buffer_get_last_iter (buffer->buffer, &end);
1112 chars = gtk_text_buffer_get_slice (buffer->buffer, &start, &end, FALSE);
1114 if (fputs (chars, file) == EOF ||
1115 fclose (file) == EOF)
1117 gchar *err = g_strdup_printf ("Error writing to '%s': %s",
1118 buffer->filename, g_strerror (errno));
1119 msgbox_run (NULL, err, "OK", NULL, NULL, 0);
1127 gtk_text_buffer_set_modified (buffer->buffer, FALSE);
1133 if (!result && have_backup)
1135 if (rename (bak_filename, buffer->filename) != 0)
1137 gchar *err = g_strdup_printf ("Error restoring backup file '%s' to '%s': %s\nBackup left as '%s'",
1138 buffer->filename, bak_filename, g_strerror (errno), bak_filename);
1139 msgbox_run (NULL, err, "OK", NULL, NULL, 0);
1144 g_free (bak_filename);
1150 save_as_ok_func (const char *filename, gpointer data)
1152 Buffer *buffer = data;
1153 char *old_filename = buffer->filename;
1155 if (!buffer->filename || strcmp (filename, buffer->filename) != 0)
1157 struct stat statbuf;
1159 if (stat (filename, &statbuf) == 0)
1161 gchar *err = g_strdup_printf ("Ovewrite existing file '%s'?", filename);
1162 gint result = msgbox_run (NULL, err, "Yes", "No", NULL, 1);
1170 buffer->filename = g_strdup (filename);
1172 if (save_buffer (buffer))
1174 g_free (old_filename);
1175 buffer_filename_set (buffer);
1180 g_free (buffer->filename);
1181 buffer->filename = old_filename;
1187 save_as_buffer (Buffer *buffer)
1189 return filesel_run (NULL, "Save File", NULL, save_as_ok_func, buffer);
1193 check_buffer_saved (Buffer *buffer)
1195 if (gtk_text_buffer_modified (buffer->buffer))
1197 char *pretty_name = buffer_pretty_name (buffer);
1198 char *msg = g_strdup_printf ("Save changes to '%s'?", pretty_name);
1201 g_free (pretty_name);
1203 result = msgbox_run (NULL, msg, "Yes", "No", "Cancel", 0);
1207 return save_as_buffer (buffer);
1208 else if (result == 1)
1218 create_buffer (void)
1221 PangoTabArray *tabs;
1223 buffer = g_new (Buffer, 1);
1225 buffer->buffer = gtk_text_buffer_new (NULL);
1226 gtk_object_ref (GTK_OBJECT (buffer->buffer));
1227 gtk_object_sink (GTK_OBJECT (buffer->buffer));
1229 buffer->refcount = 1;
1230 buffer->filename = NULL;
1231 buffer->untitled_serial = -1;
1233 buffer->not_editable_tag = gtk_text_buffer_create_tag (buffer->buffer, NULL);
1234 gtk_object_set (GTK_OBJECT (buffer->not_editable_tag),
1236 "foreground", "purple", NULL);
1238 buffer->found_text_tag = gtk_text_buffer_create_tag (buffer->buffer, NULL);
1239 gtk_object_set (GTK_OBJECT (buffer->found_text_tag),
1240 "foreground", "red", NULL);
1242 tabs = pango_tab_array_new_with_positions (4,
1247 PANGO_TAB_LEFT, 120);
1249 buffer->custom_tabs_tag = gtk_text_buffer_create_tag (buffer->buffer, NULL);
1250 gtk_object_set (GTK_OBJECT (buffer->custom_tabs_tag),
1252 "foreground", "green", NULL);
1254 pango_tab_array_free (tabs);
1256 buffers = g_slist_prepend (buffers, buffer);
1262 buffer_pretty_name (Buffer *buffer)
1264 if (buffer->filename)
1267 char *result = g_path_get_basename (buffer->filename);
1268 p = strchr (result, '/');
1276 if (buffer->untitled_serial == -1)
1277 buffer->untitled_serial = untitled_serial++;
1279 if (buffer->untitled_serial == 1)
1280 return g_strdup ("Untitled");
1282 return g_strdup_printf ("Untitled #%d", buffer->untitled_serial);
1287 buffer_filename_set (Buffer *buffer)
1289 GSList *tmp_list = views;
1293 View *view = tmp_list->data;
1295 if (view->buffer == buffer)
1296 view_set_title (view);
1298 tmp_list = tmp_list->next;
1303 buffer_search_forward (Buffer *buffer, const char *str,
1307 GtkTextIter start, end;
1312 /* remove tag from whole buffer */
1313 gtk_text_buffer_get_bounds (buffer->buffer, &start, &end);
1314 gtk_text_buffer_remove_tag (buffer->buffer, buffer->found_text_tag,
1317 gtk_text_buffer_get_iter_at_mark (buffer->buffer, &iter,
1318 gtk_text_buffer_get_mark (buffer->buffer,
1322 char_len = g_utf8_strlen (str, -1);
1326 while (gtk_text_iter_forward_search (&iter, str, TRUE, FALSE))
1328 GtkTextIter end = iter;
1330 gtk_text_iter_forward_chars (&end, char_len);
1332 gtk_text_buffer_apply_tag (buffer->buffer, buffer->found_text_tag,
1342 dialog = gtk_message_dialog_new (GTK_WINDOW (view->window),
1345 GTK_DIALOG_DESTROY_WITH_PARENT,
1346 "%d strings found and marked in red",
1349 gtk_signal_connect_object (GTK_OBJECT (dialog),
1351 GTK_SIGNAL_FUNC (gtk_widget_destroy),
1352 GTK_OBJECT (dialog));
1354 gtk_widget_show (dialog);
1359 buffer_ref (Buffer *buffer)
1365 buffer_unref (Buffer *buffer)
1368 if (buffer->refcount == 0)
1370 buffers = g_slist_remove (buffers, buffer);
1371 gtk_object_unref (GTK_OBJECT (buffer->buffer));
1372 g_free (buffer->filename);
1378 close_view (View *view)
1380 views = g_slist_remove (views, view);
1381 buffer_unref (view->buffer);
1382 gtk_widget_destroy (view->window);
1383 g_object_unref (G_OBJECT (view->item_factory));
1392 check_close_view (View *view)
1394 if (view->buffer->refcount > 1 ||
1395 check_buffer_saved (view->buffer))
1400 view_set_title (View *view)
1402 char *pretty_name = buffer_pretty_name (view->buffer);
1403 char *title = g_strconcat ("testtext - ", pretty_name, NULL);
1405 gtk_window_set_title (GTK_WINDOW (view->window), title);
1407 g_free (pretty_name);
1412 get_lines (GtkTextView *text_view,
1415 gint **buffer_coords,
1424 *buffer_coords = NULL;
1429 /* Get iter at first y */
1430 gtk_text_view_get_iter_at_location (text_view, &iter, 0, first_y);
1432 /* Move back to start of its paragraph */
1433 gtk_text_iter_set_line_offset (&iter, 0);
1435 /* For each iter, get its location and add it to the arrays.
1436 * Stop when we pass last_y
1441 while (!gtk_text_iter_is_last (&iter))
1445 gtk_text_view_get_iter_location (text_view, &iter, &loc);
1447 if (loc.y >= last_y)
1452 size = 2 * size + 2; /* + 2 handles size == 0 case */
1455 *buffer_coords = g_realloc (*buffer_coords,
1456 size * sizeof (gint));
1459 *numbers = g_realloc (*numbers,
1460 size * sizeof (gint));
1464 (*buffer_coords)[count] = loc.y;
1467 (*numbers)[count] = gtk_text_iter_get_line (&iter);
1471 gtk_text_iter_forward_line (&iter);
1478 line_numbers_expose (GtkWidget *widget,
1479 GdkEventExpose *event,
1483 gint *numbers = NULL;
1484 gint *pixels = NULL;
1488 GdkWindow *left_win;
1489 PangoLayout *layout;
1490 GtkTextView *text_view;
1492 text_view = GTK_TEXT_VIEW (widget);
1494 /* See if this expose is on the line numbers window */
1495 left_win = gtk_text_view_get_window (text_view,
1496 GTK_TEXT_WINDOW_LEFT);
1498 if (event->window != left_win)
1501 first_y = event->area.y;
1502 last_y = first_y + event->area.height;
1504 gtk_text_view_window_to_buffer_coords (text_view,
1505 GTK_TEXT_WINDOW_LEFT,
1511 get_lines (text_view,
1519 /* Draw fully internationalized numbers! */
1521 layout = gtk_widget_create_pango_layout (widget, "");
1529 gtk_text_view_buffer_to_window_coords (text_view,
1530 GTK_TEXT_WINDOW_LEFT,
1536 str = g_strdup_printf ("%d", numbers[i]);
1538 pango_layout_set_text (layout, str, -1);
1540 gdk_draw_layout (left_win,
1541 widget->style->fg_gc [widget->state],
1542 /* 2 is just a random padding */
1554 g_object_unref (G_OBJECT (layout));
1560 create_view (Buffer *buffer)
1567 view = g_new0 (View, 1);
1568 views = g_slist_prepend (views, view);
1570 view->buffer = buffer;
1571 buffer_ref (buffer);
1573 view->window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
1574 gtk_object_set_data (GTK_OBJECT (view->window), "view", view);
1576 gtk_signal_connect (GTK_OBJECT (view->window), "delete_event",
1577 GTK_SIGNAL_FUNC (delete_event_cb), NULL);
1579 view->accel_group = gtk_accel_group_new ();
1580 view->item_factory = gtk_item_factory_new (GTK_TYPE_MENU_BAR, "<main>", view->accel_group);
1581 gtk_object_set_data (GTK_OBJECT (view->item_factory), "view", view);
1583 gtk_item_factory_create_items (view->item_factory, G_N_ELEMENTS (menu_items), menu_items, view);
1585 gtk_window_add_accel_group (GTK_WINDOW (view->window), view->accel_group);
1587 vbox = gtk_vbox_new (FALSE, 0);
1588 gtk_container_add (GTK_CONTAINER (view->window), vbox);
1590 gtk_box_pack_start (GTK_BOX (vbox),
1591 gtk_item_factory_get_widget (view->item_factory, "<main>"),
1594 sw = gtk_scrolled_window_new (NULL, NULL);
1595 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
1596 GTK_POLICY_AUTOMATIC,
1597 GTK_POLICY_AUTOMATIC);
1599 view->text_view = gtk_text_view_new_with_buffer (buffer->buffer);
1600 gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (view->text_view),
1604 /* Set sizes on these windows, just for debugging */
1606 gtk_text_view_set_right_window_width (GTK_TEXT_VIEW (view->text_view),
1609 gtk_text_view_set_top_window_height (GTK_TEXT_VIEW (view->text_view),
1612 gtk_text_view_set_bottom_window_height (GTK_TEXT_VIEW (view->text_view),
1615 /* Draw line numbers in the left window; we should really be
1616 * more scientific about what width we set it to.
1618 gtk_text_view_set_left_window_width (GTK_TEXT_VIEW (view->text_view),
1621 gtk_signal_connect (GTK_OBJECT (view->text_view),
1623 GTK_SIGNAL_FUNC (line_numbers_expose),
1626 gtk_box_pack_start (GTK_BOX (vbox), sw, TRUE, TRUE, 0);
1627 gtk_container_add (GTK_CONTAINER (sw), view->text_view);
1629 gtk_window_set_default_size (GTK_WINDOW (view->window), 500, 500);
1631 gtk_widget_grab_focus (view->text_view);
1633 view_set_title (view);
1634 view_init_menus (view);
1636 gtk_widget_show_all (view->window);
1641 main (int argc, char** argv)
1647 gtk_init (&argc, &argv);
1648 gdk_rgb_init (); /* FIXME remove this */
1650 buffer = create_buffer ();
1651 view = create_view (buffer);
1652 buffer_unref (buffer);
1654 push_active_window (GTK_WINDOW (view->window));
1655 for (i=1; i < argc; i++)
1659 /* Quick and dirty canonicalization - better should be in GLib
1662 if (!g_path_is_absolute (argv[i]))
1664 char *cwd = g_get_current_dir ();
1665 filename = g_strconcat (cwd, "/", argv[i], NULL);
1671 open_ok_func (filename, view);
1673 if (filename != argv[i])
1676 pop_active_window ();