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;
29 GtkAccelGroup *accel_group;
30 GtkItemFactory *item_factory;
34 static void push_active_window (GtkWindow *window);
35 static void pop_active_window (void);
36 static GtkWindow *get_active_window (void);
38 static Buffer * create_buffer (void);
39 static gboolean check_buffer_saved (Buffer *buffer);
40 static gboolean save_buffer (Buffer *buffer);
41 static gboolean save_as_buffer (Buffer *buffer);
42 static char * buffer_pretty_name (Buffer *buffer);
43 static void buffer_filename_set (Buffer *buffer);
45 static View *view_from_widget (GtkWidget *widget);
47 static View *create_view (Buffer *buffer);
48 static void check_close_view (View *view);
49 static void close_view (View *view);
50 static void view_set_title (View *view);
51 static void view_init_menus (View *view);
53 GSList *buffers = NULL;
57 push_active_window (GtkWindow *window)
59 gtk_object_ref (GTK_OBJECT (window));
60 active_window_stack = g_slist_prepend (active_window_stack, window);
64 pop_active_window (void)
66 gtk_object_unref (active_window_stack->data);
67 active_window_stack = g_slist_delete_link (active_window_stack, active_window_stack);
71 get_active_window (void)
73 if (active_window_stack)
74 return active_window_stack->data;
80 * Filesel utility function
83 typedef gboolean (*FileselOKFunc) (const char *filename, gpointer data);
86 filesel_ok_cb (GtkWidget *button, GtkWidget *filesel)
88 FileselOKFunc ok_func = gtk_object_get_data (GTK_OBJECT (filesel), "ok-func");
89 gpointer data = gtk_object_get_data (GTK_OBJECT (filesel), "ok-data");
90 gint *result = gtk_object_get_data (GTK_OBJECT (filesel), "ok-result");
92 gtk_widget_hide (filesel);
94 if ((*ok_func) (gtk_file_selection_get_filename (GTK_FILE_SELECTION (filesel)), data))
96 gtk_widget_destroy (filesel);
100 gtk_widget_show (filesel);
104 filesel_run (GtkWindow *parent,
106 const char *start_file,
110 GtkWidget *filesel = gtk_file_selection_new (title);
111 gboolean result = FALSE;
114 parent = get_active_window ();
117 gtk_window_set_transient_for (GTK_WINDOW (filesel), parent);
120 gtk_file_selection_set_filename (GTK_FILE_SELECTION (filesel), start_file);
123 gtk_object_set_data (GTK_OBJECT (filesel), "ok-func", func);
124 gtk_object_set_data (GTK_OBJECT (filesel), "ok-data", data);
125 gtk_object_set_data (GTK_OBJECT (filesel), "ok-result", &result);
127 gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION (filesel)->ok_button),
129 GTK_SIGNAL_FUNC (filesel_ok_cb), filesel);
130 gtk_signal_connect_object (GTK_OBJECT (GTK_FILE_SELECTION (filesel)->cancel_button),
132 GTK_SIGNAL_FUNC (gtk_widget_destroy), GTK_OBJECT (filesel));
134 gtk_signal_connect (GTK_OBJECT (filesel), "destroy",
135 GTK_SIGNAL_FUNC (gtk_main_quit), NULL);
136 gtk_window_set_modal (GTK_WINDOW (filesel), TRUE);
138 gtk_widget_show (filesel);
145 * MsgBox utility functions
149 msgbox_yes_cb (GtkWidget *widget, gboolean *result)
152 gtk_object_destroy (GTK_OBJECT (gtk_widget_get_toplevel (widget)));
156 msgbox_no_cb (GtkWidget *widget, gboolean *result)
159 gtk_object_destroy (GTK_OBJECT (gtk_widget_get_toplevel (widget)));
163 msgbox_key_press_cb (GtkWidget *widget, GdkEventKey *event, gpointer data)
165 if (event->keyval == GDK_Escape)
167 gtk_signal_emit_stop_by_name (GTK_OBJECT (widget), "key_press_event");
168 gtk_object_destroy (GTK_OBJECT (widget));
176 msgbox_run (GtkWindow *parent,
178 const char *yes_button,
179 const char *no_button,
180 const char *cancel_button,
183 gboolean result = -1;
188 GtkWidget *button_box;
189 GtkWidget *separator;
191 g_return_val_if_fail (message != NULL, FALSE);
192 g_return_val_if_fail (default_index >= 0 && default_index <= 1, FALSE);
195 parent = get_active_window ();
199 dialog = gtk_window_new (GTK_WINDOW_TOPLEVEL);
200 gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
202 gtk_window_set_transient_for (GTK_WINDOW (dialog), parent);
203 gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
205 /* Quit our recursive main loop when the dialog is destroyed.
207 gtk_signal_connect (GTK_OBJECT (dialog), "destroy",
208 GTK_SIGNAL_FUNC (gtk_main_quit), NULL);
210 /* Catch Escape key presses and have them destroy the dialog
212 gtk_signal_connect (GTK_OBJECT (dialog), "key_press_event",
213 GTK_SIGNAL_FUNC (msgbox_key_press_cb), NULL);
215 /* Fill in the contents of the widget
217 vbox = gtk_vbox_new (FALSE, 0);
218 gtk_container_add (GTK_CONTAINER (dialog), vbox);
220 label = gtk_label_new (message);
221 gtk_misc_set_padding (GTK_MISC (label), 12, 12);
222 gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
223 gtk_box_pack_start (GTK_BOX (vbox), label, TRUE, TRUE, 0);
225 separator = gtk_hseparator_new ();
226 gtk_box_pack_start (GTK_BOX (vbox), separator, FALSE, FALSE, 0);
228 button_box = gtk_hbutton_box_new ();
229 gtk_box_pack_start (GTK_BOX (vbox), button_box, FALSE, FALSE, 0);
230 gtk_container_set_border_width (GTK_CONTAINER (button_box), 8);
233 /* When Yes is clicked, call the msgbox_yes_cb
234 * This sets the result variable and destroys the dialog
238 button = gtk_button_new_with_label (yes_button);
239 GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
240 gtk_container_add (GTK_CONTAINER (button_box), button);
242 if (default_index == 0)
243 gtk_widget_grab_default (button);
245 gtk_signal_connect (GTK_OBJECT (button), "clicked",
246 GTK_SIGNAL_FUNC (msgbox_yes_cb), &result);
249 /* When No is clicked, call the msgbox_no_cb
250 * This sets the result variable and destroys the dialog
254 button = gtk_button_new_with_label (no_button);
255 GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
256 gtk_container_add (GTK_CONTAINER (button_box), button);
258 if (default_index == 0)
259 gtk_widget_grab_default (button);
261 gtk_signal_connect (GTK_OBJECT (button), "clicked",
262 GTK_SIGNAL_FUNC (msgbox_no_cb), &result);
265 /* When Cancel is clicked, destroy the dialog
269 button = gtk_button_new_with_label (cancel_button);
270 GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
271 gtk_container_add (GTK_CONTAINER (button_box), button);
273 if (default_index == 1)
274 gtk_widget_grab_default (button);
276 gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
277 GTK_SIGNAL_FUNC (gtk_object_destroy), GTK_OBJECT (dialog));
280 gtk_widget_show_all (dialog);
282 /* Run a recursive main loop until a button is clicked
283 * or the user destroys the dialog through the window mananger */
290 * Example buffer filling code
293 blink_timeout(gpointer data)
296 static gboolean flip = FALSE;
298 tag = GTK_TEXT_TAG(data);
300 gtk_object_set(GTK_OBJECT(tag),
301 "foreground", flip ? "blue" : "purple",
310 tag_event_handler(GtkTextTag *tag, GtkWidget *widget, GdkEvent *event,
311 const GtkTextIter *iter, gpointer user_data)
315 char_index = gtk_text_iter_get_char_index(iter);
319 case GDK_MOTION_NOTIFY:
320 printf("Motion event at char %d tag `%s'\n",
321 char_index, tag->name);
324 case GDK_BUTTON_PRESS:
325 printf("Button press at char %d tag `%s'\n",
326 char_index, tag->name);
329 case GDK_2BUTTON_PRESS:
330 printf("Double click at char %d tag `%s'\n",
331 char_index, tag->name);
334 case GDK_3BUTTON_PRESS:
335 printf("Triple click at char %d tag `%s'\n",
336 char_index, tag->name);
339 case GDK_BUTTON_RELEASE:
340 printf("Button release at char %d tag `%s'\n",
341 char_index, tag->name);
345 case GDK_KEY_RELEASE:
346 case GDK_ENTER_NOTIFY:
347 case GDK_LEAVE_NOTIFY:
348 case GDK_PROPERTY_NOTIFY:
349 case GDK_SELECTION_CLEAR:
350 case GDK_SELECTION_REQUEST:
351 case GDK_SELECTION_NOTIFY:
352 case GDK_PROXIMITY_IN:
353 case GDK_PROXIMITY_OUT:
356 case GDK_DRAG_MOTION:
357 case GDK_DRAG_STATUS:
359 case GDK_DROP_FINISHED:
368 setup_tag(GtkTextTag *tag)
371 gtk_signal_connect(GTK_OBJECT(tag),
373 GTK_SIGNAL_FUNC(tag_event_handler),
377 static char *book_closed_xpm[] = {
405 fill_example_buffer (GtkTextBuffer *buffer)
407 GtkTextIter iter, iter2;
416 tag = gtk_text_buffer_create_tag(buffer, "fg_blue");
418 /* gtk_timeout_add(1000, blink_timeout, tag); */
422 color.red = color.green = 0;
427 gtk_object_set(GTK_OBJECT(tag),
428 "foreground_gdk", &color,
429 "background_gdk", &color2,
433 tag = gtk_text_buffer_create_tag(buffer, "fg_red");
437 color.blue = color.green = 0;
439 gtk_object_set(GTK_OBJECT(tag),
441 "foreground_gdk", &color,
444 tag = gtk_text_buffer_create_tag(buffer, "bg_green");
448 color.blue = color.red = 0;
449 color.green = 0xffff;
450 gtk_object_set(GTK_OBJECT(tag),
451 "background_gdk", &color,
455 tag = gtk_text_buffer_create_tag(buffer, "overstrike");
459 gtk_object_set(GTK_OBJECT(tag),
464 tag = gtk_text_buffer_create_tag(buffer, "underline");
468 gtk_object_set(GTK_OBJECT(tag),
469 "underline", PANGO_UNDERLINE_SINGLE,
474 gtk_object_set(GTK_OBJECT(tag),
475 "underline", PANGO_UNDERLINE_SINGLE,
478 tag = gtk_text_buffer_create_tag(buffer, "centered");
480 gtk_object_set(GTK_OBJECT(tag),
481 "justify", GTK_JUSTIFY_CENTER,
484 tag = gtk_text_buffer_create_tag(buffer, "rtl_quote");
486 gtk_object_set(GTK_OBJECT(tag),
487 "wrap_mode", GTK_WRAPMODE_WORD,
488 "direction", GTK_TEXT_DIR_RTL,
489 "left_wrapped_line_margin", 20,
494 pixmap = gdk_pixmap_colormap_create_from_xpm_d (NULL,
495 gtk_widget_get_default_colormap(),
497 NULL, book_closed_xpm);
499 g_assert(pixmap != NULL);
504 GtkTextMark * temp_mark;
506 gtk_text_buffer_get_iter_at_char(buffer, &iter, 0);
508 gtk_text_buffer_insert_pixmap (buffer, &iter, pixmap, mask);
510 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",
513 gtk_text_buffer_insert(buffer, &iter, str, -1);
517 gtk_text_buffer_get_iter_at_line_char(buffer, &iter, 0, 5);
519 gtk_text_buffer_insert(buffer, &iter,
520 "(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"
521 /* This is UTF8 stuff, Emacs doesn't
522 really know how to display it */
523 "German (Deutsch Süd) Grüß Gott Greek (Ελληνικά) Γειά σας Hebrew שלום Japanese (日本語)\n", -1);
526 gtk_text_buffer_create_mark (buffer, "tmp_mark", &iter, TRUE);
529 gtk_text_buffer_get_iter_at_line_char(buffer, &iter, 0, 6);
530 gtk_text_buffer_get_iter_at_line_char(buffer, &iter2, 0, 13);
532 gtk_text_buffer_apply_tag(buffer, "fg_blue", &iter, &iter2);
534 gtk_text_buffer_get_iter_at_line_char(buffer, &iter, 1, 10);
535 gtk_text_buffer_get_iter_at_line_char(buffer, &iter2, 1, 16);
537 gtk_text_buffer_apply_tag(buffer, "underline", &iter, &iter2);
539 gtk_text_buffer_get_iter_at_line_char(buffer, &iter, 1, 14);
540 gtk_text_buffer_get_iter_at_line_char(buffer, &iter2, 1, 24);
542 gtk_text_buffer_apply_tag(buffer, "overstrike", &iter, &iter2);
544 gtk_text_buffer_get_iter_at_line_char(buffer, &iter, 0, 9);
545 gtk_text_buffer_get_iter_at_line_char(buffer, &iter2, 0, 16);
547 gtk_text_buffer_apply_tag(buffer, "bg_green", &iter, &iter2);
549 gtk_text_buffer_get_iter_at_line_char(buffer, &iter, 4, 2);
550 gtk_text_buffer_get_iter_at_line_char(buffer, &iter2, 4, 10);
552 gtk_text_buffer_apply_tag(buffer, "bg_green", &iter, &iter2);
554 gtk_text_buffer_get_iter_at_line_char(buffer, &iter, 4, 8);
555 gtk_text_buffer_get_iter_at_line_char(buffer, &iter2, 4, 15);
557 gtk_text_buffer_apply_tag(buffer, "fg_red", &iter, &iter2);
560 gtk_text_buffer_get_iter_at_mark (buffer, &iter, temp_mark);
561 gtk_text_buffer_insert (buffer, &iter, "Centered text!\n", -1);
563 gtk_text_buffer_get_iter_at_mark (buffer, &iter2, temp_mark);
564 gtk_text_buffer_apply_tag (buffer, "centered", &iter2, &iter);
566 gtk_text_buffer_move_mark (buffer, temp_mark, &iter);
567 gtk_text_buffer_insert (buffer, &iter, "Word wrapped, Right-to-left Quote\n", -1);
568 gtk_text_buffer_insert (buffer, &iter, "وقد بدأ ثلاث من أكثر المؤسسات تقدما في شبكة اكسيون برامجها كمنظمات لا تسعى للربح، ثم تحولت في السنوات الخمس الماضية إلى مؤسسات مالية منظمة، وباتت جزءا من النظام المالي في بلدانها، ولكنها تتخصص في خدمة قطاع المشروعات الصغيرة. وأحد أكثر هذه المؤسسات نجاحا هو »بانكوسول« في بوليفيا.\n", -1);
569 gtk_text_buffer_get_iter_at_mark (buffer, &iter2, temp_mark);
570 gtk_text_buffer_apply_tag (buffer, "rtl_quote", &iter2, &iter);
575 gdk_pixmap_unref(pixmap);
577 gdk_bitmap_unref(mask);
579 printf("%d lines %d chars\n",
580 gtk_text_buffer_get_line_count(buffer),
581 gtk_text_buffer_get_char_count(buffer));
583 gtk_text_buffer_set_modified (buffer, FALSE);
587 fill_file_buffer (GtkTextBuffer *buffer, const char *filename)
592 GtkTextIter iter, end;
594 f = fopen(filename, "r");
598 gchar *err = g_strdup_printf ("Cannot open file '%s': %s",
599 filename, g_strerror (errno));
600 msgbox_run (NULL, err, "OK", NULL, NULL, 0);
605 gtk_text_buffer_get_iter_at_char(buffer, &iter, 0);
609 char *leftover, *next;
610 int to_read = 2047 - remaining;
612 count = fread (buf + remaining, 1, to_read, f);
613 buf[count + remaining] = '\0';
615 leftover = next = buf;
622 next = g_utf8_next_char (next);
625 gtk_text_buffer_insert (buffer, &iter, buf, leftover - buf);
627 remaining = buf + remaining + count - leftover;
628 g_memmove (buf, leftover, remaining);
630 if (remaining > 6 || count < to_read)
636 gchar *err = g_strdup_printf ("Invalid UTF-8 data encountered reading file '%s'", filename);
637 msgbox_run (NULL, err, "OK", NULL, NULL, 0);
641 /* We had a newline in the buffer to begin with. (The buffer always contains
642 * a newline, so we delete to the end of the buffer to clean up.
644 gtk_text_buffer_get_last_iter (buffer, &end);
645 gtk_text_buffer_delete (buffer, &iter, &end);
647 gtk_text_buffer_set_modified (buffer, FALSE);
653 delete_event_cb(GtkWidget *window, GdkEventAny *event, gpointer data)
655 View *view = view_from_widget (window);
657 push_active_window (GTK_WINDOW (window));
658 check_close_view (view);
659 pop_active_window ();
669 get_empty_view (View *view)
671 if (!view->buffer->filename &&
672 !gtk_text_buffer_modified (view->buffer->buffer))
675 return create_view (create_buffer ());
679 view_from_widget (GtkWidget *widget)
683 if (GTK_IS_MENU_ITEM (widget))
685 GtkItemFactory *item_factory = gtk_item_factory_from_widget (widget);
686 return gtk_object_get_data (GTK_OBJECT (item_factory), "view");
690 GtkWidget *app = gtk_widget_get_toplevel (widget);
691 return gtk_object_get_data (GTK_OBJECT (app), "view");
696 do_new (gpointer callback_data,
697 guint callback_action,
700 create_view (create_buffer ());
704 do_new_view (gpointer callback_data,
705 guint callback_action,
708 View *view = view_from_widget (widget);
710 create_view (view->buffer);
714 open_ok_func (const char *filename, gpointer data)
717 View *new_view = get_empty_view (view);
719 if (!fill_file_buffer (new_view->buffer->buffer, filename))
721 if (new_view != view)
722 close_view (new_view);
727 g_free (new_view->buffer->filename);
728 new_view->buffer->filename = g_strdup (filename);
729 buffer_filename_set (new_view->buffer);
736 do_open (gpointer callback_data,
737 guint callback_action,
740 View *view = view_from_widget (widget);
742 push_active_window (GTK_WINDOW (view->window));
743 filesel_run (NULL, "Open File", NULL, open_ok_func, view);
744 pop_active_window ();
748 do_save_as (gpointer callback_data,
749 guint callback_action,
752 View *view = view_from_widget (widget);
754 push_active_window (GTK_WINDOW (view->window));
755 save_as_buffer (view->buffer);
756 pop_active_window ();
760 do_save (gpointer callback_data,
761 guint callback_action,
764 View *view = view_from_widget (widget);
766 push_active_window (GTK_WINDOW (view->window));
767 if (!view->buffer->filename)
768 do_save_as (callback_data, callback_action, widget);
770 save_buffer (view->buffer);
771 pop_active_window ();
775 do_close (gpointer callback_data,
776 guint callback_action,
779 View *view = view_from_widget (widget);
781 push_active_window (GTK_WINDOW (view->window));
782 check_close_view (view);
783 pop_active_window ();
787 do_exit (gpointer callback_data,
788 guint callback_action,
791 View *view = view_from_widget (widget);
793 GSList *tmp_list = buffers;
795 push_active_window (GTK_WINDOW (view->window));
798 if (!check_buffer_saved (tmp_list->data))
801 tmp_list = tmp_list->next;
805 pop_active_window ();
809 do_example (gpointer callback_data,
810 guint callback_action,
813 View *view = view_from_widget (widget);
816 new_view = get_empty_view (view);
818 fill_example_buffer (new_view->buffer->buffer);
822 do_wrap_changed (gpointer callback_data,
823 guint callback_action,
826 View *view = view_from_widget (widget);
828 gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (view->text_view), callback_action);
832 do_direction_changed (gpointer callback_data,
833 guint callback_action,
836 View *view = view_from_widget (widget);
838 gtk_widget_set_direction (view->text_view, callback_action);
839 gtk_widget_queue_resize (view->text_view);
843 view_init_menus (View *view)
845 GtkTextDirection direction = gtk_widget_get_direction (view->text_view);
846 GtkWrapMode wrap_mode = gtk_text_view_get_wrap_mode (GTK_TEXT_VIEW (view->text_view));
847 GtkWidget *menu_item = NULL;
851 case GTK_TEXT_DIR_LTR:
852 menu_item = gtk_item_factory_get_widget (view->item_factory, "/Settings/Left-to-Right");
854 case GTK_TEXT_DIR_RTL:
855 menu_item = gtk_item_factory_get_widget (view->item_factory, "/Settings/Right-to-Left");
862 gtk_menu_item_activate (GTK_MENU_ITEM (menu_item));
866 case GTK_WRAPMODE_NONE:
867 menu_item = gtk_item_factory_get_widget (view->item_factory, "/Settings/Wrap Off");
869 case GTK_WRAPMODE_WORD:
870 menu_item = gtk_item_factory_get_widget (view->item_factory, "/Settings/Wrap Words");
877 gtk_menu_item_activate (GTK_MENU_ITEM (menu_item));
880 static GtkItemFactoryEntry menu_items[] =
882 { "/_File", NULL, 0, 0, "<Branch>" },
883 { "/File/_New", "<control>N", do_new, 0, NULL },
884 { "/File/New _View", NULL, do_new_view, 0, NULL },
885 { "/File/_Open", "<control>O", do_open, 0, NULL },
886 { "/File/_Save", "<control>S", do_save, 0, NULL },
887 { "/File/Save _As...", NULL, do_save_as, 0, NULL },
888 { "/File/sep1", NULL, 0, 0, "<Separator>" },
889 { "/File/_Close", "<control>W" , do_close, 0, NULL },
890 { "/File/E_xit", "<control>Q" , do_exit, 0, NULL },
892 { "/_Settings", NULL, 0, 0, "<Branch>" },
893 { "/Settings/Wrap _Off", NULL, do_wrap_changed, GTK_WRAPMODE_NONE, "<RadioItem>" },
894 { "/Settings/Wrap _Words", NULL, do_wrap_changed, GTK_WRAPMODE_WORD, "/Settings/Wrap Off" },
895 { "/Settings/sep1", NULL, 0, 0, "<Separator>" },
896 { "/Settings/Left-to-Right", NULL, do_direction_changed, GTK_TEXT_DIR_LTR, "<RadioItem>" },
897 { "/Settings/Right-to-Left", NULL, do_direction_changed, GTK_TEXT_DIR_RTL, "/Settings/Left-to-Right" },
899 { "/_Test", NULL, 0, 0, "<Branch>" },
900 { "/Test/_Example", NULL, do_example, 0, NULL },
904 save_buffer (Buffer *buffer)
906 GtkTextIter start, end;
908 gboolean result = FALSE;
909 gboolean have_backup = FALSE;
913 g_return_val_if_fail (buffer->filename != NULL, FALSE);
915 bak_filename = g_strconcat (buffer->filename, "~", NULL);
917 if (rename (buffer->filename, bak_filename) != 0)
921 gchar *err = g_strdup_printf ("Cannot back up '%s' to '%s': %s",
922 buffer->filename, bak_filename, g_strerror (errno));
923 msgbox_run (NULL, err, "OK", NULL, NULL, 0);
932 file = fopen (buffer->filename, "w");
935 gchar *err = g_strdup_printf ("Cannot back up '%s' to '%s': %s",
936 buffer->filename, bak_filename, g_strerror (errno));
937 msgbox_run (NULL, err, "OK", NULL, NULL, 0);
941 gtk_text_buffer_get_iter_at_char (buffer->buffer, &start, 0);
942 gtk_text_buffer_get_last_iter (buffer->buffer, &end);
944 chars = gtk_text_buffer_get_slice (buffer->buffer, &start, &end, FALSE);
946 if (fputs (chars, file) == EOF ||
947 fclose (file) == EOF)
949 gchar *err = g_strdup_printf ("Error writing to '%s': %s",
950 buffer->filename, g_strerror (errno));
951 msgbox_run (NULL, err, "OK", NULL, NULL, 0);
959 gtk_text_buffer_set_modified (buffer->buffer, FALSE);
965 if (!result && have_backup)
967 if (rename (bak_filename, buffer->filename) != 0)
969 gchar *err = g_strdup_printf ("Error restoring backup file '%s' to '%s': %s\nBackup left as '%s'",
970 buffer->filename, bak_filename, g_strerror (errno), bak_filename);
971 msgbox_run (NULL, err, "OK", NULL, NULL, 0);
976 g_free (bak_filename);
982 save_as_ok_func (const char *filename, gpointer data)
984 Buffer *buffer = data;
985 char *old_filename = buffer->filename;
987 if (!buffer->filename || strcmp (filename, buffer->filename) != 0)
991 if (stat(filename, &statbuf) == 0)
993 gchar *err = g_strdup_printf ("Ovewrite existing file '%s'?", filename);
994 gint result = msgbox_run (NULL, err, "Yes", "No", NULL, 1);
1002 buffer->filename = g_strdup (filename);
1004 if (save_buffer (buffer))
1006 g_free (old_filename);
1007 buffer_filename_set (buffer);
1012 g_free (buffer->filename);
1013 buffer->filename = old_filename;
1019 save_as_buffer (Buffer *buffer)
1021 return filesel_run (NULL, "Save File", NULL, save_as_ok_func, buffer);
1025 check_buffer_saved (Buffer *buffer)
1027 if (gtk_text_buffer_modified (buffer->buffer))
1029 char *pretty_name = buffer_pretty_name (buffer);
1030 char *msg = g_strdup_printf ("Save changes to '%s'?", pretty_name);
1033 g_free (pretty_name);
1035 result = msgbox_run (NULL, msg, "Yes", "No", "Cancel", 0);
1039 return save_as_buffer (buffer);
1040 else if (result == 1)
1050 create_buffer (void)
1054 buffer = g_new (Buffer, 1);
1056 buffer->buffer = gtk_text_buffer_new (NULL);
1057 gtk_object_ref (GTK_OBJECT (buffer->buffer));
1058 gtk_object_sink (GTK_OBJECT (buffer->buffer));
1060 buffer->refcount = 1;
1061 buffer->filename = NULL;
1062 buffer->untitled_serial = -1;
1064 buffers = g_slist_prepend (buffers, buffer);
1070 buffer_pretty_name (Buffer *buffer)
1072 if (buffer->filename)
1075 char *result = g_strdup (g_basename (buffer->filename));
1076 p = strchr (result, '/');
1084 if (buffer->untitled_serial == -1)
1085 buffer->untitled_serial = untitled_serial++;
1087 if (buffer->untitled_serial == 1)
1088 return g_strdup ("Untitled");
1090 return g_strdup_printf ("Untitled #%d", buffer->untitled_serial);
1095 buffer_filename_set (Buffer *buffer)
1097 GSList *tmp_list = views;
1101 View *view = tmp_list->data;
1103 if (view->buffer == buffer)
1104 view_set_title (view);
1106 tmp_list = tmp_list->next;
1111 buffer_ref (Buffer *buffer)
1117 buffer_unref (Buffer *buffer)
1120 if (buffer->refcount == 0)
1122 buffers = g_slist_remove (buffers, buffer);
1123 gtk_object_unref (GTK_OBJECT (buffer->buffer));
1124 g_free (buffer->filename);
1130 close_view (View *view)
1132 views = g_slist_remove (views, view);
1133 buffer_unref (view->buffer);
1134 gtk_widget_destroy (view->window);
1135 g_object_unref (G_OBJECT (view->item_factory));
1144 check_close_view (View *view)
1146 if (view->buffer->refcount > 1 ||
1147 check_buffer_saved (view->buffer))
1152 view_set_title (View *view)
1154 char *pretty_name = buffer_pretty_name (view->buffer);
1155 char *title = g_strconcat ("testtext - ", pretty_name, NULL);
1157 gtk_window_set_title (GTK_WINDOW (view->window), title);
1159 g_free (pretty_name);
1164 create_view (Buffer *buffer)
1171 view = g_new0 (View, 1);
1172 views = g_slist_prepend (views, view);
1174 view->buffer = buffer;
1175 buffer_ref (buffer);
1177 view->window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
1178 gtk_object_set_data (GTK_OBJECT (view->window), "view", view);
1180 gtk_signal_connect (GTK_OBJECT (view->window), "delete_event",
1181 GTK_SIGNAL_FUNC(delete_event_cb), NULL);
1183 view->accel_group = gtk_accel_group_new ();
1184 view->item_factory = gtk_item_factory_new (GTK_TYPE_MENU_BAR, "<main>", view->accel_group);
1185 gtk_object_set_data (GTK_OBJECT (view->item_factory), "view", view);
1187 gtk_item_factory_create_items (view->item_factory, G_N_ELEMENTS (menu_items), menu_items, view);
1189 gtk_window_add_accel_group (GTK_WINDOW (view->window), view->accel_group);
1191 vbox = gtk_vbox_new (FALSE, 0);
1192 gtk_container_add (GTK_CONTAINER (view->window), vbox);
1194 gtk_box_pack_start (GTK_BOX (vbox),
1195 gtk_item_factory_get_widget (view->item_factory, "<main>"),
1198 sw = gtk_scrolled_window_new(NULL, NULL);
1199 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(sw),
1200 GTK_POLICY_AUTOMATIC,
1201 GTK_POLICY_AUTOMATIC);
1203 view->text_view = gtk_text_view_new_with_buffer (buffer->buffer);
1204 gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (view->text_view), GTK_WRAPMODE_WORD);
1206 gtk_box_pack_start (GTK_BOX (vbox), sw, TRUE, TRUE, 0);
1207 gtk_container_add(GTK_CONTAINER(sw), view->text_view);
1209 gtk_window_set_default_size (GTK_WINDOW (view->window), 500, 500);
1211 gtk_widget_grab_focus(view->text_view);
1213 view_set_title (view);
1214 view_init_menus (view);
1216 gtk_widget_show_all (view->window);
1221 main(int argc, char** argv)
1227 gtk_init(&argc, &argv);
1229 buffer = create_buffer ();
1230 view = create_view (buffer);
1231 buffer_unref (buffer);
1233 push_active_window (GTK_WINDOW (view->window));
1234 for (i=1; i < argc; i++)
1238 /* Quick and dirty canonicalization - better should be in GLib
1241 if (!g_path_is_absolute (argv[i]))
1243 char *cwd = g_get_current_dir ();
1244 filename = g_strconcat (cwd, "/", argv[i], NULL);
1250 open_ok_func (filename, view);
1252 if (filename != argv[i])
1255 pop_active_window ();