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 gtk_text_buffer_get_iter_at_char(buffer, &iter, 0);
507 gtk_text_buffer_insert_pixmap (buffer, &iter, pixmap, mask);
509 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",
512 gtk_text_buffer_insert(buffer, &iter, str, -1);
516 gtk_text_buffer_get_iter_at_line_char(buffer, &iter, 0, 5);
518 gtk_text_buffer_insert(buffer, &iter,
519 "(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"
520 /* This is UTF8 stuff, Emacs doesn't
521 really know how to display it */
522 "German (Deutsch Süd) Grüß Gott Greek (Ελληνικά) Γειά σας Hebrew שלום Japanese (日本語)\n", -1);
524 gtk_text_buffer_create_mark (buffer, "tmp_mark", &iter, TRUE);
527 gtk_text_buffer_get_iter_at_line_char(buffer, &iter, 0, 6);
528 gtk_text_buffer_get_iter_at_line_char(buffer, &iter2, 0, 13);
530 gtk_text_buffer_apply_tag(buffer, "fg_blue", &iter, &iter2);
532 gtk_text_buffer_get_iter_at_line_char(buffer, &iter, 1, 10);
533 gtk_text_buffer_get_iter_at_line_char(buffer, &iter2, 1, 16);
535 gtk_text_buffer_apply_tag(buffer, "underline", &iter, &iter2);
537 gtk_text_buffer_get_iter_at_line_char(buffer, &iter, 1, 14);
538 gtk_text_buffer_get_iter_at_line_char(buffer, &iter2, 1, 24);
540 gtk_text_buffer_apply_tag(buffer, "overstrike", &iter, &iter2);
542 gtk_text_buffer_get_iter_at_line_char(buffer, &iter, 0, 9);
543 gtk_text_buffer_get_iter_at_line_char(buffer, &iter2, 0, 16);
545 gtk_text_buffer_apply_tag(buffer, "bg_green", &iter, &iter2);
547 gtk_text_buffer_get_iter_at_line_char(buffer, &iter, 4, 2);
548 gtk_text_buffer_get_iter_at_line_char(buffer, &iter2, 4, 10);
550 gtk_text_buffer_apply_tag(buffer, "bg_green", &iter, &iter2);
552 gtk_text_buffer_get_iter_at_line_char(buffer, &iter, 4, 8);
553 gtk_text_buffer_get_iter_at_line_char(buffer, &iter2, 4, 15);
555 gtk_text_buffer_apply_tag(buffer, "fg_red", &iter, &iter2);
558 gtk_text_buffer_get_iter_at_mark (buffer, &iter, "tmp_mark");
559 gtk_text_buffer_insert (buffer, &iter, "Centered text!\n", -1);
561 gtk_text_buffer_get_iter_at_mark (buffer, &iter2, "tmp_mark");
562 gtk_text_buffer_apply_tag (buffer, "centered", &iter2, &iter);
564 gtk_text_buffer_move_mark (buffer, "tmp_mark", &iter);
565 gtk_text_buffer_insert (buffer, &iter, "Word wrapped, Right-to-left Quote\n", -1);
566 gtk_text_buffer_insert (buffer, &iter, "وقد بدأ ثلاث من أكثر المؤسسات تقدما في شبكة اكسيون برامجها كمنظمات لا تسعى للربح، ثم تحولت في السنوات الخمس الماضية إلى مؤسسات مالية منظمة، وباتت جزءا من النظام المالي في بلدانها، ولكنها تتخصص في خدمة قطاع المشروعات الصغيرة. وأحد أكثر هذه المؤسسات نجاحا هو »بانكوسول« في بوليفيا.\n", -1);
567 gtk_text_buffer_get_iter_at_mark (buffer, &iter2, "tmp_mark");
568 gtk_text_buffer_apply_tag (buffer, "rtl_quote", &iter2, &iter);
573 gdk_pixmap_unref(pixmap);
575 gdk_bitmap_unref(mask);
577 printf("%d lines %d chars\n",
578 gtk_text_buffer_get_line_count(buffer),
579 gtk_text_buffer_get_char_count(buffer));
581 gtk_text_buffer_set_modified (buffer, FALSE);
585 fill_file_buffer (GtkTextBuffer *buffer, const char *filename)
590 GtkTextIter iter, end;
592 f = fopen(filename, "r");
596 gchar *err = g_strdup_printf ("Cannot open file '%s': %s",
597 filename, g_strerror (errno));
598 msgbox_run (NULL, err, "OK", NULL, NULL, 0);
603 gtk_text_buffer_get_iter_at_char(buffer, &iter, 0);
607 char *leftover, *next;
609 int to_read = 2047 - remaining;
611 count = fread (buf + remaining, 1, to_read, f);
612 buf[count + remaining] = '\0';
614 leftover = next = buf;
621 next = unicode_get_utf8 (next, &wc);
624 gtk_text_buffer_insert (buffer, &iter, buf, leftover - buf);
626 remaining = buf + remaining + count - leftover;
627 g_memmove (buf, leftover, remaining);
629 if (remaining > 6 || count < to_read)
635 gchar *err = g_strdup_printf ("Invalid UTF-8 data encountered reading file '%s'", filename);
636 msgbox_run (NULL, err, "OK", NULL, NULL, 0);
640 /* We had a newline in the buffer to begin with. (The buffer always contains
641 * a newline, so we delete to the end of the buffer to clean up.
643 gtk_text_buffer_get_last_iter (buffer, &end);
644 gtk_text_buffer_delete (buffer, &iter, &end);
646 gtk_text_buffer_set_modified (buffer, FALSE);
652 delete_event_cb(GtkWidget *window, GdkEventAny *event, gpointer data)
654 View *view = view_from_widget (window);
656 push_active_window (GTK_WINDOW (window));
657 check_close_view (view);
658 pop_active_window ();
668 get_empty_view (View *view)
670 if (!view->buffer->filename &&
671 !gtk_text_buffer_modified (view->buffer->buffer))
674 return create_view (create_buffer ());
678 view_from_widget (GtkWidget *widget)
682 if (GTK_IS_MENU_ITEM (widget))
684 GtkItemFactory *item_factory = gtk_item_factory_from_widget (widget);
685 return gtk_object_get_data (GTK_OBJECT (item_factory), "view");
689 GtkWidget *app = gtk_widget_get_toplevel (widget);
690 return gtk_object_get_data (GTK_OBJECT (app), "view");
695 do_new (gpointer callback_data,
696 guint callback_action,
699 create_view (create_buffer ());
703 do_new_view (gpointer callback_data,
704 guint callback_action,
707 View *view = view_from_widget (widget);
709 create_view (view->buffer);
713 open_ok_func (const char *filename, gpointer data)
716 View *new_view = get_empty_view (view);
718 if (!fill_file_buffer (new_view->buffer->buffer, filename))
720 if (new_view != view)
721 close_view (new_view);
726 g_free (new_view->buffer->filename);
727 new_view->buffer->filename = g_strdup (filename);
728 buffer_filename_set (new_view->buffer);
735 do_open (gpointer callback_data,
736 guint callback_action,
739 View *view = view_from_widget (widget);
741 push_active_window (GTK_WINDOW (view->window));
742 filesel_run (NULL, "Open File", NULL, open_ok_func, view);
743 pop_active_window ();
747 do_save_as (gpointer callback_data,
748 guint callback_action,
751 View *view = view_from_widget (widget);
753 push_active_window (GTK_WINDOW (view->window));
754 save_as_buffer (view->buffer);
755 pop_active_window ();
759 do_save (gpointer callback_data,
760 guint callback_action,
763 View *view = view_from_widget (widget);
765 push_active_window (GTK_WINDOW (view->window));
766 if (!view->buffer->filename)
767 do_save_as (callback_data, callback_action, widget);
769 save_buffer (view->buffer);
770 pop_active_window ();
774 do_close (gpointer callback_data,
775 guint callback_action,
778 View *view = view_from_widget (widget);
780 push_active_window (GTK_WINDOW (view->window));
781 check_close_view (view);
782 pop_active_window ();
786 do_exit (gpointer callback_data,
787 guint callback_action,
790 View *view = view_from_widget (widget);
792 GSList *tmp_list = buffers;
794 push_active_window (GTK_WINDOW (view->window));
797 if (!check_buffer_saved (tmp_list->data))
800 tmp_list = tmp_list->next;
804 pop_active_window ();
808 do_example (gpointer callback_data,
809 guint callback_action,
812 View *view = view_from_widget (widget);
815 new_view = get_empty_view (view);
817 fill_example_buffer (new_view->buffer->buffer);
821 do_wrap_changed (gpointer callback_data,
822 guint callback_action,
825 View *view = view_from_widget (widget);
827 gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (view->text_view), callback_action);
831 do_direction_changed (gpointer callback_data,
832 guint callback_action,
835 View *view = view_from_widget (widget);
837 gtk_widget_set_direction (view->text_view, callback_action);
838 gtk_widget_queue_resize (view->text_view);
842 view_init_menus (View *view)
844 GtkTextDirection direction = gtk_widget_get_direction (view->text_view);
845 GtkWrapMode wrap_mode = gtk_text_view_get_wrap_mode (GTK_TEXT_VIEW (view->text_view));
846 GtkWidget *menu_item = NULL;
850 case GTK_TEXT_DIR_LTR:
851 menu_item = gtk_item_factory_get_widget (view->item_factory, "/Settings/Left-to-Right");
853 case GTK_TEXT_DIR_RTL:
854 menu_item = gtk_item_factory_get_widget (view->item_factory, "/Settings/Right-to-Left");
861 gtk_menu_item_activate (GTK_MENU_ITEM (menu_item));
865 case GTK_WRAPMODE_NONE:
866 menu_item = gtk_item_factory_get_widget (view->item_factory, "/Settings/Wrap Off");
868 case GTK_WRAPMODE_WORD:
869 menu_item = gtk_item_factory_get_widget (view->item_factory, "/Settings/Wrap Words");
876 gtk_menu_item_activate (GTK_MENU_ITEM (menu_item));
879 static GtkItemFactoryEntry menu_items[] =
881 { "/_File", NULL, 0, 0, "<Branch>" },
882 { "/File/_New", "<control>N", do_new, 0, NULL },
883 { "/File/New _View", NULL, do_new_view, 0, NULL },
884 { "/File/_Open", "<control>O", do_open, 0, NULL },
885 { "/File/_Save", "<control>S", do_save, 0, NULL },
886 { "/File/Save _As...", NULL, do_save_as, 0, NULL },
887 { "/File/sep1", NULL, 0, 0, "<Separator>" },
888 { "/File/_Close", "<control>W" , do_close, 0, NULL },
889 { "/File/E_xit", "<control>Q" , do_exit, 0, NULL },
891 { "/_Settings", NULL, 0, 0, "<Branch>" },
892 { "/Settings/Wrap _Off", NULL, do_wrap_changed, GTK_WRAPMODE_NONE, "<RadioItem>" },
893 { "/Settings/Wrap _Words", NULL, do_wrap_changed, GTK_WRAPMODE_WORD, "/Settings/Wrap Off" },
894 { "/Settings/sep1", NULL, 0, 0, "<Separator>" },
895 { "/Settings/Left-to-Right", NULL, do_direction_changed, GTK_TEXT_DIR_LTR, "<RadioItem>" },
896 { "/Settings/Right-to-Left", NULL, do_direction_changed, GTK_TEXT_DIR_RTL, "/Settings/Left-to-Right" },
898 { "/_Test", NULL, 0, 0, "<Branch>" },
899 { "/Test/_Example", NULL, do_example, 0, NULL },
903 save_buffer (Buffer *buffer)
905 GtkTextIter start, end;
907 gboolean result = FALSE;
908 gboolean have_backup = FALSE;
912 g_return_val_if_fail (buffer->filename != NULL, FALSE);
914 bak_filename = g_strconcat (buffer->filename, "~", NULL);
916 if (rename (buffer->filename, bak_filename) != 0)
920 gchar *err = g_strdup_printf ("Cannot back up '%s' to '%s': %s",
921 buffer->filename, bak_filename, g_strerror (errno));
922 msgbox_run (NULL, err, "OK", NULL, NULL, 0);
931 file = fopen (buffer->filename, "w");
934 gchar *err = g_strdup_printf ("Cannot back up '%s' to '%s': %s",
935 buffer->filename, bak_filename, g_strerror (errno));
936 msgbox_run (NULL, err, "OK", NULL, NULL, 0);
940 gtk_text_buffer_get_iter_at_char (buffer->buffer, &start, 0);
941 gtk_text_buffer_get_last_iter (buffer->buffer, &end);
943 chars = gtk_text_buffer_get_slice (buffer->buffer, &start, &end, FALSE);
945 if (fputs (chars, file) == EOF ||
946 fclose (file) == EOF)
948 gchar *err = g_strdup_printf ("Error writing to '%s': %s",
949 buffer->filename, g_strerror (errno));
950 msgbox_run (NULL, err, "OK", NULL, NULL, 0);
958 gtk_text_buffer_set_modified (buffer->buffer, FALSE);
964 if (!result && have_backup)
966 if (rename (bak_filename, buffer->filename) != 0)
968 gchar *err = g_strdup_printf ("Error restoring backup file '%s' to '%s': %s\nBackup left as '%s'",
969 buffer->filename, bak_filename, g_strerror (errno), bak_filename);
970 msgbox_run (NULL, err, "OK", NULL, NULL, 0);
975 g_free (bak_filename);
981 save_as_ok_func (const char *filename, gpointer data)
983 Buffer *buffer = data;
984 char *old_filename = buffer->filename;
986 if (!buffer->filename || strcmp (filename, buffer->filename) != 0)
990 if (stat(filename, &statbuf) == 0)
992 gchar *err = g_strdup_printf ("Ovewrite existing file '%s'?", filename);
993 gint result = msgbox_run (NULL, err, "Yes", "No", NULL, 1);
1001 buffer->filename = g_strdup (filename);
1003 if (save_buffer (buffer))
1005 g_free (old_filename);
1006 buffer_filename_set (buffer);
1011 g_free (buffer->filename);
1012 buffer->filename = old_filename;
1018 save_as_buffer (Buffer *buffer)
1020 return filesel_run (NULL, "Save File", NULL, save_as_ok_func, buffer);
1024 check_buffer_saved (Buffer *buffer)
1026 if (gtk_text_buffer_modified (buffer->buffer))
1028 char *pretty_name = buffer_pretty_name (buffer);
1029 char *msg = g_strdup_printf ("Save changes to '%s'?", pretty_name);
1032 g_free (pretty_name);
1034 result = msgbox_run (NULL, msg, "Yes", "No", "Cancel", 0);
1038 return save_as_buffer (buffer);
1039 else if (result == 1)
1049 create_buffer (void)
1053 buffer = g_new (Buffer, 1);
1055 buffer->buffer = gtk_text_buffer_new (NULL);
1056 gtk_object_ref (GTK_OBJECT (buffer->buffer));
1057 gtk_object_sink (GTK_OBJECT (buffer->buffer));
1059 buffer->refcount = 1;
1060 buffer->filename = NULL;
1061 buffer->untitled_serial = -1;
1063 buffers = g_slist_prepend (buffers, buffer);
1069 buffer_pretty_name (Buffer *buffer)
1071 if (buffer->filename)
1074 char *result = g_strdup (g_basename (buffer->filename));
1075 p = strchr (result, '/');
1083 if (buffer->untitled_serial == -1)
1084 buffer->untitled_serial = untitled_serial++;
1086 if (buffer->untitled_serial == 1)
1087 return g_strdup ("Untitled");
1089 return g_strdup_printf ("Untitled #%d", buffer->untitled_serial);
1094 buffer_filename_set (Buffer *buffer)
1096 GSList *tmp_list = views;
1100 View *view = tmp_list->data;
1102 if (view->buffer == buffer)
1103 view_set_title (view);
1105 tmp_list = tmp_list->next;
1110 buffer_ref (Buffer *buffer)
1116 buffer_unref (Buffer *buffer)
1119 if (buffer->refcount == 0)
1121 buffers = g_slist_remove (buffers, buffer);
1122 gtk_object_unref (GTK_OBJECT (buffer->buffer));
1123 g_free (buffer->filename);
1129 close_view (View *view)
1131 views = g_slist_remove (views, view);
1132 buffer_unref (view->buffer);
1133 gtk_widget_destroy (view->window);
1134 g_object_unref (G_OBJECT (view->item_factory));
1143 check_close_view (View *view)
1145 if (view->buffer->refcount > 1 ||
1146 check_buffer_saved (view->buffer))
1151 view_set_title (View *view)
1153 char *pretty_name = buffer_pretty_name (view->buffer);
1154 char *title = g_strconcat ("testtext - ", pretty_name, NULL);
1156 gtk_window_set_title (GTK_WINDOW (view->window), title);
1158 g_free (pretty_name);
1163 create_view (Buffer *buffer)
1170 view = g_new0 (View, 1);
1171 views = g_slist_prepend (views, view);
1173 view->buffer = buffer;
1174 buffer_ref (buffer);
1176 view->window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
1177 gtk_object_set_data (GTK_OBJECT (view->window), "view", view);
1179 gtk_signal_connect (GTK_OBJECT (view->window), "delete_event",
1180 GTK_SIGNAL_FUNC(delete_event_cb), NULL);
1182 view->accel_group = gtk_accel_group_new ();
1183 view->item_factory = gtk_item_factory_new (GTK_TYPE_MENU_BAR, "<main>", view->accel_group);
1184 gtk_object_set_data (GTK_OBJECT (view->item_factory), "view", view);
1186 gtk_item_factory_create_items (view->item_factory, G_N_ELEMENTS (menu_items), menu_items, view);
1188 gtk_window_add_accel_group (GTK_WINDOW (view->window), view->accel_group);
1190 vbox = gtk_vbox_new (FALSE, 0);
1191 gtk_container_add (GTK_CONTAINER (view->window), vbox);
1193 gtk_box_pack_start (GTK_BOX (vbox),
1194 gtk_item_factory_get_widget (view->item_factory, "<main>"),
1197 sw = gtk_scrolled_window_new(NULL, NULL);
1198 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
1199 GTK_POLICY_AUTOMATIC,
1200 GTK_POLICY_AUTOMATIC);
1202 view->text_view = gtk_text_view_new_with_buffer (buffer->buffer);
1203 gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (view->text_view), GTK_WRAPMODE_WORD);
1205 gtk_box_pack_start (GTK_BOX (vbox), sw, TRUE, TRUE, 0);
1206 gtk_container_add(GTK_CONTAINER(sw), view->text_view);
1208 gtk_window_set_default_size (GTK_WINDOW (view->window), 500, 500);
1210 gtk_widget_grab_focus(view->text_view);
1212 view_set_title (view);
1213 view_init_menus (view);
1215 gtk_widget_show_all (view->window);
1220 main(int argc, char** argv)
1226 gtk_init(&argc, &argv);
1228 buffer = create_buffer ();
1229 view = create_view (buffer);
1230 buffer_unref (buffer);
1232 push_active_window (GTK_WINDOW (view->window));
1233 for (i=1; i < argc; i++)
1237 /* Quick and dirty canonicalization - better should be in GLib
1240 if (!g_path_is_absolute (argv[i]))
1242 char *cwd = g_get_current_dir ();
1243 filename = g_strconcat (cwd, "/", argv[i], NULL);
1249 open_ok_func (filename, view);
1251 if (filename != argv[i])
1254 pop_active_window ();