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;
31 GtkAccelGroup *accel_group;
32 GtkItemFactory *item_factory;
36 static void push_active_window (GtkWindow *window);
37 static void pop_active_window (void);
38 static GtkWindow *get_active_window (void);
40 static Buffer * create_buffer (void);
41 static gboolean check_buffer_saved (Buffer *buffer);
42 static gboolean save_buffer (Buffer *buffer);
43 static gboolean save_as_buffer (Buffer *buffer);
44 static char * buffer_pretty_name (Buffer *buffer);
45 static void buffer_filename_set (Buffer *buffer);
46 static void buffer_search_forward (Buffer *buffer,
50 static View *view_from_widget (GtkWidget *widget);
52 static View *create_view (Buffer *buffer);
53 static void check_close_view (View *view);
54 static void close_view (View *view);
55 static void view_set_title (View *view);
56 static void view_init_menus (View *view);
58 GSList *buffers = NULL;
62 push_active_window (GtkWindow *window)
64 gtk_object_ref (GTK_OBJECT (window));
65 active_window_stack = g_slist_prepend (active_window_stack, window);
69 pop_active_window (void)
71 gtk_object_unref (active_window_stack->data);
72 active_window_stack = g_slist_delete_link (active_window_stack, active_window_stack);
76 get_active_window (void)
78 if (active_window_stack)
79 return active_window_stack->data;
85 * Filesel utility function
88 typedef gboolean (*FileselOKFunc) (const char *filename, gpointer data);
91 filesel_ok_cb (GtkWidget *button, GtkWidget *filesel)
93 FileselOKFunc ok_func = gtk_object_get_data (GTK_OBJECT (filesel), "ok-func");
94 gpointer data = gtk_object_get_data (GTK_OBJECT (filesel), "ok-data");
95 gint *result = gtk_object_get_data (GTK_OBJECT (filesel), "ok-result");
97 gtk_widget_hide (filesel);
99 if ((*ok_func) (gtk_file_selection_get_filename (GTK_FILE_SELECTION (filesel)), data))
101 gtk_widget_destroy (filesel);
105 gtk_widget_show (filesel);
109 filesel_run (GtkWindow *parent,
111 const char *start_file,
115 GtkWidget *filesel = gtk_file_selection_new (title);
116 gboolean result = FALSE;
119 parent = get_active_window ();
122 gtk_window_set_transient_for (GTK_WINDOW (filesel), parent);
125 gtk_file_selection_set_filename (GTK_FILE_SELECTION (filesel), start_file);
128 gtk_object_set_data (GTK_OBJECT (filesel), "ok-func", func);
129 gtk_object_set_data (GTK_OBJECT (filesel), "ok-data", data);
130 gtk_object_set_data (GTK_OBJECT (filesel), "ok-result", &result);
132 gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION (filesel)->ok_button),
134 GTK_SIGNAL_FUNC (filesel_ok_cb), filesel);
135 gtk_signal_connect_object (GTK_OBJECT (GTK_FILE_SELECTION (filesel)->cancel_button),
137 GTK_SIGNAL_FUNC (gtk_widget_destroy), GTK_OBJECT (filesel));
139 gtk_signal_connect (GTK_OBJECT (filesel), "destroy",
140 GTK_SIGNAL_FUNC (gtk_main_quit), NULL);
141 gtk_window_set_modal (GTK_WINDOW (filesel), TRUE);
143 gtk_widget_show (filesel);
150 * MsgBox utility functions
154 msgbox_yes_cb (GtkWidget *widget, gboolean *result)
157 gtk_object_destroy (GTK_OBJECT (gtk_widget_get_toplevel (widget)));
161 msgbox_no_cb (GtkWidget *widget, gboolean *result)
164 gtk_object_destroy (GTK_OBJECT (gtk_widget_get_toplevel (widget)));
168 msgbox_key_press_cb (GtkWidget *widget, GdkEventKey *event, gpointer data)
170 if (event->keyval == GDK_Escape)
172 gtk_signal_emit_stop_by_name (GTK_OBJECT (widget), "key_press_event");
173 gtk_object_destroy (GTK_OBJECT (widget));
181 msgbox_run (GtkWindow *parent,
183 const char *yes_button,
184 const char *no_button,
185 const char *cancel_button,
188 gboolean result = -1;
193 GtkWidget *button_box;
194 GtkWidget *separator;
196 g_return_val_if_fail (message != NULL, FALSE);
197 g_return_val_if_fail (default_index >= 0 && default_index <= 1, FALSE);
200 parent = get_active_window ();
204 dialog = gtk_window_new (GTK_WINDOW_TOPLEVEL);
205 gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
207 gtk_window_set_transient_for (GTK_WINDOW (dialog), parent);
208 gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
210 /* Quit our recursive main loop when the dialog is destroyed.
212 gtk_signal_connect (GTK_OBJECT (dialog), "destroy",
213 GTK_SIGNAL_FUNC (gtk_main_quit), NULL);
215 /* Catch Escape key presses and have them destroy the dialog
217 gtk_signal_connect (GTK_OBJECT (dialog), "key_press_event",
218 GTK_SIGNAL_FUNC (msgbox_key_press_cb), NULL);
220 /* Fill in the contents of the widget
222 vbox = gtk_vbox_new (FALSE, 0);
223 gtk_container_add (GTK_CONTAINER (dialog), vbox);
225 label = gtk_label_new (message);
226 gtk_misc_set_padding (GTK_MISC (label), 12, 12);
227 gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
228 gtk_box_pack_start (GTK_BOX (vbox), label, TRUE, TRUE, 0);
230 separator = gtk_hseparator_new ();
231 gtk_box_pack_start (GTK_BOX (vbox), separator, FALSE, FALSE, 0);
233 button_box = gtk_hbutton_box_new ();
234 gtk_box_pack_start (GTK_BOX (vbox), button_box, FALSE, FALSE, 0);
235 gtk_container_set_border_width (GTK_CONTAINER (button_box), 8);
238 /* When Yes is clicked, call the msgbox_yes_cb
239 * This sets the result variable and destroys the dialog
243 button = gtk_button_new_with_label (yes_button);
244 GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
245 gtk_container_add (GTK_CONTAINER (button_box), button);
247 if (default_index == 0)
248 gtk_widget_grab_default (button);
250 gtk_signal_connect (GTK_OBJECT (button), "clicked",
251 GTK_SIGNAL_FUNC (msgbox_yes_cb), &result);
254 /* When No is clicked, call the msgbox_no_cb
255 * This sets the result variable and destroys the dialog
259 button = gtk_button_new_with_label (no_button);
260 GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
261 gtk_container_add (GTK_CONTAINER (button_box), button);
263 if (default_index == 0)
264 gtk_widget_grab_default (button);
266 gtk_signal_connect (GTK_OBJECT (button), "clicked",
267 GTK_SIGNAL_FUNC (msgbox_no_cb), &result);
270 /* When Cancel is clicked, destroy the dialog
274 button = gtk_button_new_with_label (cancel_button);
275 GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
276 gtk_container_add (GTK_CONTAINER (button_box), button);
278 if (default_index == 1)
279 gtk_widget_grab_default (button);
281 gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
282 GTK_SIGNAL_FUNC (gtk_object_destroy), GTK_OBJECT (dialog));
285 gtk_widget_show_all (dialog);
287 /* Run a recursive main loop until a button is clicked
288 * or the user destroys the dialog through the window mananger */
295 * Example buffer filling code
298 blink_timeout (gpointer data)
301 static gboolean flip = FALSE;
303 tag = GTK_TEXT_TAG (data);
305 gtk_object_set (GTK_OBJECT (tag),
306 "foreground", flip ? "blue" : "purple",
315 tag_event_handler (GtkTextTag *tag, GtkWidget *widget, GdkEvent *event,
316 const GtkTextIter *iter, gpointer user_data)
320 char_index = gtk_text_iter_get_offset (iter);
324 case GDK_MOTION_NOTIFY:
325 printf ("Motion event at char %d tag `%s'\n",
326 char_index, tag->name);
329 case GDK_BUTTON_PRESS:
330 printf ("Button press at char %d tag `%s'\n",
331 char_index, tag->name);
334 case GDK_2BUTTON_PRESS:
335 printf ("Double click at char %d tag `%s'\n",
336 char_index, tag->name);
339 case GDK_3BUTTON_PRESS:
340 printf ("Triple click at char %d tag `%s'\n",
341 char_index, tag->name);
344 case GDK_BUTTON_RELEASE:
345 printf ("Button release at char %d tag `%s'\n",
346 char_index, tag->name);
350 case GDK_KEY_RELEASE:
351 printf ("Key event at char %d tag `%s'\n",
352 char_index, tag->name);
355 case GDK_ENTER_NOTIFY:
356 case GDK_LEAVE_NOTIFY:
357 case GDK_PROPERTY_NOTIFY:
358 case GDK_SELECTION_CLEAR:
359 case GDK_SELECTION_REQUEST:
360 case GDK_SELECTION_NOTIFY:
361 case GDK_PROXIMITY_IN:
362 case GDK_PROXIMITY_OUT:
365 case GDK_DRAG_MOTION:
366 case GDK_DRAG_STATUS:
368 case GDK_DROP_FINISHED:
377 setup_tag (GtkTextTag *tag)
380 gtk_signal_connect (GTK_OBJECT (tag),
382 GTK_SIGNAL_FUNC (tag_event_handler),
386 static char *book_closed_xpm[] = {
414 fill_example_buffer (GtkTextBuffer *buffer)
416 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, "overstrike");
468 gtk_object_set (GTK_OBJECT (tag),
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,
503 pixmap = gdk_pixmap_colormap_create_from_xpm_d (NULL,
504 gtk_widget_get_default_colormap (),
506 NULL, book_closed_xpm);
508 g_assert (pixmap != NULL);
513 GtkTextMark * temp_mark;
515 gtk_text_buffer_get_iter_at_offset (buffer, &iter, 0);
517 gtk_text_buffer_insert_pixmap (buffer, &iter, pixmap, mask);
519 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",
522 gtk_text_buffer_insert (buffer, &iter, str, -1);
526 gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, 0, 5);
528 gtk_text_buffer_insert (buffer, &iter,
529 "(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"
530 /* This is UTF8 stuff, Emacs doesn't
531 really know how to display it */
532 "German (Deutsch Süd) Grüß Gott Greek (Ελληνικά) Γειά σας Hebrew שלום Japanese (日本語)\n", -1);
535 gtk_text_buffer_create_mark (buffer, "tmp_mark", &iter, TRUE);
538 gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, 0, 6);
539 gtk_text_buffer_get_iter_at_line_offset (buffer, &iter2, 0, 13);
541 gtk_text_buffer_apply_tag_by_name (buffer, "fg_blue", &iter, &iter2);
543 gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, 1, 10);
544 gtk_text_buffer_get_iter_at_line_offset (buffer, &iter2, 1, 16);
546 gtk_text_buffer_apply_tag_by_name (buffer, "underline", &iter, &iter2);
548 gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, 1, 14);
549 gtk_text_buffer_get_iter_at_line_offset (buffer, &iter2, 1, 24);
551 gtk_text_buffer_apply_tag_by_name (buffer, "overstrike", &iter, &iter2);
553 gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, 0, 9);
554 gtk_text_buffer_get_iter_at_line_offset (buffer, &iter2, 0, 16);
556 gtk_text_buffer_apply_tag_by_name (buffer, "bg_green", &iter, &iter2);
558 gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, 4, 2);
559 gtk_text_buffer_get_iter_at_line_offset (buffer, &iter2, 4, 10);
561 gtk_text_buffer_apply_tag_by_name (buffer, "bg_green", &iter, &iter2);
563 gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, 4, 8);
564 gtk_text_buffer_get_iter_at_line_offset (buffer, &iter2, 4, 15);
566 gtk_text_buffer_apply_tag_by_name (buffer, "fg_red", &iter, &iter2);
569 gtk_text_buffer_get_iter_at_mark (buffer, &iter, temp_mark);
570 gtk_text_buffer_insert (buffer, &iter, "Centered text!\n", -1);
572 gtk_text_buffer_get_iter_at_mark (buffer, &iter2, temp_mark);
573 gtk_text_buffer_apply_tag_by_name (buffer, "centered", &iter2, &iter);
575 gtk_text_buffer_move_mark (buffer, temp_mark, &iter);
576 gtk_text_buffer_insert (buffer, &iter, "Word wrapped, Right-to-left Quote\n", -1);
577 gtk_text_buffer_insert (buffer, &iter, "وقد بدأ ثلاث من أكثر المؤسسات تقدما في شبكة اكسيون برامجها كمنظمات لا تسعى للربح، ثم تحولت في السنوات الخمس الماضية إلى مؤسسات مالية منظمة، وباتت جزءا من النظام المالي في بلدانها، ولكنها تتخصص في خدمة قطاع المشروعات الصغيرة. وأحد أكثر هذه المؤسسات نجاحا هو »بانكوسول« في بوليفيا.\n", -1);
578 gtk_text_buffer_get_iter_at_mark (buffer, &iter2, temp_mark);
579 gtk_text_buffer_apply_tag_by_name (buffer, "rtl_quote", &iter2, &iter);
584 gdk_pixmap_unref (pixmap);
586 gdk_bitmap_unref (mask);
588 printf ("%d lines %d chars\n",
589 gtk_text_buffer_get_line_count (buffer),
590 gtk_text_buffer_get_char_count (buffer));
592 gtk_text_buffer_set_modified (buffer, FALSE);
596 fill_file_buffer (GtkTextBuffer *buffer, const char *filename)
601 GtkTextIter iter, end;
603 f = fopen (filename, "r");
607 gchar *err = g_strdup_printf ("Cannot open file '%s': %s",
608 filename, g_strerror (errno));
609 msgbox_run (NULL, err, "OK", NULL, NULL, 0);
614 gtk_text_buffer_get_iter_at_offset (buffer, &iter, 0);
618 char *leftover, *next;
619 int to_read = 2047 - remaining;
621 count = fread (buf + remaining, 1, to_read, f);
622 buf[count + remaining] = '\0';
624 leftover = next = buf;
631 next = g_utf8_next_char (next);
634 gtk_text_buffer_insert (buffer, &iter, buf, leftover - buf);
636 remaining = buf + remaining + count - leftover;
637 g_memmove (buf, leftover, remaining);
639 if (remaining > 6 || count < to_read)
645 gchar *err = g_strdup_printf ("Invalid UTF-8 data encountered reading file '%s'", filename);
646 msgbox_run (NULL, err, "OK", NULL, NULL, 0);
650 /* We had a newline in the buffer to begin with. (The buffer always contains
651 * a newline, so we delete to the end of the buffer to clean up.
653 gtk_text_buffer_get_last_iter (buffer, &end);
654 gtk_text_buffer_delete (buffer, &iter, &end);
656 gtk_text_buffer_set_modified (buffer, FALSE);
662 delete_event_cb (GtkWidget *window, GdkEventAny *event, gpointer data)
664 View *view = view_from_widget (window);
666 push_active_window (GTK_WINDOW (window));
667 check_close_view (view);
668 pop_active_window ();
678 get_empty_view (View *view)
680 if (!view->buffer->filename &&
681 !gtk_text_buffer_modified (view->buffer->buffer))
684 return create_view (create_buffer ());
688 view_from_widget (GtkWidget *widget)
692 if (GTK_IS_MENU_ITEM (widget))
694 GtkItemFactory *item_factory = gtk_item_factory_from_widget (widget);
695 return gtk_object_get_data (GTK_OBJECT (item_factory), "view");
699 GtkWidget *app = gtk_widget_get_toplevel (widget);
700 return gtk_object_get_data (GTK_OBJECT (app), "view");
705 do_new (gpointer callback_data,
706 guint callback_action,
709 create_view (create_buffer ());
713 do_new_view (gpointer callback_data,
714 guint callback_action,
717 View *view = view_from_widget (widget);
719 create_view (view->buffer);
723 open_ok_func (const char *filename, gpointer data)
726 View *new_view = get_empty_view (view);
728 if (!fill_file_buffer (new_view->buffer->buffer, filename))
730 if (new_view != view)
731 close_view (new_view);
736 g_free (new_view->buffer->filename);
737 new_view->buffer->filename = g_strdup (filename);
738 buffer_filename_set (new_view->buffer);
745 do_open (gpointer callback_data,
746 guint callback_action,
749 View *view = view_from_widget (widget);
751 push_active_window (GTK_WINDOW (view->window));
752 filesel_run (NULL, "Open File", NULL, open_ok_func, view);
753 pop_active_window ();
757 do_save_as (gpointer callback_data,
758 guint callback_action,
761 View *view = view_from_widget (widget);
763 push_active_window (GTK_WINDOW (view->window));
764 save_as_buffer (view->buffer);
765 pop_active_window ();
769 do_save (gpointer callback_data,
770 guint callback_action,
773 View *view = view_from_widget (widget);
775 push_active_window (GTK_WINDOW (view->window));
776 if (!view->buffer->filename)
777 do_save_as (callback_data, callback_action, widget);
779 save_buffer (view->buffer);
780 pop_active_window ();
784 do_close (gpointer callback_data,
785 guint callback_action,
788 View *view = view_from_widget (widget);
790 push_active_window (GTK_WINDOW (view->window));
791 check_close_view (view);
792 pop_active_window ();
796 do_exit (gpointer callback_data,
797 guint callback_action,
800 View *view = view_from_widget (widget);
802 GSList *tmp_list = buffers;
804 push_active_window (GTK_WINDOW (view->window));
807 if (!check_buffer_saved (tmp_list->data))
810 tmp_list = tmp_list->next;
814 pop_active_window ();
818 do_example (gpointer callback_data,
819 guint callback_action,
822 View *view = view_from_widget (widget);
825 new_view = get_empty_view (view);
827 fill_example_buffer (new_view->buffer->buffer);
831 do_wrap_changed (gpointer callback_data,
832 guint callback_action,
835 View *view = view_from_widget (widget);
837 gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (view->text_view), callback_action);
841 do_direction_changed (gpointer callback_data,
842 guint callback_action,
845 View *view = view_from_widget (widget);
847 gtk_widget_set_direction (view->text_view, callback_action);
848 gtk_widget_queue_resize (view->text_view);
852 do_editable_changed (gpointer callback_data,
853 guint callback_action,
856 View *view = view_from_widget (widget);
858 gtk_text_view_set_editable (GTK_TEXT_VIEW (view->text_view), callback_action);
862 do_cursor_visible_changed (gpointer callback_data,
863 guint callback_action,
866 View *view = view_from_widget (widget);
868 gtk_text_view_set_cursor_visible (GTK_TEXT_VIEW (view->text_view), callback_action);
872 do_apply_editable (gpointer callback_data,
873 guint callback_action,
876 View *view = view_from_widget (widget);
880 if (gtk_text_buffer_get_selection_bounds (view->buffer->buffer,
885 gtk_text_buffer_remove_tag (view->buffer->buffer,
886 view->buffer->not_editable_tag,
891 gtk_text_buffer_apply_tag (view->buffer->buffer,
892 view->buffer->not_editable_tag,
899 dialog_response_callback (GtkWidget *dialog, gint response_id, gpointer data)
901 GtkTextBuffer *buffer;
903 GtkTextIter start, end;
904 gchar *search_string;
906 buffer = gtk_object_get_data (GTK_OBJECT (dialog), "buffer");
908 gtk_text_buffer_get_bounds (buffer, &start, &end);
910 /* Remove trailing newline */
911 gtk_text_iter_prev_char (&end);
913 search_string = gtk_text_iter_get_text (&start, &end);
915 printf ("Searching for `%s'\n", search_string);
917 buffer_search_forward (view->buffer, search_string, view);
919 g_free (search_string);
921 gtk_widget_destroy (dialog);
925 do_search (gpointer callback_data,
926 guint callback_action,
929 View *view = view_from_widget (widget);
931 GtkWidget *search_text;
932 GtkTextBuffer *buffer;
936 dialog = gtk_dialog_new_with_buttons ("Search",
937 GTK_WINDOW (view->window),
938 GTK_DIALOG_DESTROY_WITH_PARENT,
939 GTK_STOCK_BUTTON_CLOSE,
940 GTK_RESPONSE_NONE, NULL);
942 buffer = gtk_text_buffer_new (NULL);
944 /* FIXME memory leak once buffer is a GObject */
945 search_text = gtk_text_view_new_with_buffer (buffer);
947 gtk_box_pack_end (GTK_BOX (GTK_DIALOG (dialog)->vbox),
951 gtk_object_set_data (GTK_OBJECT (dialog), "buffer", buffer);
953 gtk_signal_connect (GTK_OBJECT (dialog),
955 GTK_SIGNAL_FUNC (dialog_response_callback),
958 gtk_widget_show (search_text);
960 gtk_widget_grab_focus (search_text);
962 gtk_widget_show_all (dialog);
967 view_init_menus (View *view)
969 GtkTextDirection direction = gtk_widget_get_direction (view->text_view);
970 GtkWrapMode wrap_mode = gtk_text_view_get_wrap_mode (GTK_TEXT_VIEW (view->text_view));
971 GtkWidget *menu_item = NULL;
975 case GTK_TEXT_DIR_LTR:
976 menu_item = gtk_item_factory_get_widget (view->item_factory, "/Settings/Left-to-Right");
978 case GTK_TEXT_DIR_RTL:
979 menu_item = gtk_item_factory_get_widget (view->item_factory, "/Settings/Right-to-Left");
986 gtk_menu_item_activate (GTK_MENU_ITEM (menu_item));
990 case GTK_WRAPMODE_NONE:
991 menu_item = gtk_item_factory_get_widget (view->item_factory, "/Settings/Wrap Off");
993 case GTK_WRAPMODE_WORD:
994 menu_item = gtk_item_factory_get_widget (view->item_factory, "/Settings/Wrap Words");
1001 gtk_menu_item_activate (GTK_MENU_ITEM (menu_item));
1004 static GtkItemFactoryEntry menu_items[] =
1006 { "/_File", NULL, 0, 0, "<Branch>" },
1007 { "/File/_New", "<control>N", do_new, 0, NULL },
1008 { "/File/New _View", NULL, do_new_view, 0, NULL },
1009 { "/File/_Open", "<control>O", do_open, 0, NULL },
1010 { "/File/_Save", "<control>S", do_save, 0, NULL },
1011 { "/File/Save _As...", NULL, do_save_as, 0, NULL },
1012 { "/File/sep1", NULL, 0, 0, "<Separator>" },
1013 { "/File/_Close", "<control>W" , do_close, 0, NULL },
1014 { "/File/E_xit", "<control>Q" , do_exit, 0, NULL },
1016 { "/_Edit", NULL, 0, 0, "<Branch>" },
1017 { "/Edit/Find...", NULL, do_search, 0, NULL },
1019 { "/_Settings", NULL, 0, 0, "<Branch>" },
1020 { "/Settings/Wrap _Off", NULL, do_wrap_changed, GTK_WRAPMODE_NONE, "<RadioItem>" },
1021 { "/Settings/Wrap _Words", NULL, do_wrap_changed, GTK_WRAPMODE_WORD, "/Settings/Wrap Off" },
1022 { "/Settings/sep1", NULL, 0, 0, "<Separator>" },
1023 { "/Settings/Editable", NULL, do_editable_changed, TRUE, "<RadioItem>" },
1024 { "/Settings/Not editable", NULL, do_editable_changed, FALSE, "/Settings/Editable" },
1025 { "/Settings/sep1", NULL, 0, 0, "<Separator>" },
1027 { "/Settings/Cursor visible", NULL, do_cursor_visible_changed, TRUE, "<RadioItem>" },
1028 { "/Settings/Cursor not visible", NULL, do_cursor_visible_changed, FALSE, "/Settings/Cursor visible" },
1029 { "/Settings/sep1", NULL, 0, 0, "<Separator>" },
1031 { "/Settings/Left-to-Right", NULL, do_direction_changed, GTK_TEXT_DIR_LTR, "<RadioItem>" },
1032 { "/Settings/Right-to-Left", NULL, do_direction_changed, GTK_TEXT_DIR_RTL, "/Settings/Left-to-Right" },
1033 { "/_Attributes", NULL, 0, 0, "<Branch>" },
1034 { "/Attributes/Editable", NULL, do_apply_editable, TRUE, NULL },
1035 { "/Attributes/Not editable", NULL, do_apply_editable, FALSE, NULL },
1036 { "/_Test", NULL, 0, 0, "<Branch>" },
1037 { "/Test/_Example", NULL, do_example, 0, NULL },
1041 save_buffer (Buffer *buffer)
1043 GtkTextIter start, end;
1045 gboolean result = FALSE;
1046 gboolean have_backup = FALSE;
1047 gchar *bak_filename;
1050 g_return_val_if_fail (buffer->filename != NULL, FALSE);
1052 bak_filename = g_strconcat (buffer->filename, "~", NULL);
1054 if (rename (buffer->filename, bak_filename) != 0)
1056 if (errno != ENOENT)
1058 gchar *err = g_strdup_printf ("Cannot back up '%s' to '%s': %s",
1059 buffer->filename, bak_filename, g_strerror (errno));
1060 msgbox_run (NULL, err, "OK", NULL, NULL, 0);
1069 file = fopen (buffer->filename, "w");
1072 gchar *err = g_strdup_printf ("Cannot back up '%s' to '%s': %s",
1073 buffer->filename, bak_filename, g_strerror (errno));
1074 msgbox_run (NULL, err, "OK", NULL, NULL, 0);
1078 gtk_text_buffer_get_iter_at_offset (buffer->buffer, &start, 0);
1079 gtk_text_buffer_get_last_iter (buffer->buffer, &end);
1081 chars = gtk_text_buffer_get_slice (buffer->buffer, &start, &end, FALSE);
1083 if (fputs (chars, file) == EOF ||
1084 fclose (file) == EOF)
1086 gchar *err = g_strdup_printf ("Error writing to '%s': %s",
1087 buffer->filename, g_strerror (errno));
1088 msgbox_run (NULL, err, "OK", NULL, NULL, 0);
1096 gtk_text_buffer_set_modified (buffer->buffer, FALSE);
1102 if (!result && have_backup)
1104 if (rename (bak_filename, buffer->filename) != 0)
1106 gchar *err = g_strdup_printf ("Error restoring backup file '%s' to '%s': %s\nBackup left as '%s'",
1107 buffer->filename, bak_filename, g_strerror (errno), bak_filename);
1108 msgbox_run (NULL, err, "OK", NULL, NULL, 0);
1113 g_free (bak_filename);
1119 save_as_ok_func (const char *filename, gpointer data)
1121 Buffer *buffer = data;
1122 char *old_filename = buffer->filename;
1124 if (!buffer->filename || strcmp (filename, buffer->filename) != 0)
1126 struct stat statbuf;
1128 if (stat (filename, &statbuf) == 0)
1130 gchar *err = g_strdup_printf ("Ovewrite existing file '%s'?", filename);
1131 gint result = msgbox_run (NULL, err, "Yes", "No", NULL, 1);
1139 buffer->filename = g_strdup (filename);
1141 if (save_buffer (buffer))
1143 g_free (old_filename);
1144 buffer_filename_set (buffer);
1149 g_free (buffer->filename);
1150 buffer->filename = old_filename;
1156 save_as_buffer (Buffer *buffer)
1158 return filesel_run (NULL, "Save File", NULL, save_as_ok_func, buffer);
1162 check_buffer_saved (Buffer *buffer)
1164 if (gtk_text_buffer_modified (buffer->buffer))
1166 char *pretty_name = buffer_pretty_name (buffer);
1167 char *msg = g_strdup_printf ("Save changes to '%s'?", pretty_name);
1170 g_free (pretty_name);
1172 result = msgbox_run (NULL, msg, "Yes", "No", "Cancel", 0);
1176 return save_as_buffer (buffer);
1177 else if (result == 1)
1187 create_buffer (void)
1191 buffer = g_new (Buffer, 1);
1193 buffer->buffer = gtk_text_buffer_new (NULL);
1194 gtk_object_ref (GTK_OBJECT (buffer->buffer));
1195 gtk_object_sink (GTK_OBJECT (buffer->buffer));
1197 buffer->refcount = 1;
1198 buffer->filename = NULL;
1199 buffer->untitled_serial = -1;
1201 buffer->not_editable_tag = gtk_text_buffer_create_tag (buffer->buffer, NULL);
1202 gtk_object_set (GTK_OBJECT (buffer->not_editable_tag),
1204 "foreground", "purple", NULL);
1206 buffer->found_text_tag = gtk_text_buffer_create_tag (buffer->buffer, NULL);
1207 gtk_object_set (GTK_OBJECT (buffer->found_text_tag),
1208 "foreground", "red", NULL);
1210 buffers = g_slist_prepend (buffers, buffer);
1216 buffer_pretty_name (Buffer *buffer)
1218 if (buffer->filename)
1221 char *result = g_path_get_basename (buffer->filename);
1222 p = strchr (result, '/');
1230 if (buffer->untitled_serial == -1)
1231 buffer->untitled_serial = untitled_serial++;
1233 if (buffer->untitled_serial == 1)
1234 return g_strdup ("Untitled");
1236 return g_strdup_printf ("Untitled #%d", buffer->untitled_serial);
1241 buffer_filename_set (Buffer *buffer)
1243 GSList *tmp_list = views;
1247 View *view = tmp_list->data;
1249 if (view->buffer == buffer)
1250 view_set_title (view);
1252 tmp_list = tmp_list->next;
1257 buffer_search_forward (Buffer *buffer, const char *str,
1261 GtkTextIter start, end;
1266 /* remove tag from whole buffer */
1267 gtk_text_buffer_get_bounds (buffer->buffer, &start, &end);
1268 gtk_text_buffer_remove_tag (buffer->buffer, buffer->found_text_tag,
1271 gtk_text_buffer_get_iter_at_mark (buffer->buffer, &iter,
1272 gtk_text_buffer_get_mark (buffer->buffer,
1276 char_len = g_utf8_strlen (str, -1);
1280 while (gtk_text_iter_forward_search (&iter, str, TRUE, FALSE))
1282 GtkTextIter end = iter;
1284 gtk_text_iter_forward_chars (&end, char_len);
1286 gtk_text_buffer_apply_tag (buffer->buffer, buffer->found_text_tag,
1296 dialog = gtk_message_dialog_new (GTK_WINDOW (view->window),
1299 GTK_DIALOG_DESTROY_WITH_PARENT,
1300 "%d strings found and marked in red",
1303 gtk_signal_connect_object (GTK_OBJECT (dialog),
1305 GTK_SIGNAL_FUNC (gtk_widget_destroy),
1306 GTK_OBJECT (dialog));
1308 gtk_widget_show (dialog);
1313 buffer_ref (Buffer *buffer)
1319 buffer_unref (Buffer *buffer)
1322 if (buffer->refcount == 0)
1324 buffers = g_slist_remove (buffers, buffer);
1325 gtk_object_unref (GTK_OBJECT (buffer->buffer));
1326 g_free (buffer->filename);
1332 close_view (View *view)
1334 views = g_slist_remove (views, view);
1335 buffer_unref (view->buffer);
1336 gtk_widget_destroy (view->window);
1337 g_object_unref (G_OBJECT (view->item_factory));
1346 check_close_view (View *view)
1348 if (view->buffer->refcount > 1 ||
1349 check_buffer_saved (view->buffer))
1354 view_set_title (View *view)
1356 char *pretty_name = buffer_pretty_name (view->buffer);
1357 char *title = g_strconcat ("testtext - ", pretty_name, NULL);
1359 gtk_window_set_title (GTK_WINDOW (view->window), title);
1361 g_free (pretty_name);
1366 create_view (Buffer *buffer)
1373 view = g_new0 (View, 1);
1374 views = g_slist_prepend (views, view);
1376 view->buffer = buffer;
1377 buffer_ref (buffer);
1379 view->window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
1380 gtk_object_set_data (GTK_OBJECT (view->window), "view", view);
1382 gtk_signal_connect (GTK_OBJECT (view->window), "delete_event",
1383 GTK_SIGNAL_FUNC (delete_event_cb), NULL);
1385 view->accel_group = gtk_accel_group_new ();
1386 view->item_factory = gtk_item_factory_new (GTK_TYPE_MENU_BAR, "<main>", view->accel_group);
1387 gtk_object_set_data (GTK_OBJECT (view->item_factory), "view", view);
1389 gtk_item_factory_create_items (view->item_factory, G_N_ELEMENTS (menu_items), menu_items, view);
1391 gtk_window_add_accel_group (GTK_WINDOW (view->window), view->accel_group);
1393 vbox = gtk_vbox_new (FALSE, 0);
1394 gtk_container_add (GTK_CONTAINER (view->window), vbox);
1396 gtk_box_pack_start (GTK_BOX (vbox),
1397 gtk_item_factory_get_widget (view->item_factory, "<main>"),
1400 sw = gtk_scrolled_window_new (NULL, NULL);
1401 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
1402 GTK_POLICY_AUTOMATIC,
1403 GTK_POLICY_AUTOMATIC);
1405 view->text_view = gtk_text_view_new_with_buffer (buffer->buffer);
1406 gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (view->text_view), GTK_WRAPMODE_WORD);
1408 gtk_box_pack_start (GTK_BOX (vbox), sw, TRUE, TRUE, 0);
1409 gtk_container_add (GTK_CONTAINER (sw), view->text_view);
1411 gtk_window_set_default_size (GTK_WINDOW (view->window), 500, 500);
1413 gtk_widget_grab_focus (view->text_view);
1415 view_set_title (view);
1416 view_init_menus (view);
1418 gtk_widget_show_all (view->window);
1423 main (int argc, char** argv)
1429 gtk_init (&argc, &argv);
1430 gdk_rgb_init (); /* FIXME remove this */
1432 buffer = create_buffer ();
1433 view = create_view (buffer);
1434 buffer_unref (buffer);
1436 push_active_window (GTK_WINDOW (view->window));
1437 for (i=1; i < argc; i++)
1441 /* Quick and dirty canonicalization - better should be in GLib
1444 if (!g_path_is_absolute (argv[i]))
1446 char *cwd = g_get_current_dir ();
1447 filename = g_strconcat (cwd, "/", argv[i], NULL);
1453 open_ok_func (filename, view);
1455 if (filename != argv[i])
1458 pop_active_window ();