9 #include <gdk/gdkkeysyms.h>
11 typedef struct _Buffer Buffer;
12 typedef struct _View View;
14 static gint untitled_serial = 1;
16 GSList *active_window_stack = NULL;
21 GtkTextBuffer *buffer;
30 GtkAccelGroup *accel_group;
31 GtkItemFactory *item_factory;
35 static void push_active_window (GtkWindow *window);
36 static void pop_active_window (void);
37 static GtkWindow *get_active_window (void);
39 static Buffer * create_buffer (void);
40 static gboolean check_buffer_saved (Buffer *buffer);
41 static gboolean save_buffer (Buffer *buffer);
42 static gboolean save_as_buffer (Buffer *buffer);
43 static char * buffer_pretty_name (Buffer *buffer);
44 static void buffer_filename_set (Buffer *buffer);
46 static View *view_from_widget (GtkWidget *widget);
48 static View *create_view (Buffer *buffer);
49 static void check_close_view (View *view);
50 static void close_view (View *view);
51 static void view_set_title (View *view);
52 static void view_init_menus (View *view);
54 GSList *buffers = NULL;
58 push_active_window (GtkWindow *window)
60 gtk_object_ref (GTK_OBJECT (window));
61 active_window_stack = g_slist_prepend (active_window_stack, window);
65 pop_active_window (void)
67 gtk_object_unref (active_window_stack->data);
68 active_window_stack = g_slist_delete_link (active_window_stack, active_window_stack);
72 get_active_window (void)
74 if (active_window_stack)
75 return active_window_stack->data;
81 * Filesel utility function
84 typedef gboolean (*FileselOKFunc) (const char *filename, gpointer data);
87 filesel_ok_cb (GtkWidget *button, GtkWidget *filesel)
89 FileselOKFunc ok_func = gtk_object_get_data (GTK_OBJECT (filesel), "ok-func");
90 gpointer data = gtk_object_get_data (GTK_OBJECT (filesel), "ok-data");
91 gint *result = gtk_object_get_data (GTK_OBJECT (filesel), "ok-result");
93 gtk_widget_hide (filesel);
95 if ((*ok_func) (gtk_file_selection_get_filename (GTK_FILE_SELECTION (filesel)), data))
97 gtk_widget_destroy (filesel);
101 gtk_widget_show (filesel);
105 filesel_run (GtkWindow *parent,
107 const char *start_file,
111 GtkWidget *filesel = gtk_file_selection_new (title);
112 gboolean result = FALSE;
115 parent = get_active_window ();
118 gtk_window_set_transient_for (GTK_WINDOW (filesel), parent);
121 gtk_file_selection_set_filename (GTK_FILE_SELECTION (filesel), start_file);
124 gtk_object_set_data (GTK_OBJECT (filesel), "ok-func", func);
125 gtk_object_set_data (GTK_OBJECT (filesel), "ok-data", data);
126 gtk_object_set_data (GTK_OBJECT (filesel), "ok-result", &result);
128 gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION (filesel)->ok_button),
130 GTK_SIGNAL_FUNC (filesel_ok_cb), filesel);
131 gtk_signal_connect_object (GTK_OBJECT (GTK_FILE_SELECTION (filesel)->cancel_button),
133 GTK_SIGNAL_FUNC (gtk_widget_destroy), GTK_OBJECT (filesel));
135 gtk_signal_connect (GTK_OBJECT (filesel), "destroy",
136 GTK_SIGNAL_FUNC (gtk_main_quit), NULL);
137 gtk_window_set_modal (GTK_WINDOW (filesel), TRUE);
139 gtk_widget_show (filesel);
146 * MsgBox utility functions
150 msgbox_yes_cb (GtkWidget *widget, gboolean *result)
153 gtk_object_destroy (GTK_OBJECT (gtk_widget_get_toplevel (widget)));
157 msgbox_no_cb (GtkWidget *widget, gboolean *result)
160 gtk_object_destroy (GTK_OBJECT (gtk_widget_get_toplevel (widget)));
164 msgbox_key_press_cb (GtkWidget *widget, GdkEventKey *event, gpointer data)
166 if (event->keyval == GDK_Escape)
168 gtk_signal_emit_stop_by_name (GTK_OBJECT (widget), "key_press_event");
169 gtk_object_destroy (GTK_OBJECT (widget));
177 msgbox_run (GtkWindow *parent,
179 const char *yes_button,
180 const char *no_button,
181 const char *cancel_button,
184 gboolean result = -1;
189 GtkWidget *button_box;
190 GtkWidget *separator;
192 g_return_val_if_fail (message != NULL, FALSE);
193 g_return_val_if_fail (default_index >= 0 && default_index <= 1, FALSE);
196 parent = get_active_window ();
200 dialog = gtk_window_new (GTK_WINDOW_TOPLEVEL);
201 gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
203 gtk_window_set_transient_for (GTK_WINDOW (dialog), parent);
204 gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
206 /* Quit our recursive main loop when the dialog is destroyed.
208 gtk_signal_connect (GTK_OBJECT (dialog), "destroy",
209 GTK_SIGNAL_FUNC (gtk_main_quit), NULL);
211 /* Catch Escape key presses and have them destroy the dialog
213 gtk_signal_connect (GTK_OBJECT (dialog), "key_press_event",
214 GTK_SIGNAL_FUNC (msgbox_key_press_cb), NULL);
216 /* Fill in the contents of the widget
218 vbox = gtk_vbox_new (FALSE, 0);
219 gtk_container_add (GTK_CONTAINER (dialog), vbox);
221 label = gtk_label_new (message);
222 gtk_misc_set_padding (GTK_MISC (label), 12, 12);
223 gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
224 gtk_box_pack_start (GTK_BOX (vbox), label, TRUE, TRUE, 0);
226 separator = gtk_hseparator_new ();
227 gtk_box_pack_start (GTK_BOX (vbox), separator, FALSE, FALSE, 0);
229 button_box = gtk_hbutton_box_new ();
230 gtk_box_pack_start (GTK_BOX (vbox), button_box, FALSE, FALSE, 0);
231 gtk_container_set_border_width (GTK_CONTAINER (button_box), 8);
234 /* When Yes is clicked, call the msgbox_yes_cb
235 * This sets the result variable and destroys the dialog
239 button = gtk_button_new_with_label (yes_button);
240 GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
241 gtk_container_add (GTK_CONTAINER (button_box), button);
243 if (default_index == 0)
244 gtk_widget_grab_default (button);
246 gtk_signal_connect (GTK_OBJECT (button), "clicked",
247 GTK_SIGNAL_FUNC (msgbox_yes_cb), &result);
250 /* When No is clicked, call the msgbox_no_cb
251 * This sets the result variable and destroys the dialog
255 button = gtk_button_new_with_label (no_button);
256 GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
257 gtk_container_add (GTK_CONTAINER (button_box), button);
259 if (default_index == 0)
260 gtk_widget_grab_default (button);
262 gtk_signal_connect (GTK_OBJECT (button), "clicked",
263 GTK_SIGNAL_FUNC (msgbox_no_cb), &result);
266 /* When Cancel is clicked, destroy the dialog
270 button = gtk_button_new_with_label (cancel_button);
271 GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
272 gtk_container_add (GTK_CONTAINER (button_box), button);
274 if (default_index == 1)
275 gtk_widget_grab_default (button);
277 gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
278 GTK_SIGNAL_FUNC (gtk_object_destroy), GTK_OBJECT (dialog));
281 gtk_widget_show_all (dialog);
283 /* Run a recursive main loop until a button is clicked
284 * or the user destroys the dialog through the window mananger */
291 * Example buffer filling code
294 blink_timeout(gpointer data)
297 static gboolean flip = FALSE;
299 tag = GTK_TEXT_TAG(data);
301 gtk_object_set(GTK_OBJECT(tag),
302 "foreground", flip ? "blue" : "purple",
311 tag_event_handler(GtkTextTag *tag, GtkWidget *widget, GdkEvent *event,
312 const GtkTextIter *iter, gpointer user_data)
316 char_index = gtk_text_iter_get_char_index(iter);
320 case GDK_MOTION_NOTIFY:
321 printf("Motion event at char %d tag `%s'\n",
322 char_index, tag->name);
325 case GDK_BUTTON_PRESS:
326 printf("Button press at char %d tag `%s'\n",
327 char_index, tag->name);
330 case GDK_2BUTTON_PRESS:
331 printf("Double click at char %d tag `%s'\n",
332 char_index, tag->name);
335 case GDK_3BUTTON_PRESS:
336 printf("Triple click at char %d tag `%s'\n",
337 char_index, tag->name);
340 case GDK_BUTTON_RELEASE:
341 printf("Button release at char %d tag `%s'\n",
342 char_index, tag->name);
346 case GDK_KEY_RELEASE:
347 case GDK_ENTER_NOTIFY:
348 case GDK_LEAVE_NOTIFY:
349 case GDK_PROPERTY_NOTIFY:
350 case GDK_SELECTION_CLEAR:
351 case GDK_SELECTION_REQUEST:
352 case GDK_SELECTION_NOTIFY:
353 case GDK_PROXIMITY_IN:
354 case GDK_PROXIMITY_OUT:
357 case GDK_DRAG_MOTION:
358 case GDK_DRAG_STATUS:
360 case GDK_DROP_FINISHED:
369 setup_tag(GtkTextTag *tag)
372 gtk_signal_connect(GTK_OBJECT(tag),
374 GTK_SIGNAL_FUNC(tag_event_handler),
378 static char *book_closed_xpm[] = {
406 fill_example_buffer (GtkTextBuffer *buffer)
408 GtkTextIter iter, iter2;
417 tag = gtk_text_buffer_create_tag(buffer, "fg_blue");
419 /* gtk_timeout_add(1000, blink_timeout, tag); */
423 color.red = color.green = 0;
428 gtk_object_set(GTK_OBJECT(tag),
429 "foreground_gdk", &color,
430 "background_gdk", &color2,
434 tag = gtk_text_buffer_create_tag(buffer, "fg_red");
438 color.blue = color.green = 0;
440 gtk_object_set(GTK_OBJECT(tag),
442 "foreground_gdk", &color,
445 tag = gtk_text_buffer_create_tag(buffer, "bg_green");
449 color.blue = color.red = 0;
450 color.green = 0xffff;
451 gtk_object_set(GTK_OBJECT(tag),
452 "background_gdk", &color,
456 tag = gtk_text_buffer_create_tag(buffer, "overstrike");
460 gtk_object_set(GTK_OBJECT(tag),
465 tag = gtk_text_buffer_create_tag(buffer, "underline");
469 gtk_object_set(GTK_OBJECT(tag),
470 "underline", PANGO_UNDERLINE_SINGLE,
475 gtk_object_set(GTK_OBJECT(tag),
476 "underline", PANGO_UNDERLINE_SINGLE,
479 tag = gtk_text_buffer_create_tag(buffer, "centered");
481 gtk_object_set(GTK_OBJECT(tag),
482 "justify", GTK_JUSTIFY_CENTER,
485 tag = gtk_text_buffer_create_tag(buffer, "rtl_quote");
487 gtk_object_set(GTK_OBJECT(tag),
488 "wrap_mode", GTK_WRAPMODE_WORD,
489 "direction", GTK_TEXT_DIR_RTL,
490 "left_wrapped_line_margin", 20,
495 pixmap = gdk_pixmap_colormap_create_from_xpm_d (NULL,
496 gtk_widget_get_default_colormap(),
498 NULL, book_closed_xpm);
500 g_assert(pixmap != NULL);
505 GtkTextMark * temp_mark;
507 gtk_text_buffer_get_iter_at_char(buffer, &iter, 0);
509 gtk_text_buffer_insert_pixmap (buffer, &iter, pixmap, mask);
511 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",
514 gtk_text_buffer_insert(buffer, &iter, str, -1);
518 gtk_text_buffer_get_iter_at_line_char(buffer, &iter, 0, 5);
520 gtk_text_buffer_insert(buffer, &iter,
521 "(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"
522 /* This is UTF8 stuff, Emacs doesn't
523 really know how to display it */
524 "German (Deutsch Süd) Grüß Gott Greek (Ελληνικά) Γειά σας Hebrew שלום Japanese (日本語)\n", -1);
527 gtk_text_buffer_create_mark (buffer, "tmp_mark", &iter, TRUE);
530 gtk_text_buffer_get_iter_at_line_char(buffer, &iter, 0, 6);
531 gtk_text_buffer_get_iter_at_line_char(buffer, &iter2, 0, 13);
533 gtk_text_buffer_apply_tag(buffer, "fg_blue", &iter, &iter2);
535 gtk_text_buffer_get_iter_at_line_char(buffer, &iter, 1, 10);
536 gtk_text_buffer_get_iter_at_line_char(buffer, &iter2, 1, 16);
538 gtk_text_buffer_apply_tag(buffer, "underline", &iter, &iter2);
540 gtk_text_buffer_get_iter_at_line_char(buffer, &iter, 1, 14);
541 gtk_text_buffer_get_iter_at_line_char(buffer, &iter2, 1, 24);
543 gtk_text_buffer_apply_tag(buffer, "overstrike", &iter, &iter2);
545 gtk_text_buffer_get_iter_at_line_char(buffer, &iter, 0, 9);
546 gtk_text_buffer_get_iter_at_line_char(buffer, &iter2, 0, 16);
548 gtk_text_buffer_apply_tag(buffer, "bg_green", &iter, &iter2);
550 gtk_text_buffer_get_iter_at_line_char(buffer, &iter, 4, 2);
551 gtk_text_buffer_get_iter_at_line_char(buffer, &iter2, 4, 10);
553 gtk_text_buffer_apply_tag(buffer, "bg_green", &iter, &iter2);
555 gtk_text_buffer_get_iter_at_line_char(buffer, &iter, 4, 8);
556 gtk_text_buffer_get_iter_at_line_char(buffer, &iter2, 4, 15);
558 gtk_text_buffer_apply_tag(buffer, "fg_red", &iter, &iter2);
561 gtk_text_buffer_get_iter_at_mark (buffer, &iter, temp_mark);
562 gtk_text_buffer_insert (buffer, &iter, "Centered text!\n", -1);
564 gtk_text_buffer_get_iter_at_mark (buffer, &iter2, temp_mark);
565 gtk_text_buffer_apply_tag (buffer, "centered", &iter2, &iter);
567 gtk_text_buffer_move_mark (buffer, temp_mark, &iter);
568 gtk_text_buffer_insert (buffer, &iter, "Word wrapped, Right-to-left Quote\n", -1);
569 gtk_text_buffer_insert (buffer, &iter, "وقد بدأ ثلاث من أكثر المؤسسات تقدما في شبكة اكسيون برامجها كمنظمات لا تسعى للربح، ثم تحولت في السنوات الخمس الماضية إلى مؤسسات مالية منظمة، وباتت جزءا من النظام المالي في بلدانها، ولكنها تتخصص في خدمة قطاع المشروعات الصغيرة. وأحد أكثر هذه المؤسسات نجاحا هو »بانكوسول« في بوليفيا.\n", -1);
570 gtk_text_buffer_get_iter_at_mark (buffer, &iter2, temp_mark);
571 gtk_text_buffer_apply_tag (buffer, "rtl_quote", &iter2, &iter);
576 gdk_pixmap_unref(pixmap);
578 gdk_bitmap_unref(mask);
580 printf("%d lines %d chars\n",
581 gtk_text_buffer_get_line_count(buffer),
582 gtk_text_buffer_get_char_count(buffer));
584 gtk_text_buffer_set_modified (buffer, FALSE);
588 fill_file_buffer (GtkTextBuffer *buffer, const char *filename)
593 GtkTextIter iter, end;
595 f = fopen(filename, "r");
599 gchar *err = g_strdup_printf ("Cannot open file '%s': %s",
600 filename, g_strerror (errno));
601 msgbox_run (NULL, err, "OK", NULL, NULL, 0);
606 gtk_text_buffer_get_iter_at_char(buffer, &iter, 0);
610 char *leftover, *next;
612 int to_read = 2047 - remaining;
614 count = fread (buf + remaining, 1, to_read, f);
615 buf[count + remaining] = '\0';
617 leftover = next = buf;
624 next = unicode_get_utf8 (next, &wc);
627 gtk_text_buffer_insert (buffer, &iter, buf, leftover - buf);
629 remaining = buf + remaining + count - leftover;
630 g_memmove (buf, leftover, remaining);
632 if (remaining > 6 || count < to_read)
638 gchar *err = g_strdup_printf ("Invalid UTF-8 data encountered reading file '%s'", filename);
639 msgbox_run (NULL, err, "OK", NULL, NULL, 0);
643 /* We had a newline in the buffer to begin with. (The buffer always contains
644 * a newline, so we delete to the end of the buffer to clean up.
646 gtk_text_buffer_get_last_iter (buffer, &end);
647 gtk_text_buffer_delete (buffer, &iter, &end);
649 gtk_text_buffer_set_modified (buffer, FALSE);
655 delete_event_cb(GtkWidget *window, GdkEventAny *event, gpointer data)
657 View *view = view_from_widget (window);
659 push_active_window (GTK_WINDOW (window));
660 check_close_view (view);
661 pop_active_window ();
671 get_empty_view (View *view)
673 if (!view->buffer->filename &&
674 !gtk_text_buffer_modified (view->buffer->buffer))
677 return create_view (create_buffer ());
681 view_from_widget (GtkWidget *widget)
685 if (GTK_IS_MENU_ITEM (widget))
687 GtkItemFactory *item_factory = gtk_item_factory_from_widget (widget);
688 return gtk_object_get_data (GTK_OBJECT (item_factory), "view");
692 GtkWidget *app = gtk_widget_get_toplevel (widget);
693 return gtk_object_get_data (GTK_OBJECT (app), "view");
698 do_new (gpointer callback_data,
699 guint callback_action,
702 create_view (create_buffer ());
706 do_new_view (gpointer callback_data,
707 guint callback_action,
710 View *view = view_from_widget (widget);
712 create_view (view->buffer);
716 open_ok_func (const char *filename, gpointer data)
719 View *new_view = get_empty_view (view);
721 if (!fill_file_buffer (new_view->buffer->buffer, filename))
723 if (new_view != view)
724 close_view (new_view);
729 g_free (new_view->buffer->filename);
730 new_view->buffer->filename = g_strdup (filename);
731 buffer_filename_set (new_view->buffer);
738 do_open (gpointer callback_data,
739 guint callback_action,
742 View *view = view_from_widget (widget);
744 push_active_window (GTK_WINDOW (view->window));
745 filesel_run (NULL, "Open File", NULL, open_ok_func, view);
746 pop_active_window ();
750 do_save_as (gpointer callback_data,
751 guint callback_action,
754 View *view = view_from_widget (widget);
756 push_active_window (GTK_WINDOW (view->window));
757 save_as_buffer (view->buffer);
758 pop_active_window ();
762 do_save (gpointer callback_data,
763 guint callback_action,
766 View *view = view_from_widget (widget);
768 push_active_window (GTK_WINDOW (view->window));
769 if (!view->buffer->filename)
770 do_save_as (callback_data, callback_action, widget);
772 save_buffer (view->buffer);
773 pop_active_window ();
777 do_close (gpointer callback_data,
778 guint callback_action,
781 View *view = view_from_widget (widget);
783 push_active_window (GTK_WINDOW (view->window));
784 check_close_view (view);
785 pop_active_window ();
789 do_exit (gpointer callback_data,
790 guint callback_action,
793 View *view = view_from_widget (widget);
795 GSList *tmp_list = buffers;
797 push_active_window (GTK_WINDOW (view->window));
800 if (!check_buffer_saved (tmp_list->data))
803 tmp_list = tmp_list->next;
807 pop_active_window ();
811 do_example (gpointer callback_data,
812 guint callback_action,
815 View *view = view_from_widget (widget);
818 new_view = get_empty_view (view);
820 fill_example_buffer (new_view->buffer->buffer);
824 do_wrap_changed (gpointer callback_data,
825 guint callback_action,
828 View *view = view_from_widget (widget);
830 gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (view->text_view), callback_action);
834 do_direction_changed (gpointer callback_data,
835 guint callback_action,
838 View *view = view_from_widget (widget);
840 gtk_widget_set_direction (view->text_view, callback_action);
841 gtk_widget_queue_resize (view->text_view);
845 view_init_menus (View *view)
847 GtkTextDirection direction = gtk_widget_get_direction (view->text_view);
848 GtkWrapMode wrap_mode = gtk_text_view_get_wrap_mode (GTK_TEXT_VIEW (view->text_view));
849 GtkWidget *menu_item = NULL;
853 case GTK_TEXT_DIR_LTR:
854 menu_item = gtk_item_factory_get_widget (view->item_factory, "/Settings/Left-to-Right");
856 case GTK_TEXT_DIR_RTL:
857 menu_item = gtk_item_factory_get_widget (view->item_factory, "/Settings/Right-to-Left");
864 gtk_menu_item_activate (GTK_MENU_ITEM (menu_item));
868 case GTK_WRAPMODE_NONE:
869 menu_item = gtk_item_factory_get_widget (view->item_factory, "/Settings/Wrap Off");
871 case GTK_WRAPMODE_WORD:
872 menu_item = gtk_item_factory_get_widget (view->item_factory, "/Settings/Wrap Words");
879 gtk_menu_item_activate (GTK_MENU_ITEM (menu_item));
882 static GtkItemFactoryEntry menu_items[] =
884 { "/_File", NULL, 0, 0, "<Branch>" },
885 { "/File/_New", "<control>N", do_new, 0, NULL },
886 { "/File/New _View", NULL, do_new_view, 0, NULL },
887 { "/File/_Open", "<control>O", do_open, 0, NULL },
888 { "/File/_Save", "<control>S", do_save, 0, NULL },
889 { "/File/Save _As...", NULL, do_save_as, 0, NULL },
890 { "/File/sep1", NULL, 0, 0, "<Separator>" },
891 { "/File/_Close", "<control>W" , do_close, 0, NULL },
892 { "/File/E_xit", "<control>Q" , do_exit, 0, NULL },
894 { "/_Settings", NULL, 0, 0, "<Branch>" },
895 { "/Settings/Wrap _Off", NULL, do_wrap_changed, GTK_WRAPMODE_NONE, "<RadioItem>" },
896 { "/Settings/Wrap _Words", NULL, do_wrap_changed, GTK_WRAPMODE_WORD, "/Settings/Wrap Off" },
897 { "/Settings/sep1", NULL, 0, 0, "<Separator>" },
898 { "/Settings/Left-to-Right", NULL, do_direction_changed, GTK_TEXT_DIR_LTR, "<RadioItem>" },
899 { "/Settings/Right-to-Left", NULL, do_direction_changed, GTK_TEXT_DIR_RTL, "/Settings/Left-to-Right" },
901 { "/_Test", NULL, 0, 0, "<Branch>" },
902 { "/Test/_Example", NULL, do_example, 0, NULL },
906 save_buffer (Buffer *buffer)
908 GtkTextIter start, end;
910 gboolean result = FALSE;
911 gboolean have_backup = FALSE;
915 g_return_val_if_fail (buffer->filename != NULL, FALSE);
917 bak_filename = g_strconcat (buffer->filename, "~", NULL);
919 if (rename (buffer->filename, bak_filename) != 0)
923 gchar *err = g_strdup_printf ("Cannot back up '%s' to '%s': %s",
924 buffer->filename, bak_filename, g_strerror (errno));
925 msgbox_run (NULL, err, "OK", NULL, NULL, 0);
934 file = fopen (buffer->filename, "w");
937 gchar *err = g_strdup_printf ("Cannot back up '%s' to '%s': %s",
938 buffer->filename, bak_filename, g_strerror (errno));
939 msgbox_run (NULL, err, "OK", NULL, NULL, 0);
943 gtk_text_buffer_get_iter_at_char (buffer->buffer, &start, 0);
944 gtk_text_buffer_get_last_iter (buffer->buffer, &end);
946 chars = gtk_text_buffer_get_slice (buffer->buffer, &start, &end, FALSE);
948 if (fputs (chars, file) == EOF ||
949 fclose (file) == EOF)
951 gchar *err = g_strdup_printf ("Error writing to '%s': %s",
952 buffer->filename, g_strerror (errno));
953 msgbox_run (NULL, err, "OK", NULL, NULL, 0);
961 gtk_text_buffer_set_modified (buffer->buffer, FALSE);
967 if (!result && have_backup)
969 if (rename (bak_filename, buffer->filename) != 0)
971 gchar *err = g_strdup_printf ("Error restoring backup file '%s' to '%s': %s\nBackup left as '%s'",
972 buffer->filename, bak_filename, g_strerror (errno), bak_filename);
973 msgbox_run (NULL, err, "OK", NULL, NULL, 0);
978 g_free (bak_filename);
984 save_as_ok_func (const char *filename, gpointer data)
986 Buffer *buffer = data;
987 char *old_filename = buffer->filename;
989 if (!buffer->filename || strcmp (filename, buffer->filename) != 0)
993 if (stat(filename, &statbuf) == 0)
995 gchar *err = g_strdup_printf ("Ovewrite existing file '%s'?", filename);
996 gint result = msgbox_run (NULL, err, "Yes", "No", NULL, 1);
1004 buffer->filename = g_strdup (filename);
1006 if (save_buffer (buffer))
1008 g_free (old_filename);
1009 buffer_filename_set (buffer);
1014 g_free (buffer->filename);
1015 buffer->filename = old_filename;
1021 save_as_buffer (Buffer *buffer)
1023 return filesel_run (NULL, "Save File", NULL, save_as_ok_func, buffer);
1027 check_buffer_saved (Buffer *buffer)
1029 if (gtk_text_buffer_modified (buffer->buffer))
1031 char *pretty_name = buffer_pretty_name (buffer);
1032 char *msg = g_strdup_printf ("Save changes to '%s'?", pretty_name);
1035 g_free (pretty_name);
1037 result = msgbox_run (NULL, msg, "Yes", "No", "Cancel", 0);
1041 return save_as_buffer (buffer);
1042 else if (result == 1)
1052 create_buffer (void)
1056 buffer = g_new (Buffer, 1);
1058 buffer->buffer = gtk_text_buffer_new (NULL);
1059 gtk_object_ref (GTK_OBJECT (buffer->buffer));
1060 gtk_object_sink (GTK_OBJECT (buffer->buffer));
1062 buffer->refcount = 1;
1063 buffer->filename = NULL;
1064 buffer->untitled_serial = -1;
1066 buffers = g_slist_prepend (buffers, buffer);
1072 buffer_pretty_name (Buffer *buffer)
1074 if (buffer->filename)
1077 char *result = g_strdup (g_basename (buffer->filename));
1078 p = strchr (result, '/');
1086 if (buffer->untitled_serial == -1)
1087 buffer->untitled_serial = untitled_serial++;
1089 if (buffer->untitled_serial == 1)
1090 return g_strdup ("Untitled");
1092 return g_strdup_printf ("Untitled #%d", buffer->untitled_serial);
1097 buffer_filename_set (Buffer *buffer)
1099 GSList *tmp_list = views;
1103 View *view = tmp_list->data;
1105 if (view->buffer == buffer)
1106 view_set_title (view);
1108 tmp_list = tmp_list->next;
1113 buffer_ref (Buffer *buffer)
1119 buffer_unref (Buffer *buffer)
1122 if (buffer->refcount == 0)
1124 buffers = g_slist_remove (buffers, buffer);
1125 gtk_object_unref (GTK_OBJECT (buffer->buffer));
1126 g_free (buffer->filename);
1132 close_view (View *view)
1134 views = g_slist_remove (views, view);
1135 buffer_unref (view->buffer);
1136 gtk_widget_destroy (view->window);
1137 g_object_unref (G_OBJECT (view->item_factory));
1146 check_close_view (View *view)
1148 if (view->buffer->refcount > 1 ||
1149 check_buffer_saved (view->buffer))
1154 view_set_title (View *view)
1156 char *pretty_name = buffer_pretty_name (view->buffer);
1157 char *title = g_strconcat ("testtext - ", pretty_name, NULL);
1159 gtk_window_set_title (GTK_WINDOW (view->window), title);
1161 g_free (pretty_name);
1166 create_view (Buffer *buffer)
1173 view = g_new0 (View, 1);
1174 views = g_slist_prepend (views, view);
1176 view->buffer = buffer;
1177 buffer_ref (buffer);
1179 view->window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
1180 gtk_object_set_data (GTK_OBJECT (view->window), "view", view);
1182 gtk_signal_connect (GTK_OBJECT (view->window), "delete_event",
1183 GTK_SIGNAL_FUNC(delete_event_cb), NULL);
1185 view->accel_group = gtk_accel_group_new ();
1186 view->item_factory = gtk_item_factory_new (GTK_TYPE_MENU_BAR, "<main>", view->accel_group);
1187 gtk_object_set_data (GTK_OBJECT (view->item_factory), "view", view);
1189 gtk_item_factory_create_items (view->item_factory, G_N_ELEMENTS (menu_items), menu_items, view);
1191 gtk_window_add_accel_group (GTK_WINDOW (view->window), view->accel_group);
1193 vbox = gtk_vbox_new (FALSE, 0);
1194 gtk_container_add (GTK_CONTAINER (view->window), vbox);
1196 gtk_box_pack_start (GTK_BOX (vbox),
1197 gtk_item_factory_get_widget (view->item_factory, "<main>"),
1200 sw = gtk_scrolled_window_new(NULL, NULL);
1201 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
1202 GTK_POLICY_AUTOMATIC,
1203 GTK_POLICY_AUTOMATIC);
1205 view->text_view = gtk_text_view_new_with_buffer (buffer->buffer);
1206 gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (view->text_view), GTK_WRAPMODE_WORD);
1208 gtk_box_pack_start (GTK_BOX (vbox), sw, TRUE, TRUE, 0);
1209 gtk_container_add(GTK_CONTAINER(sw), view->text_view);
1211 gtk_window_set_default_size (GTK_WINDOW (view->window), 500, 500);
1213 gtk_widget_grab_focus(view->text_view);
1215 view_set_title (view);
1216 view_init_menus (view);
1218 gtk_widget_show_all (view->window);
1223 main(int argc, char** argv)
1229 gtk_init(&argc, &argv);
1231 buffer = create_buffer ();
1232 view = create_view (buffer);
1233 buffer_unref (buffer);
1235 push_active_window (GTK_WINDOW (view->window));
1236 for (i=1; i < argc; i++)
1240 /* Quick and dirty canonicalization - better should be in GLib
1243 if (!g_path_is_absolute (argv[i]))
1245 char *cwd = g_get_current_dir ();
1246 filename = g_strconcat (cwd, "/", argv[i], NULL);
1252 open_ok_func (filename, view);
1254 if (filename != argv[i])
1257 pop_active_window ();