8 #include <gdk/gdkkeysyms.h>
10 typedef struct _Buffer Buffer;
11 typedef struct _View View;
13 static gint untitled_serial = 1;
15 GSList *active_window_stack = NULL;
20 GtkTextBuffer *buffer;
23 GtkTextTag *not_editable_tag;
24 GtkTextTag *found_text_tag;
25 GtkTextTag *custom_tabs_tag;
32 GtkAccelGroup *accel_group;
33 GtkItemFactory *item_factory;
37 static void push_active_window (GtkWindow *window);
38 static void pop_active_window (void);
39 static GtkWindow *get_active_window (void);
41 static Buffer * create_buffer (void);
42 static gboolean check_buffer_saved (Buffer *buffer);
43 static gboolean save_buffer (Buffer *buffer);
44 static gboolean save_as_buffer (Buffer *buffer);
45 static char * buffer_pretty_name (Buffer *buffer);
46 static void buffer_filename_set (Buffer *buffer);
47 static void buffer_search_forward (Buffer *buffer,
50 static void buffer_search_backward (Buffer *buffer,
54 static View *view_from_widget (GtkWidget *widget);
56 static View *create_view (Buffer *buffer);
57 static void check_close_view (View *view);
58 static void close_view (View *view);
59 static void view_set_title (View *view);
60 static void view_init_menus (View *view);
62 GSList *buffers = NULL;
66 push_active_window (GtkWindow *window)
68 gtk_object_ref (GTK_OBJECT (window));
69 active_window_stack = g_slist_prepend (active_window_stack, window);
73 pop_active_window (void)
75 gtk_object_unref (active_window_stack->data);
76 active_window_stack = g_slist_delete_link (active_window_stack, active_window_stack);
80 get_active_window (void)
82 if (active_window_stack)
83 return active_window_stack->data;
89 * Filesel utility function
92 typedef gboolean (*FileselOKFunc) (const char *filename, gpointer data);
95 filesel_ok_cb (GtkWidget *button, GtkWidget *filesel)
97 FileselOKFunc ok_func = gtk_object_get_data (GTK_OBJECT (filesel), "ok-func");
98 gpointer data = gtk_object_get_data (GTK_OBJECT (filesel), "ok-data");
99 gint *result = gtk_object_get_data (GTK_OBJECT (filesel), "ok-result");
101 gtk_widget_hide (filesel);
103 if ((*ok_func) (gtk_file_selection_get_filename (GTK_FILE_SELECTION (filesel)), data))
105 gtk_widget_destroy (filesel);
109 gtk_widget_show (filesel);
113 filesel_run (GtkWindow *parent,
115 const char *start_file,
119 GtkWidget *filesel = gtk_file_selection_new (title);
120 gboolean result = FALSE;
123 parent = get_active_window ();
126 gtk_window_set_transient_for (GTK_WINDOW (filesel), parent);
129 gtk_file_selection_set_filename (GTK_FILE_SELECTION (filesel), start_file);
132 gtk_object_set_data (GTK_OBJECT (filesel), "ok-func", func);
133 gtk_object_set_data (GTK_OBJECT (filesel), "ok-data", data);
134 gtk_object_set_data (GTK_OBJECT (filesel), "ok-result", &result);
136 gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION (filesel)->ok_button),
138 GTK_SIGNAL_FUNC (filesel_ok_cb), filesel);
139 gtk_signal_connect_object (GTK_OBJECT (GTK_FILE_SELECTION (filesel)->cancel_button),
141 GTK_SIGNAL_FUNC (gtk_widget_destroy), GTK_OBJECT (filesel));
143 gtk_signal_connect (GTK_OBJECT (filesel), "destroy",
144 GTK_SIGNAL_FUNC (gtk_main_quit), NULL);
145 gtk_window_set_modal (GTK_WINDOW (filesel), TRUE);
147 gtk_widget_show (filesel);
154 * MsgBox utility functions
158 msgbox_yes_cb (GtkWidget *widget, gboolean *result)
161 gtk_object_destroy (GTK_OBJECT (gtk_widget_get_toplevel (widget)));
165 msgbox_no_cb (GtkWidget *widget, gboolean *result)
168 gtk_object_destroy (GTK_OBJECT (gtk_widget_get_toplevel (widget)));
172 msgbox_key_press_cb (GtkWidget *widget, GdkEventKey *event, gpointer data)
174 if (event->keyval == GDK_Escape)
176 gtk_signal_emit_stop_by_name (GTK_OBJECT (widget), "key_press_event");
177 gtk_object_destroy (GTK_OBJECT (widget));
185 msgbox_run (GtkWindow *parent,
187 const char *yes_button,
188 const char *no_button,
189 const char *cancel_button,
192 gboolean result = -1;
197 GtkWidget *button_box;
198 GtkWidget *separator;
200 g_return_val_if_fail (message != NULL, FALSE);
201 g_return_val_if_fail (default_index >= 0 && default_index <= 1, FALSE);
204 parent = get_active_window ();
208 dialog = gtk_window_new (GTK_WINDOW_TOPLEVEL);
209 gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
211 gtk_window_set_transient_for (GTK_WINDOW (dialog), parent);
212 gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
214 /* Quit our recursive main loop when the dialog is destroyed.
216 gtk_signal_connect (GTK_OBJECT (dialog), "destroy",
217 GTK_SIGNAL_FUNC (gtk_main_quit), NULL);
219 /* Catch Escape key presses and have them destroy the dialog
221 gtk_signal_connect (GTK_OBJECT (dialog), "key_press_event",
222 GTK_SIGNAL_FUNC (msgbox_key_press_cb), NULL);
224 /* Fill in the contents of the widget
226 vbox = gtk_vbox_new (FALSE, 0);
227 gtk_container_add (GTK_CONTAINER (dialog), vbox);
229 label = gtk_label_new (message);
230 gtk_misc_set_padding (GTK_MISC (label), 12, 12);
231 gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
232 gtk_box_pack_start (GTK_BOX (vbox), label, TRUE, TRUE, 0);
234 separator = gtk_hseparator_new ();
235 gtk_box_pack_start (GTK_BOX (vbox), separator, FALSE, FALSE, 0);
237 button_box = gtk_hbutton_box_new ();
238 gtk_box_pack_start (GTK_BOX (vbox), button_box, FALSE, FALSE, 0);
239 gtk_container_set_border_width (GTK_CONTAINER (button_box), 8);
242 /* When Yes is clicked, call the msgbox_yes_cb
243 * This sets the result variable and destroys the dialog
247 button = gtk_button_new_with_label (yes_button);
248 GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
249 gtk_container_add (GTK_CONTAINER (button_box), button);
251 if (default_index == 0)
252 gtk_widget_grab_default (button);
254 gtk_signal_connect (GTK_OBJECT (button), "clicked",
255 GTK_SIGNAL_FUNC (msgbox_yes_cb), &result);
258 /* When No is clicked, call the msgbox_no_cb
259 * This sets the result variable and destroys the dialog
263 button = gtk_button_new_with_label (no_button);
264 GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
265 gtk_container_add (GTK_CONTAINER (button_box), button);
267 if (default_index == 0)
268 gtk_widget_grab_default (button);
270 gtk_signal_connect (GTK_OBJECT (button), "clicked",
271 GTK_SIGNAL_FUNC (msgbox_no_cb), &result);
274 /* When Cancel is clicked, destroy the dialog
278 button = gtk_button_new_with_label (cancel_button);
279 GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
280 gtk_container_add (GTK_CONTAINER (button_box), button);
282 if (default_index == 1)
283 gtk_widget_grab_default (button);
285 gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
286 GTK_SIGNAL_FUNC (gtk_object_destroy), GTK_OBJECT (dialog));
289 gtk_widget_show_all (dialog);
291 /* Run a recursive main loop until a button is clicked
292 * or the user destroys the dialog through the window mananger */
299 * Example buffer filling code
302 blink_timeout (gpointer data)
305 static gboolean flip = FALSE;
307 tag = GTK_TEXT_TAG (data);
309 gtk_object_set (GTK_OBJECT (tag),
310 "foreground", flip ? "blue" : "purple",
319 tag_event_handler (GtkTextTag *tag, GtkWidget *widget, GdkEvent *event,
320 const GtkTextIter *iter, gpointer user_data)
324 char_index = gtk_text_iter_get_offset (iter);
328 case GDK_MOTION_NOTIFY:
329 printf ("Motion event at char %d tag `%s'\n",
330 char_index, tag->name);
333 case GDK_BUTTON_PRESS:
334 printf ("Button press at char %d tag `%s'\n",
335 char_index, tag->name);
338 case GDK_2BUTTON_PRESS:
339 printf ("Double click at char %d tag `%s'\n",
340 char_index, tag->name);
343 case GDK_3BUTTON_PRESS:
344 printf ("Triple click at char %d tag `%s'\n",
345 char_index, tag->name);
348 case GDK_BUTTON_RELEASE:
349 printf ("Button release at char %d tag `%s'\n",
350 char_index, tag->name);
354 case GDK_KEY_RELEASE:
355 printf ("Key event at char %d tag `%s'\n",
356 char_index, tag->name);
359 case GDK_ENTER_NOTIFY:
360 case GDK_LEAVE_NOTIFY:
361 case GDK_PROPERTY_NOTIFY:
362 case GDK_SELECTION_CLEAR:
363 case GDK_SELECTION_REQUEST:
364 case GDK_SELECTION_NOTIFY:
365 case GDK_PROXIMITY_IN:
366 case GDK_PROXIMITY_OUT:
369 case GDK_DRAG_MOTION:
370 case GDK_DRAG_STATUS:
372 case GDK_DROP_FINISHED:
381 setup_tag (GtkTextTag *tag)
384 gtk_signal_connect (GTK_OBJECT (tag),
386 GTK_SIGNAL_FUNC (tag_event_handler),
390 static char *book_closed_xpm[] = {
418 fill_example_buffer (GtkTextBuffer *buffer)
420 GtkTextIter iter, iter2;
428 /* FIXME this is broken if called twice on a buffer, since
429 * we try to create tags a second time.
432 tag = gtk_text_buffer_create_tag (buffer, "fg_blue");
434 /* gtk_timeout_add (1000, blink_timeout, tag); */
438 color.red = color.green = 0;
443 gtk_object_set (GTK_OBJECT (tag),
444 "foreground_gdk", &color,
445 "background_gdk", &color2,
449 tag = gtk_text_buffer_create_tag (buffer, "fg_red");
453 color.blue = color.green = 0;
455 gtk_object_set (GTK_OBJECT (tag),
457 "foreground_gdk", &color,
460 tag = gtk_text_buffer_create_tag (buffer, "bg_green");
464 color.blue = color.red = 0;
465 color.green = 0xffff;
466 gtk_object_set (GTK_OBJECT (tag),
467 "background_gdk", &color,
471 tag = gtk_text_buffer_create_tag (buffer, "strikethrough");
475 gtk_object_set (GTK_OBJECT (tag),
476 "strikethrough", TRUE,
480 tag = gtk_text_buffer_create_tag (buffer, "underline");
484 gtk_object_set (GTK_OBJECT (tag),
485 "underline", PANGO_UNDERLINE_SINGLE,
490 gtk_object_set (GTK_OBJECT (tag),
491 "underline", PANGO_UNDERLINE_SINGLE,
494 tag = gtk_text_buffer_create_tag (buffer, "centered");
496 gtk_object_set (GTK_OBJECT (tag),
497 "justify", GTK_JUSTIFY_CENTER,
500 tag = gtk_text_buffer_create_tag (buffer, "rtl_quote");
502 gtk_object_set (GTK_OBJECT (tag),
503 "wrap_mode", GTK_WRAPMODE_WORD,
504 "direction", GTK_TEXT_DIR_RTL,
505 "left_wrapped_line_margin", 20,
512 pixbuf = gdk_pixbuf_new_from_xpm_data (book_closed_xpm);
517 GtkTextMark * temp_mark;
519 gtk_text_buffer_get_iter_at_offset (buffer, &iter, 0);
521 gtk_text_buffer_insert_pixbuf (buffer, &iter, pixbuf);
523 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",
526 gtk_text_buffer_insert (buffer, &iter, str, -1);
530 gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, 0, 5);
532 gtk_text_buffer_insert (buffer, &iter,
533 "(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"
534 /* This is UTF8 stuff, Emacs doesn't
535 really know how to display it */
536 "German (Deutsch Süd) Grüß Gott Greek (Ελληνικά) Γειά σας Hebrew שלום Japanese (日本語)\n", -1);
539 gtk_text_buffer_create_mark (buffer, "tmp_mark", &iter, TRUE);
542 gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, 0, 6);
543 gtk_text_buffer_get_iter_at_line_offset (buffer, &iter2, 0, 13);
545 gtk_text_buffer_apply_tag_by_name (buffer, "fg_blue", &iter, &iter2);
547 gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, 1, 10);
548 gtk_text_buffer_get_iter_at_line_offset (buffer, &iter2, 1, 16);
550 gtk_text_buffer_apply_tag_by_name (buffer, "underline", &iter, &iter2);
552 gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, 1, 14);
553 gtk_text_buffer_get_iter_at_line_offset (buffer, &iter2, 1, 24);
555 gtk_text_buffer_apply_tag_by_name (buffer, "strikethrough", &iter, &iter2);
557 gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, 0, 9);
558 gtk_text_buffer_get_iter_at_line_offset (buffer, &iter2, 0, 16);
560 gtk_text_buffer_apply_tag_by_name (buffer, "bg_green", &iter, &iter2);
562 gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, 4, 2);
563 gtk_text_buffer_get_iter_at_line_offset (buffer, &iter2, 4, 10);
565 gtk_text_buffer_apply_tag_by_name (buffer, "bg_green", &iter, &iter2);
567 gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, 4, 8);
568 gtk_text_buffer_get_iter_at_line_offset (buffer, &iter2, 4, 15);
570 gtk_text_buffer_apply_tag_by_name (buffer, "fg_red", &iter, &iter2);
573 gtk_text_buffer_get_iter_at_mark (buffer, &iter, temp_mark);
574 gtk_text_buffer_insert (buffer, &iter, "Centered text!\n", -1);
576 gtk_text_buffer_get_iter_at_mark (buffer, &iter2, temp_mark);
577 gtk_text_buffer_apply_tag_by_name (buffer, "centered", &iter2, &iter);
579 gtk_text_buffer_move_mark (buffer, temp_mark, &iter);
580 gtk_text_buffer_insert (buffer, &iter, "Word wrapped, Right-to-left Quote\n", -1);
581 gtk_text_buffer_insert (buffer, &iter, "وقد بدأ ثلاث من أكثر المؤسسات تقدما في شبكة اكسيون برامجها كمنظمات لا تسعى للربح، ثم تحولت في السنوات الخمس الماضية إلى مؤسسات مالية منظمة، وباتت جزءا من النظام المالي في بلدانها، ولكنها تتخصص في خدمة قطاع المشروعات الصغيرة. وأحد أكثر هذه المؤسسات نجاحا هو »بانكوسول« في بوليفيا.\n", -1);
582 gtk_text_buffer_get_iter_at_mark (buffer, &iter2, temp_mark);
583 gtk_text_buffer_apply_tag_by_name (buffer, "rtl_quote", &iter2, &iter);
588 g_object_unref (G_OBJECT (pixbuf));
590 printf ("%d lines %d chars\n",
591 gtk_text_buffer_get_line_count (buffer),
592 gtk_text_buffer_get_char_count (buffer));
594 /* Move cursor to start */
595 gtk_text_buffer_get_iter_at_offset (buffer, &iter, 0);
596 gtk_text_buffer_place_cursor (buffer, &iter);
598 gtk_text_buffer_set_modified (buffer, FALSE);
602 fill_file_buffer (GtkTextBuffer *buffer, const char *filename)
607 GtkTextIter iter, end;
609 f = fopen (filename, "r");
613 gchar *err = g_strdup_printf ("Cannot open file '%s': %s",
614 filename, g_strerror (errno));
615 msgbox_run (NULL, err, "OK", NULL, NULL, 0);
620 gtk_text_buffer_get_iter_at_offset (buffer, &iter, 0);
624 char *leftover, *next;
625 int to_read = 2047 - remaining;
627 count = fread (buf + remaining, 1, to_read, f);
628 buf[count + remaining] = '\0';
630 leftover = next = buf;
637 next = g_utf8_next_char (next);
638 if (next > buf+count+remaining) {
644 gtk_text_buffer_insert (buffer, &iter, buf, leftover - buf);
646 remaining = buf + remaining + count - leftover;
647 g_memmove (buf, leftover, remaining);
649 if (remaining > 6 || count < to_read)
655 gchar *err = g_strdup_printf ("Invalid UTF-8 data encountered reading file '%s'", filename);
656 msgbox_run (NULL, err, "OK", NULL, NULL, 0);
660 /* We had a newline in the buffer to begin with. (The buffer always contains
661 * a newline, so we delete to the end of the buffer to clean up.
663 gtk_text_buffer_get_last_iter (buffer, &end);
664 gtk_text_buffer_delete (buffer, &iter, &end);
666 gtk_text_buffer_set_modified (buffer, FALSE);
672 delete_event_cb (GtkWidget *window, GdkEventAny *event, gpointer data)
674 View *view = view_from_widget (window);
676 push_active_window (GTK_WINDOW (window));
677 check_close_view (view);
678 pop_active_window ();
688 get_empty_view (View *view)
690 if (!view->buffer->filename &&
691 !gtk_text_buffer_modified (view->buffer->buffer))
694 return create_view (create_buffer ());
698 view_from_widget (GtkWidget *widget)
702 if (GTK_IS_MENU_ITEM (widget))
704 GtkItemFactory *item_factory = gtk_item_factory_from_widget (widget);
705 return gtk_object_get_data (GTK_OBJECT (item_factory), "view");
709 GtkWidget *app = gtk_widget_get_toplevel (widget);
710 return gtk_object_get_data (GTK_OBJECT (app), "view");
715 do_new (gpointer callback_data,
716 guint callback_action,
719 create_view (create_buffer ());
723 do_new_view (gpointer callback_data,
724 guint callback_action,
727 View *view = view_from_widget (widget);
729 create_view (view->buffer);
733 open_ok_func (const char *filename, gpointer data)
736 View *new_view = get_empty_view (view);
738 if (!fill_file_buffer (new_view->buffer->buffer, filename))
740 if (new_view != view)
741 close_view (new_view);
746 g_free (new_view->buffer->filename);
747 new_view->buffer->filename = g_strdup (filename);
748 buffer_filename_set (new_view->buffer);
755 do_open (gpointer callback_data,
756 guint callback_action,
759 View *view = view_from_widget (widget);
761 push_active_window (GTK_WINDOW (view->window));
762 filesel_run (NULL, "Open File", NULL, open_ok_func, view);
763 pop_active_window ();
767 do_save_as (gpointer callback_data,
768 guint callback_action,
771 View *view = view_from_widget (widget);
773 push_active_window (GTK_WINDOW (view->window));
774 save_as_buffer (view->buffer);
775 pop_active_window ();
779 do_save (gpointer callback_data,
780 guint callback_action,
783 View *view = view_from_widget (widget);
785 push_active_window (GTK_WINDOW (view->window));
786 if (!view->buffer->filename)
787 do_save_as (callback_data, callback_action, widget);
789 save_buffer (view->buffer);
790 pop_active_window ();
794 do_close (gpointer callback_data,
795 guint callback_action,
798 View *view = view_from_widget (widget);
800 push_active_window (GTK_WINDOW (view->window));
801 check_close_view (view);
802 pop_active_window ();
806 do_exit (gpointer callback_data,
807 guint callback_action,
810 View *view = view_from_widget (widget);
812 GSList *tmp_list = buffers;
814 push_active_window (GTK_WINDOW (view->window));
817 if (!check_buffer_saved (tmp_list->data))
820 tmp_list = tmp_list->next;
824 pop_active_window ();
828 do_example (gpointer callback_data,
829 guint callback_action,
832 View *view = view_from_widget (widget);
835 new_view = get_empty_view (view);
837 fill_example_buffer (new_view->buffer->buffer);
841 do_wrap_changed (gpointer callback_data,
842 guint callback_action,
845 View *view = view_from_widget (widget);
847 gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (view->text_view), callback_action);
851 do_direction_changed (gpointer callback_data,
852 guint callback_action,
855 View *view = view_from_widget (widget);
857 gtk_widget_set_direction (view->text_view, callback_action);
858 gtk_widget_queue_resize (view->text_view);
862 do_editable_changed (gpointer callback_data,
863 guint callback_action,
866 View *view = view_from_widget (widget);
868 gtk_text_view_set_editable (GTK_TEXT_VIEW (view->text_view), callback_action);
872 do_cursor_visible_changed (gpointer callback_data,
873 guint callback_action,
876 View *view = view_from_widget (widget);
878 gtk_text_view_set_cursor_visible (GTK_TEXT_VIEW (view->text_view), callback_action);
882 do_apply_editable (gpointer callback_data,
883 guint callback_action,
886 View *view = view_from_widget (widget);
890 if (gtk_text_buffer_get_selection_bounds (view->buffer->buffer,
895 gtk_text_buffer_remove_tag (view->buffer->buffer,
896 view->buffer->not_editable_tag,
901 gtk_text_buffer_apply_tag (view->buffer->buffer,
902 view->buffer->not_editable_tag,
910 do_apply_tabs (gpointer callback_data,
911 guint callback_action,
914 View *view = view_from_widget (widget);
918 if (gtk_text_buffer_get_selection_bounds (view->buffer->buffer,
923 gtk_text_buffer_remove_tag (view->buffer->buffer,
924 view->buffer->custom_tabs_tag,
929 gtk_text_buffer_apply_tag (view->buffer->buffer,
930 view->buffer->custom_tabs_tag,
943 dialog_response_callback (GtkWidget *dialog, gint response_id, gpointer data)
945 GtkTextBuffer *buffer;
947 GtkTextIter start, end;
948 gchar *search_string;
950 if (response_id != RESPONSE_FORWARD &&
951 response_id != RESPONSE_BACKWARD)
953 gtk_widget_destroy (dialog);
957 buffer = gtk_object_get_data (GTK_OBJECT (dialog), "buffer");
959 gtk_text_buffer_get_bounds (buffer, &start, &end);
961 /* Remove trailing newline */
962 gtk_text_iter_prev_char (&end);
964 search_string = gtk_text_iter_get_text (&start, &end);
966 printf ("Searching for `%s'\n", search_string);
968 if (response_id == RESPONSE_FORWARD)
969 buffer_search_forward (view->buffer, search_string, view);
970 else if (response_id == RESPONSE_BACKWARD)
971 buffer_search_backward (view->buffer, search_string, view);
973 g_free (search_string);
975 gtk_widget_destroy (dialog);
979 do_search (gpointer callback_data,
980 guint callback_action,
983 View *view = view_from_widget (widget);
985 GtkWidget *search_text;
986 GtkTextBuffer *buffer;
988 dialog = gtk_dialog_new_with_buttons ("Search",
989 GTK_WINDOW (view->window),
990 GTK_DIALOG_DESTROY_WITH_PARENT,
991 "Forward", RESPONSE_FORWARD,
992 "Backward", RESPONSE_BACKWARD,
993 GTK_STOCK_BUTTON_CANCEL,
994 GTK_RESPONSE_NONE, NULL);
997 buffer = gtk_text_buffer_new (NULL);
999 /* FIXME memory leak once buffer is a GObject */
1000 search_text = gtk_text_view_new_with_buffer (buffer);
1002 gtk_box_pack_end (GTK_BOX (GTK_DIALOG (dialog)->vbox),
1006 gtk_object_set_data (GTK_OBJECT (dialog), "buffer", buffer);
1008 gtk_signal_connect (GTK_OBJECT (dialog),
1010 GTK_SIGNAL_FUNC (dialog_response_callback),
1013 gtk_widget_show (search_text);
1015 gtk_widget_grab_focus (search_text);
1017 gtk_widget_show_all (dialog);
1021 view_init_menus (View *view)
1023 GtkTextDirection direction = gtk_widget_get_direction (view->text_view);
1024 GtkWrapMode wrap_mode = gtk_text_view_get_wrap_mode (GTK_TEXT_VIEW (view->text_view));
1025 GtkWidget *menu_item = NULL;
1029 case GTK_TEXT_DIR_LTR:
1030 menu_item = gtk_item_factory_get_widget (view->item_factory, "/Settings/Left-to-Right");
1032 case GTK_TEXT_DIR_RTL:
1033 menu_item = gtk_item_factory_get_widget (view->item_factory, "/Settings/Right-to-Left");
1040 gtk_menu_item_activate (GTK_MENU_ITEM (menu_item));
1044 case GTK_WRAPMODE_NONE:
1045 menu_item = gtk_item_factory_get_widget (view->item_factory, "/Settings/Wrap Off");
1047 case GTK_WRAPMODE_WORD:
1048 menu_item = gtk_item_factory_get_widget (view->item_factory, "/Settings/Wrap Words");
1055 gtk_menu_item_activate (GTK_MENU_ITEM (menu_item));
1058 static GtkItemFactoryEntry menu_items[] =
1060 { "/_File", NULL, 0, 0, "<Branch>" },
1061 { "/File/_New", "<control>N", do_new, 0, NULL },
1062 { "/File/New _View", NULL, do_new_view, 0, NULL },
1063 { "/File/_Open", "<control>O", do_open, 0, NULL },
1064 { "/File/_Save", "<control>S", do_save, 0, NULL },
1065 { "/File/Save _As...", NULL, do_save_as, 0, NULL },
1066 { "/File/sep1", NULL, 0, 0, "<Separator>" },
1067 { "/File/_Close", "<control>W" , do_close, 0, NULL },
1068 { "/File/E_xit", "<control>Q" , do_exit, 0, NULL },
1070 { "/_Edit", NULL, 0, 0, "<Branch>" },
1071 { "/Edit/Find...", NULL, do_search, 0, NULL },
1073 { "/_Settings", NULL, 0, 0, "<Branch>" },
1074 { "/Settings/Wrap _Off", NULL, do_wrap_changed, GTK_WRAPMODE_NONE, "<RadioItem>" },
1075 { "/Settings/Wrap _Words", NULL, do_wrap_changed, GTK_WRAPMODE_WORD, "/Settings/Wrap Off" },
1076 { "/Settings/sep1", NULL, 0, 0, "<Separator>" },
1077 { "/Settings/Editable", NULL, do_editable_changed, TRUE, "<RadioItem>" },
1078 { "/Settings/Not editable", NULL, do_editable_changed, FALSE, "/Settings/Editable" },
1079 { "/Settings/sep1", NULL, 0, 0, "<Separator>" },
1081 { "/Settings/Cursor visible", NULL, do_cursor_visible_changed, TRUE, "<RadioItem>" },
1082 { "/Settings/Cursor not visible", NULL, do_cursor_visible_changed, FALSE, "/Settings/Cursor visible" },
1083 { "/Settings/sep1", NULL, 0, 0, "<Separator>" },
1085 { "/Settings/Left-to-Right", NULL, do_direction_changed, GTK_TEXT_DIR_LTR, "<RadioItem>" },
1086 { "/Settings/Right-to-Left", NULL, do_direction_changed, GTK_TEXT_DIR_RTL, "/Settings/Left-to-Right" },
1087 { "/_Attributes", NULL, 0, 0, "<Branch>" },
1088 { "/Attributes/Editable", NULL, do_apply_editable, TRUE, NULL },
1089 { "/Attributes/Not editable", NULL, do_apply_editable, FALSE, NULL },
1090 { "/Attributes/Custom tabs", NULL, do_apply_tabs, FALSE, NULL },
1091 { "/Attributes/Default tabs", NULL, do_apply_tabs, TRUE, NULL },
1092 { "/_Test", NULL, 0, 0, "<Branch>" },
1093 { "/Test/_Example", NULL, do_example, 0, NULL },
1097 save_buffer (Buffer *buffer)
1099 GtkTextIter start, end;
1101 gboolean result = FALSE;
1102 gboolean have_backup = FALSE;
1103 gchar *bak_filename;
1106 g_return_val_if_fail (buffer->filename != NULL, FALSE);
1108 bak_filename = g_strconcat (buffer->filename, "~", NULL);
1110 if (rename (buffer->filename, bak_filename) != 0)
1112 if (errno != ENOENT)
1114 gchar *err = g_strdup_printf ("Cannot back up '%s' to '%s': %s",
1115 buffer->filename, bak_filename, g_strerror (errno));
1116 msgbox_run (NULL, err, "OK", NULL, NULL, 0);
1124 file = fopen (buffer->filename, "w");
1127 gchar *err = g_strdup_printf ("Cannot back up '%s' to '%s': %s",
1128 buffer->filename, bak_filename, g_strerror (errno));
1129 msgbox_run (NULL, err, "OK", NULL, NULL, 0);
1133 gtk_text_buffer_get_iter_at_offset (buffer->buffer, &start, 0);
1134 gtk_text_buffer_get_last_iter (buffer->buffer, &end);
1136 chars = gtk_text_buffer_get_slice (buffer->buffer, &start, &end, FALSE);
1138 if (fputs (chars, file) == EOF ||
1139 fclose (file) == EOF)
1141 gchar *err = g_strdup_printf ("Error writing to '%s': %s",
1142 buffer->filename, g_strerror (errno));
1143 msgbox_run (NULL, err, "OK", NULL, NULL, 0);
1151 gtk_text_buffer_set_modified (buffer->buffer, FALSE);
1157 if (!result && have_backup)
1159 if (rename (bak_filename, buffer->filename) != 0)
1161 gchar *err = g_strdup_printf ("Error restoring backup file '%s' to '%s': %s\nBackup left as '%s'",
1162 buffer->filename, bak_filename, g_strerror (errno), bak_filename);
1163 msgbox_run (NULL, err, "OK", NULL, NULL, 0);
1168 g_free (bak_filename);
1174 save_as_ok_func (const char *filename, gpointer data)
1176 Buffer *buffer = data;
1177 char *old_filename = buffer->filename;
1179 if (!buffer->filename || strcmp (filename, buffer->filename) != 0)
1181 struct stat statbuf;
1183 if (stat (filename, &statbuf) == 0)
1185 gchar *err = g_strdup_printf ("Ovewrite existing file '%s'?", filename);
1186 gint result = msgbox_run (NULL, err, "Yes", "No", NULL, 1);
1194 buffer->filename = g_strdup (filename);
1196 if (save_buffer (buffer))
1198 g_free (old_filename);
1199 buffer_filename_set (buffer);
1204 g_free (buffer->filename);
1205 buffer->filename = old_filename;
1211 save_as_buffer (Buffer *buffer)
1213 return filesel_run (NULL, "Save File", NULL, save_as_ok_func, buffer);
1217 check_buffer_saved (Buffer *buffer)
1219 if (gtk_text_buffer_modified (buffer->buffer))
1221 char *pretty_name = buffer_pretty_name (buffer);
1222 char *msg = g_strdup_printf ("Save changes to '%s'?", pretty_name);
1225 g_free (pretty_name);
1227 result = msgbox_run (NULL, msg, "Yes", "No", "Cancel", 0);
1231 return save_as_buffer (buffer);
1232 else if (result == 1)
1242 create_buffer (void)
1245 PangoTabArray *tabs;
1247 buffer = g_new (Buffer, 1);
1249 buffer->buffer = gtk_text_buffer_new (NULL);
1250 gtk_object_ref (GTK_OBJECT (buffer->buffer));
1251 gtk_object_sink (GTK_OBJECT (buffer->buffer));
1253 buffer->refcount = 1;
1254 buffer->filename = NULL;
1255 buffer->untitled_serial = -1;
1257 buffer->not_editable_tag = gtk_text_buffer_create_tag (buffer->buffer, NULL);
1258 gtk_object_set (GTK_OBJECT (buffer->not_editable_tag),
1260 "foreground", "purple", NULL);
1262 buffer->found_text_tag = gtk_text_buffer_create_tag (buffer->buffer, NULL);
1263 gtk_object_set (GTK_OBJECT (buffer->found_text_tag),
1264 "foreground", "red", NULL);
1266 tabs = pango_tab_array_new_with_positions (4,
1271 PANGO_TAB_LEFT, 120);
1273 buffer->custom_tabs_tag = gtk_text_buffer_create_tag (buffer->buffer, NULL);
1274 gtk_object_set (GTK_OBJECT (buffer->custom_tabs_tag),
1276 "foreground", "green", NULL);
1278 pango_tab_array_free (tabs);
1280 buffers = g_slist_prepend (buffers, buffer);
1286 buffer_pretty_name (Buffer *buffer)
1288 if (buffer->filename)
1291 char *result = g_path_get_basename (buffer->filename);
1292 p = strchr (result, '/');
1300 if (buffer->untitled_serial == -1)
1301 buffer->untitled_serial = untitled_serial++;
1303 if (buffer->untitled_serial == 1)
1304 return g_strdup ("Untitled");
1306 return g_strdup_printf ("Untitled #%d", buffer->untitled_serial);
1311 buffer_filename_set (Buffer *buffer)
1313 GSList *tmp_list = views;
1317 View *view = tmp_list->data;
1319 if (view->buffer == buffer)
1320 view_set_title (view);
1322 tmp_list = tmp_list->next;
1327 buffer_search (Buffer *buffer,
1333 GtkTextIter start, end;
1337 /* remove tag from whole buffer */
1338 gtk_text_buffer_get_bounds (buffer->buffer, &start, &end);
1339 gtk_text_buffer_remove_tag (buffer->buffer, buffer->found_text_tag,
1342 gtk_text_buffer_get_iter_at_mark (buffer->buffer, &iter,
1343 gtk_text_buffer_get_mark (buffer->buffer,
1349 GtkTextIter match_start, match_end;
1353 while (gtk_text_iter_forward_search (&iter, str, TRUE, FALSE,
1354 &match_start, &match_end))
1357 gtk_text_buffer_apply_tag (buffer->buffer, buffer->found_text_tag,
1358 &match_start, &match_end);
1365 while (gtk_text_iter_backward_search (&iter, str, TRUE, FALSE,
1366 &match_start, &match_end))
1369 gtk_text_buffer_apply_tag (buffer->buffer, buffer->found_text_tag,
1370 &match_start, &match_end);
1377 dialog = gtk_message_dialog_new (GTK_WINDOW (view->window),
1380 GTK_DIALOG_DESTROY_WITH_PARENT,
1381 "%d strings found and marked in red",
1384 gtk_signal_connect_object (GTK_OBJECT (dialog),
1386 GTK_SIGNAL_FUNC (gtk_widget_destroy),
1387 GTK_OBJECT (dialog));
1389 gtk_widget_show (dialog);
1393 buffer_search_forward (Buffer *buffer, const char *str,
1396 buffer_search (buffer, str, view, TRUE);
1400 buffer_search_backward (Buffer *buffer, const char *str,
1403 buffer_search (buffer, str, view, FALSE);
1407 buffer_ref (Buffer *buffer)
1413 buffer_unref (Buffer *buffer)
1416 if (buffer->refcount == 0)
1418 buffers = g_slist_remove (buffers, buffer);
1419 gtk_object_unref (GTK_OBJECT (buffer->buffer));
1420 g_free (buffer->filename);
1426 close_view (View *view)
1428 views = g_slist_remove (views, view);
1429 buffer_unref (view->buffer);
1430 gtk_widget_destroy (view->window);
1431 g_object_unref (G_OBJECT (view->item_factory));
1440 check_close_view (View *view)
1442 if (view->buffer->refcount > 1 ||
1443 check_buffer_saved (view->buffer))
1448 view_set_title (View *view)
1450 char *pretty_name = buffer_pretty_name (view->buffer);
1451 char *title = g_strconcat ("testtext - ", pretty_name, NULL);
1453 gtk_window_set_title (GTK_WINDOW (view->window), title);
1455 g_free (pretty_name);
1460 cursor_set_callback (GtkTextBuffer *buffer,
1461 const GtkTextIter *location,
1465 GtkTextView *text_view;
1467 /* Redraw tab windows if the cursor moves
1468 * on the mapped widget (windows may not exist before realization...
1471 text_view = GTK_TEXT_VIEW (user_data);
1473 if (GTK_WIDGET_MAPPED (text_view) &&
1474 mark == gtk_text_buffer_get_insert (buffer))
1476 GdkWindow *tab_window;
1478 tab_window = gtk_text_view_get_window (text_view,
1479 GTK_TEXT_WINDOW_TOP);
1481 gdk_window_invalidate_rect (tab_window, NULL, FALSE);
1483 tab_window = gtk_text_view_get_window (text_view,
1484 GTK_TEXT_WINDOW_BOTTOM);
1486 gdk_window_invalidate_rect (tab_window, NULL, FALSE);
1492 text_changed_callback (GtkTextBuffer *buffer,
1495 GtkTextView *text_view;
1497 /* Redraw line number windows if the buffer changes
1498 * and the widget is mapped (windows may not exist otherwise)
1501 text_view = GTK_TEXT_VIEW (user_data);
1503 if (GTK_WIDGET_MAPPED (text_view))
1505 GdkWindow *line_window;
1507 line_window = gtk_text_view_get_window (text_view,
1508 GTK_TEXT_WINDOW_LEFT);
1510 gdk_window_invalidate_rect (line_window, NULL, FALSE);
1512 line_window = gtk_text_view_get_window (text_view,
1513 GTK_TEXT_WINDOW_RIGHT);
1515 gdk_window_invalidate_rect (line_window, NULL, FALSE);
1520 tab_stops_expose (GtkWidget *widget,
1521 GdkEventExpose *event,
1528 GdkWindow *bottom_win;
1529 GtkTextView *text_view;
1530 GtkTextWindowType type;
1531 GdkDrawable *target;
1532 gint *positions = NULL;
1534 GtkTextAttributes *attrs;
1536 GtkTextBuffer *buffer;
1539 text_view = GTK_TEXT_VIEW (widget);
1541 /* See if this expose is on the tab stop window */
1542 top_win = gtk_text_view_get_window (text_view,
1543 GTK_TEXT_WINDOW_TOP);
1545 bottom_win = gtk_text_view_get_window (text_view,
1546 GTK_TEXT_WINDOW_BOTTOM);
1548 if (event->window == top_win)
1550 type = GTK_TEXT_WINDOW_TOP;
1553 else if (event->window == bottom_win)
1555 type = GTK_TEXT_WINDOW_BOTTOM;
1556 target = bottom_win;
1561 first_x = event->area.x;
1562 last_x = first_x + event->area.width;
1564 gtk_text_view_window_to_buffer_coords (text_view,
1571 gtk_text_view_window_to_buffer_coords (text_view,
1578 buffer = gtk_text_view_get_buffer (text_view);
1580 gtk_text_buffer_get_iter_at_mark (buffer,
1582 gtk_text_buffer_get_mark (buffer,
1585 attrs = gtk_text_attributes_new ();
1587 gtk_text_iter_get_attributes (&insert, attrs);
1591 size = pango_tab_array_get_size (attrs->tabs);
1593 pango_tab_array_get_tabs (attrs->tabs,
1597 in_pixels = pango_tab_array_get_positions_in_pixels (attrs->tabs);
1605 gtk_text_attributes_unref (attrs);
1613 positions[i] = PANGO_PIXELS (positions[i]);
1615 gtk_text_view_buffer_to_window_coords (text_view,
1622 gdk_draw_line (target,
1623 widget->style->fg_gc [widget->state],
1636 get_lines (GtkTextView *text_view,
1639 GArray *buffer_coords,
1647 g_array_set_size (buffer_coords, 0);
1648 g_array_set_size (numbers, 0);
1650 /* Get iter at first y */
1651 gtk_text_view_get_line_at_y (text_view, &iter, first_y, NULL);
1653 /* For each iter, get its location and add it to the arrays.
1654 * Stop when we pass last_y
1659 while (!gtk_text_iter_is_last (&iter))
1664 gtk_text_view_get_line_yrange (text_view, &iter, &y, &height);
1666 g_array_append_val (buffer_coords, y);
1667 line_num = gtk_text_iter_get_line (&iter);
1668 g_array_append_val (numbers, line_num);
1672 if ((y + height) >= last_y)
1675 gtk_text_iter_forward_line (&iter);
1682 line_numbers_expose (GtkWidget *widget,
1683 GdkEventExpose *event,
1692 GdkWindow *left_win;
1693 GdkWindow *right_win;
1694 PangoLayout *layout;
1695 GtkTextView *text_view;
1696 GtkTextWindowType type;
1697 GdkDrawable *target;
1699 text_view = GTK_TEXT_VIEW (widget);
1701 /* See if this expose is on the line numbers window */
1702 left_win = gtk_text_view_get_window (text_view,
1703 GTK_TEXT_WINDOW_LEFT);
1705 right_win = gtk_text_view_get_window (text_view,
1706 GTK_TEXT_WINDOW_RIGHT);
1708 if (event->window == left_win)
1710 type = GTK_TEXT_WINDOW_LEFT;
1713 else if (event->window == right_win)
1715 type = GTK_TEXT_WINDOW_RIGHT;
1721 first_y = event->area.y;
1722 last_y = first_y + event->area.height;
1724 gtk_text_view_window_to_buffer_coords (text_view,
1731 gtk_text_view_window_to_buffer_coords (text_view,
1738 numbers = g_array_new (FALSE, FALSE, sizeof (gint));
1739 pixels = g_array_new (FALSE, FALSE, sizeof (gint));
1741 get_lines (text_view,
1748 /* Draw fully internationalized numbers! */
1750 layout = gtk_widget_create_pango_layout (widget, "");
1758 gtk_text_view_buffer_to_window_coords (text_view,
1761 g_array_index (pixels, gint, i),
1765 str = g_strdup_printf ("%d", g_array_index (numbers, gint, i));
1767 pango_layout_set_text (layout, str, -1);
1770 gdk_draw_layout (target,
1771 widget->style->fg_gc [widget->state],
1772 /* 2 is just a random padding */
1781 g_array_free (pixels, TRUE);
1782 g_array_free (numbers, TRUE);
1784 g_object_unref (G_OBJECT (layout));
1790 create_view (Buffer *buffer)
1797 view = g_new0 (View, 1);
1798 views = g_slist_prepend (views, view);
1800 view->buffer = buffer;
1801 buffer_ref (buffer);
1803 view->window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
1804 gtk_object_set_data (GTK_OBJECT (view->window), "view", view);
1806 gtk_signal_connect (GTK_OBJECT (view->window), "delete_event",
1807 GTK_SIGNAL_FUNC (delete_event_cb), NULL);
1809 view->accel_group = gtk_accel_group_new ();
1810 view->item_factory = gtk_item_factory_new (GTK_TYPE_MENU_BAR, "<main>", view->accel_group);
1811 gtk_object_set_data (GTK_OBJECT (view->item_factory), "view", view);
1813 gtk_item_factory_create_items (view->item_factory, G_N_ELEMENTS (menu_items), menu_items, view);
1815 gtk_window_add_accel_group (GTK_WINDOW (view->window), view->accel_group);
1817 vbox = gtk_vbox_new (FALSE, 0);
1818 gtk_container_add (GTK_CONTAINER (view->window), vbox);
1820 gtk_box_pack_start (GTK_BOX (vbox),
1821 gtk_item_factory_get_widget (view->item_factory, "<main>"),
1824 sw = gtk_scrolled_window_new (NULL, NULL);
1825 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
1826 GTK_POLICY_AUTOMATIC,
1827 GTK_POLICY_AUTOMATIC);
1829 view->text_view = gtk_text_view_new_with_buffer (buffer->buffer);
1830 gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (view->text_view),
1834 /* Draw tab stops in the top and bottom windows. */
1836 gtk_text_view_set_border_window_size (GTK_TEXT_VIEW (view->text_view),
1837 GTK_TEXT_WINDOW_TOP,
1840 gtk_text_view_set_border_window_size (GTK_TEXT_VIEW (view->text_view),
1841 GTK_TEXT_WINDOW_BOTTOM,
1844 gtk_signal_connect (GTK_OBJECT (view->text_view),
1846 GTK_SIGNAL_FUNC (tab_stops_expose),
1849 gtk_signal_connect (GTK_OBJECT (view->buffer->buffer),
1851 GTK_SIGNAL_FUNC (cursor_set_callback),
1854 /* Draw line numbers in the side windows; we should really be
1855 * more scientific about what width we set them to.
1857 gtk_text_view_set_border_window_size (GTK_TEXT_VIEW (view->text_view),
1858 GTK_TEXT_WINDOW_RIGHT,
1861 gtk_text_view_set_border_window_size (GTK_TEXT_VIEW (view->text_view),
1862 GTK_TEXT_WINDOW_LEFT,
1865 gtk_signal_connect (GTK_OBJECT (view->text_view),
1867 GTK_SIGNAL_FUNC (line_numbers_expose),
1870 gtk_signal_connect (GTK_OBJECT (view->buffer->buffer),
1872 GTK_SIGNAL_FUNC (text_changed_callback),
1875 gtk_box_pack_start (GTK_BOX (vbox), sw, TRUE, TRUE, 0);
1876 gtk_container_add (GTK_CONTAINER (sw), view->text_view);
1878 gtk_window_set_default_size (GTK_WINDOW (view->window), 500, 500);
1880 gtk_widget_grab_focus (view->text_view);
1882 view_set_title (view);
1883 view_init_menus (view);
1885 gtk_widget_show_all (view->window);
1890 main (int argc, char** argv)
1896 gtk_init (&argc, &argv);
1898 buffer = create_buffer ();
1899 view = create_view (buffer);
1900 buffer_unref (buffer);
1902 push_active_window (GTK_WINDOW (view->window));
1903 for (i=1; i < argc; i++)
1907 /* Quick and dirty canonicalization - better should be in GLib
1910 if (!g_path_is_absolute (argv[i]))
1912 char *cwd = g_get_current_dir ();
1913 filename = g_strconcat (cwd, "/", argv[i], NULL);
1919 open_ok_func (filename, view);
1921 if (filename != argv[i])
1924 pop_active_window ();