8 #include <gdk/gdkkeysyms.h>
10 #include "prop-editor.h"
12 typedef struct _Buffer Buffer;
13 typedef struct _View View;
15 static gint untitled_serial = 1;
17 GSList *active_window_stack = NULL;
22 GtkTextBuffer *buffer;
25 GtkTextTag *invisible_tag;
26 GtkTextTag *not_editable_tag;
27 GtkTextTag *found_text_tag;
28 GtkTextTag *custom_tabs_tag;
30 guint color_cycle_timeout;
38 GtkAccelGroup *accel_group;
39 GtkItemFactory *item_factory;
43 static void push_active_window (GtkWindow *window);
44 static void pop_active_window (void);
45 static GtkWindow *get_active_window (void);
47 static Buffer * create_buffer (void);
48 static gboolean check_buffer_saved (Buffer *buffer);
49 static gboolean save_buffer (Buffer *buffer);
50 static gboolean save_as_buffer (Buffer *buffer);
51 static char * buffer_pretty_name (Buffer *buffer);
52 static void buffer_filename_set (Buffer *buffer);
53 static void buffer_search_forward (Buffer *buffer,
56 static void buffer_search_backward (Buffer *buffer,
59 static void buffer_set_colors (Buffer *buffer,
61 static void buffer_cycle_colors (Buffer *buffer);
63 static View *view_from_widget (GtkWidget *widget);
65 static View *create_view (Buffer *buffer);
66 static void check_close_view (View *view);
67 static void close_view (View *view);
68 static void view_set_title (View *view);
69 static void view_init_menus (View *view);
70 static void view_add_example_widgets (View *view);
72 GSList *buffers = NULL;
76 push_active_window (GtkWindow *window)
78 g_object_ref (G_OBJECT (window));
79 active_window_stack = g_slist_prepend (active_window_stack, window);
83 pop_active_window (void)
85 gtk_object_unref (active_window_stack->data);
86 active_window_stack = g_slist_delete_link (active_window_stack, active_window_stack);
90 get_active_window (void)
92 if (active_window_stack)
93 return active_window_stack->data;
99 * Filesel utility function
102 typedef gboolean (*FileselOKFunc) (const char *filename, gpointer data);
105 filesel_ok_cb (GtkWidget *button, GtkWidget *filesel)
107 FileselOKFunc ok_func = g_object_get_data (G_OBJECT (filesel), "ok-func");
108 gpointer data = g_object_get_data (G_OBJECT (filesel), "ok-data");
109 gint *result = g_object_get_data (G_OBJECT (filesel), "ok-result");
111 gtk_widget_hide (filesel);
113 if ((*ok_func) (gtk_file_selection_get_filename (GTK_FILE_SELECTION (filesel)), data))
115 gtk_widget_destroy (filesel);
119 gtk_widget_show (filesel);
123 filesel_run (GtkWindow *parent,
125 const char *start_file,
129 GtkWidget *filesel = gtk_file_selection_new (title);
130 gboolean result = FALSE;
133 parent = get_active_window ();
136 gtk_window_set_transient_for (GTK_WINDOW (filesel), parent);
139 gtk_file_selection_set_filename (GTK_FILE_SELECTION (filesel), start_file);
142 g_object_set_data (G_OBJECT (filesel), "ok-func", func);
143 g_object_set_data (G_OBJECT (filesel), "ok-data", data);
144 g_object_set_data (G_OBJECT (filesel), "ok-result", &result);
146 gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION (filesel)->ok_button),
148 GTK_SIGNAL_FUNC (filesel_ok_cb), filesel);
149 gtk_signal_connect_object (GTK_OBJECT (GTK_FILE_SELECTION (filesel)->cancel_button),
151 GTK_SIGNAL_FUNC (gtk_widget_destroy), GTK_OBJECT (filesel));
153 gtk_signal_connect (GTK_OBJECT (filesel), "destroy",
154 GTK_SIGNAL_FUNC (gtk_main_quit), NULL);
155 gtk_window_set_modal (GTK_WINDOW (filesel), TRUE);
157 gtk_widget_show (filesel);
164 * MsgBox utility functions
168 msgbox_yes_cb (GtkWidget *widget, gboolean *result)
171 gtk_object_destroy (GTK_OBJECT (gtk_widget_get_toplevel (widget)));
175 msgbox_no_cb (GtkWidget *widget, gboolean *result)
178 gtk_object_destroy (GTK_OBJECT (gtk_widget_get_toplevel (widget)));
182 msgbox_key_press_cb (GtkWidget *widget, GdkEventKey *event, gpointer data)
184 if (event->keyval == GDK_Escape)
186 gtk_signal_emit_stop_by_name (GTK_OBJECT (widget), "key_press_event");
187 gtk_object_destroy (GTK_OBJECT (widget));
194 /* Don't copy this example, it's all crack-smoking - you can just use
195 * GtkMessageDialog now
198 msgbox_run (GtkWindow *parent,
200 const char *yes_button,
201 const char *no_button,
202 const char *cancel_button,
205 gboolean result = -1;
210 GtkWidget *button_box;
211 GtkWidget *separator;
213 g_return_val_if_fail (message != NULL, FALSE);
214 g_return_val_if_fail (default_index >= 0 && default_index <= 1, FALSE);
217 parent = get_active_window ();
221 dialog = gtk_window_new (GTK_WINDOW_TOPLEVEL);
222 gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
224 gtk_window_set_transient_for (GTK_WINDOW (dialog), parent);
225 gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
227 /* Quit our recursive main loop when the dialog is destroyed.
229 gtk_signal_connect (GTK_OBJECT (dialog), "destroy",
230 GTK_SIGNAL_FUNC (gtk_main_quit), NULL);
232 /* Catch Escape key presses and have them destroy the dialog
234 gtk_signal_connect (GTK_OBJECT (dialog), "key_press_event",
235 GTK_SIGNAL_FUNC (msgbox_key_press_cb), NULL);
237 /* Fill in the contents of the widget
239 vbox = gtk_vbox_new (FALSE, 0);
240 gtk_container_add (GTK_CONTAINER (dialog), vbox);
242 label = gtk_label_new (message);
243 gtk_misc_set_padding (GTK_MISC (label), 12, 12);
244 gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
245 gtk_box_pack_start (GTK_BOX (vbox), label, TRUE, TRUE, 0);
247 separator = gtk_hseparator_new ();
248 gtk_box_pack_start (GTK_BOX (vbox), separator, FALSE, FALSE, 0);
250 button_box = gtk_hbutton_box_new ();
251 gtk_box_pack_start (GTK_BOX (vbox), button_box, FALSE, FALSE, 0);
252 gtk_container_set_border_width (GTK_CONTAINER (button_box), 8);
255 /* When Yes is clicked, call the msgbox_yes_cb
256 * This sets the result variable and destroys the dialog
260 button = gtk_button_new_with_label (yes_button);
261 GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
262 gtk_container_add (GTK_CONTAINER (button_box), button);
264 if (default_index == 0)
265 gtk_widget_grab_default (button);
267 gtk_signal_connect (GTK_OBJECT (button), "clicked",
268 GTK_SIGNAL_FUNC (msgbox_yes_cb), &result);
271 /* When No is clicked, call the msgbox_no_cb
272 * This sets the result variable and destroys the dialog
276 button = gtk_button_new_with_label (no_button);
277 GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
278 gtk_container_add (GTK_CONTAINER (button_box), button);
280 if (default_index == 0)
281 gtk_widget_grab_default (button);
283 gtk_signal_connect (GTK_OBJECT (button), "clicked",
284 GTK_SIGNAL_FUNC (msgbox_no_cb), &result);
287 /* When Cancel is clicked, destroy the dialog
291 button = gtk_button_new_with_label (cancel_button);
292 GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
293 gtk_container_add (GTK_CONTAINER (button_box), button);
295 if (default_index == 1)
296 gtk_widget_grab_default (button);
298 gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
299 GTK_SIGNAL_FUNC (gtk_object_destroy), GTK_OBJECT (dialog));
302 gtk_widget_show_all (dialog);
304 /* Run a recursive main loop until a button is clicked
305 * or the user destroys the dialog through the window mananger */
312 * Example buffer filling code
315 blink_timeout (gpointer data)
318 static gboolean flip = FALSE;
320 tag = GTK_TEXT_TAG (data);
322 g_object_set (G_OBJECT (tag),
323 "foreground", flip ? "blue" : "purple",
332 tag_event_handler (GtkTextTag *tag, GtkWidget *widget, GdkEvent *event,
333 const GtkTextIter *iter, gpointer user_data)
337 char_index = gtk_text_iter_get_offset (iter);
341 case GDK_MOTION_NOTIFY:
342 printf ("Motion event at char %d tag `%s'\n",
343 char_index, tag->name);
346 case GDK_BUTTON_PRESS:
347 printf ("Button press at char %d tag `%s'\n",
348 char_index, tag->name);
351 case GDK_2BUTTON_PRESS:
352 printf ("Double click at char %d tag `%s'\n",
353 char_index, tag->name);
356 case GDK_3BUTTON_PRESS:
357 printf ("Triple click at char %d tag `%s'\n",
358 char_index, tag->name);
361 case GDK_BUTTON_RELEASE:
362 printf ("Button release at char %d tag `%s'\n",
363 char_index, tag->name);
367 case GDK_KEY_RELEASE:
368 printf ("Key event at char %d tag `%s'\n",
369 char_index, tag->name);
372 case GDK_ENTER_NOTIFY:
373 case GDK_LEAVE_NOTIFY:
374 case GDK_PROPERTY_NOTIFY:
375 case GDK_SELECTION_CLEAR:
376 case GDK_SELECTION_REQUEST:
377 case GDK_SELECTION_NOTIFY:
378 case GDK_PROXIMITY_IN:
379 case GDK_PROXIMITY_OUT:
382 case GDK_DRAG_MOTION:
383 case GDK_DRAG_STATUS:
385 case GDK_DROP_FINISHED:
394 setup_tag (GtkTextTag *tag)
396 g_signal_connect (G_OBJECT (tag),
398 G_CALLBACK (tag_event_handler),
402 static const char *book_closed_xpm[] = {
428 fill_example_buffer (GtkTextBuffer *buffer)
430 GtkTextIter iter, iter2;
432 GtkTextChildAnchor *anchor;
439 /* FIXME this is broken if called twice on a buffer, since
440 * we try to create tags a second time.
443 tag = gtk_text_buffer_create_tag (buffer, "fg_blue", NULL);
445 /* gtk_timeout_add (1000, blink_timeout, tag); */
449 color.red = color.green = 0;
454 g_object_set (G_OBJECT (tag),
455 "foreground_gdk", &color,
456 "background_gdk", &color2,
460 tag = gtk_text_buffer_create_tag (buffer, "fg_red", NULL);
464 color.blue = color.green = 0;
466 g_object_set (G_OBJECT (tag),
467 "rise", -4 * PANGO_SCALE,
468 "foreground_gdk", &color,
471 tag = gtk_text_buffer_create_tag (buffer, "bg_green", NULL);
475 color.blue = color.red = 0;
476 color.green = 0xffff;
477 g_object_set (G_OBJECT (tag),
478 "background_gdk", &color,
482 tag = gtk_text_buffer_create_tag (buffer, "strikethrough", NULL);
486 g_object_set (G_OBJECT (tag),
487 "strikethrough", TRUE,
491 tag = gtk_text_buffer_create_tag (buffer, "underline", NULL);
495 g_object_set (G_OBJECT (tag),
496 "underline", PANGO_UNDERLINE_SINGLE,
501 g_object_set (G_OBJECT (tag),
502 "underline", PANGO_UNDERLINE_SINGLE,
505 tag = gtk_text_buffer_create_tag (buffer, "centered", NULL);
507 g_object_set (G_OBJECT (tag),
508 "justification", GTK_JUSTIFY_CENTER,
511 tag = gtk_text_buffer_create_tag (buffer, "rtl_quote", NULL);
513 g_object_set (G_OBJECT (tag),
514 "wrap_mode", GTK_WRAP_WORD,
515 "direction", GTK_TEXT_DIR_RTL,
522 gtk_text_buffer_get_iter_at_offset (buffer, &iter, 0);
524 anchor = gtk_text_buffer_create_child_anchor (buffer, &iter);
526 g_object_ref (G_OBJECT (anchor));
528 g_object_set_data_full (G_OBJECT (buffer), "anchor", anchor,
529 (GDestroyNotify) g_object_unref);
531 pixbuf = gdk_pixbuf_new_from_xpm_data (book_closed_xpm);
536 GtkTextMark * temp_mark;
538 gtk_text_buffer_get_iter_at_offset (buffer, &iter, 0);
540 gtk_text_buffer_insert_pixbuf (buffer, &iter, pixbuf);
542 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",
545 gtk_text_buffer_insert (buffer, &iter, str, -1);
549 gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, 0, 5);
551 gtk_text_buffer_insert (buffer, &iter,
552 "(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"
553 /* This is UTF8 stuff, Emacs doesn't
554 really know how to display it */
555 "German (Deutsch Süd) Grüß Gott Greek (Ελληνικά) Γειά σας Hebrew(שלום) Hebrew punctuation(\xd6\xbfש\xd6\xbb\xd6\xbc\xd6\xbb\xd6\xbfל\xd6\xbcו\xd6\xbc\xd6\xbb\xd6\xbb\xd6\xbfם\xd6\xbc\xd6\xbb\xd6\xbf) Japanese (日本語) Thai (สวัสดีครับ) Thai wrong spelling (คำต่อไปนื่สะกดผิด พัั้ัั่งโกะ)\n", -1);
558 gtk_text_buffer_create_mark (buffer, "tmp_mark", &iter, TRUE);
561 gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, 0, 6);
562 gtk_text_buffer_get_iter_at_line_offset (buffer, &iter2, 0, 13);
564 gtk_text_buffer_apply_tag_by_name (buffer, "fg_blue", &iter, &iter2);
566 gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, 1, 10);
567 gtk_text_buffer_get_iter_at_line_offset (buffer, &iter2, 1, 16);
569 gtk_text_buffer_apply_tag_by_name (buffer, "underline", &iter, &iter2);
571 gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, 1, 14);
572 gtk_text_buffer_get_iter_at_line_offset (buffer, &iter2, 1, 24);
574 gtk_text_buffer_apply_tag_by_name (buffer, "strikethrough", &iter, &iter2);
576 gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, 0, 9);
577 gtk_text_buffer_get_iter_at_line_offset (buffer, &iter2, 0, 16);
579 gtk_text_buffer_apply_tag_by_name (buffer, "bg_green", &iter, &iter2);
581 gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, 4, 2);
582 gtk_text_buffer_get_iter_at_line_offset (buffer, &iter2, 4, 10);
584 gtk_text_buffer_apply_tag_by_name (buffer, "bg_green", &iter, &iter2);
586 gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, 4, 8);
587 gtk_text_buffer_get_iter_at_line_offset (buffer, &iter2, 4, 15);
589 gtk_text_buffer_apply_tag_by_name (buffer, "fg_red", &iter, &iter2);
592 gtk_text_buffer_get_iter_at_mark (buffer, &iter, temp_mark);
593 gtk_text_buffer_insert (buffer, &iter, "Centered text!\n", -1);
595 gtk_text_buffer_get_iter_at_mark (buffer, &iter2, temp_mark);
596 gtk_text_buffer_apply_tag_by_name (buffer, "centered", &iter2, &iter);
598 gtk_text_buffer_move_mark (buffer, temp_mark, &iter);
599 gtk_text_buffer_insert (buffer, &iter, "Word wrapped, Right-to-left Quote\n", -1);
600 gtk_text_buffer_insert (buffer, &iter, "وقد بدأ ثلاث من أكثر المؤسسات تقدما في شبكة اكسيون برامجها كمنظمات لا تسعى للربح، ثم تحولت في السنوات الخمس الماضية إلى مؤسسات مالية منظمة، وباتت جزءا من النظام المالي في بلدانها، ولكنها تتخصص في خدمة قطاع المشروعات الصغيرة. وأحد أكثر هذه المؤسسات نجاحا هو »بانكوسول« في بوليفيا.\n", -1);
601 gtk_text_buffer_get_iter_at_mark (buffer, &iter2, temp_mark);
602 gtk_text_buffer_apply_tag_by_name (buffer, "rtl_quote", &iter2, &iter);
607 g_object_unref (G_OBJECT (pixbuf));
609 printf ("%d lines %d chars\n",
610 gtk_text_buffer_get_line_count (buffer),
611 gtk_text_buffer_get_char_count (buffer));
613 /* Move cursor to start */
614 gtk_text_buffer_get_iter_at_offset (buffer, &iter, 0);
615 gtk_text_buffer_place_cursor (buffer, &iter);
617 gtk_text_buffer_set_modified (buffer, FALSE);
621 fill_file_buffer (GtkTextBuffer *buffer, const char *filename)
626 GtkTextIter iter, end;
628 f = fopen (filename, "r");
632 gchar *err = g_strdup_printf ("Cannot open file '%s': %s",
633 filename, g_strerror (errno));
634 msgbox_run (NULL, err, "OK", NULL, NULL, 0);
639 gtk_text_buffer_get_iter_at_offset (buffer, &iter, 0);
643 const char *leftover;
644 int to_read = 2047 - remaining;
646 count = fread (buf + remaining, 1, to_read, f);
647 buf[count + remaining] = '\0';
649 g_utf8_validate (buf, count + remaining, &leftover);
651 g_assert (g_utf8_validate (buf, leftover - buf, NULL));
652 gtk_text_buffer_insert (buffer, &iter, buf, leftover - buf);
654 remaining = (buf + remaining + count) - leftover;
655 g_memmove (buf, leftover, remaining);
657 if (remaining > 6 || count < to_read)
663 gchar *err = g_strdup_printf ("Invalid UTF-8 data encountered reading file '%s'", filename);
664 msgbox_run (NULL, err, "OK", NULL, NULL, 0);
668 /* We had a newline in the buffer to begin with. (The buffer always contains
669 * a newline, so we delete to the end of the buffer to clean up.
671 gtk_text_buffer_get_end_iter (buffer, &end);
672 gtk_text_buffer_delete (buffer, &iter, &end);
674 gtk_text_buffer_set_modified (buffer, FALSE);
680 delete_event_cb (GtkWidget *window, GdkEventAny *event, gpointer data)
682 View *view = view_from_widget (window);
684 push_active_window (GTK_WINDOW (window));
685 check_close_view (view);
686 pop_active_window ();
696 get_empty_view (View *view)
698 if (!view->buffer->filename &&
699 !gtk_text_buffer_get_modified (view->buffer->buffer))
702 return create_view (create_buffer ());
706 view_from_widget (GtkWidget *widget)
708 if (GTK_IS_MENU_ITEM (widget))
710 GtkItemFactory *item_factory = gtk_item_factory_from_widget (widget);
711 return g_object_get_data (G_OBJECT (item_factory), "view");
715 GtkWidget *app = gtk_widget_get_toplevel (widget);
716 return g_object_get_data (G_OBJECT (app), "view");
721 do_new (gpointer callback_data,
722 guint callback_action,
725 create_view (create_buffer ());
729 do_new_view (gpointer callback_data,
730 guint callback_action,
733 View *view = view_from_widget (widget);
735 create_view (view->buffer);
739 open_ok_func (const char *filename, gpointer data)
742 View *new_view = get_empty_view (view);
744 if (!fill_file_buffer (new_view->buffer->buffer, filename))
746 if (new_view != view)
747 close_view (new_view);
752 g_free (new_view->buffer->filename);
753 new_view->buffer->filename = g_strdup (filename);
754 buffer_filename_set (new_view->buffer);
761 do_open (gpointer callback_data,
762 guint callback_action,
765 View *view = view_from_widget (widget);
767 push_active_window (GTK_WINDOW (view->window));
768 filesel_run (NULL, "Open File", NULL, open_ok_func, view);
769 pop_active_window ();
773 do_save_as (gpointer callback_data,
774 guint callback_action,
777 View *view = view_from_widget (widget);
779 push_active_window (GTK_WINDOW (view->window));
780 save_as_buffer (view->buffer);
781 pop_active_window ();
785 do_save (gpointer callback_data,
786 guint callback_action,
789 View *view = view_from_widget (widget);
791 push_active_window (GTK_WINDOW (view->window));
792 if (!view->buffer->filename)
793 do_save_as (callback_data, callback_action, widget);
795 save_buffer (view->buffer);
796 pop_active_window ();
800 do_close (gpointer callback_data,
801 guint callback_action,
804 View *view = view_from_widget (widget);
806 push_active_window (GTK_WINDOW (view->window));
807 check_close_view (view);
808 pop_active_window ();
812 do_exit (gpointer callback_data,
813 guint callback_action,
816 View *view = view_from_widget (widget);
818 GSList *tmp_list = buffers;
820 push_active_window (GTK_WINDOW (view->window));
823 if (!check_buffer_saved (tmp_list->data))
826 tmp_list = tmp_list->next;
830 pop_active_window ();
834 do_example (gpointer callback_data,
835 guint callback_action,
838 View *view = view_from_widget (widget);
841 new_view = get_empty_view (view);
843 fill_example_buffer (new_view->buffer->buffer);
845 view_add_example_widgets (new_view);
850 do_insert_and_scroll (gpointer callback_data,
851 guint callback_action,
854 View *view = view_from_widget (widget);
855 GtkTextBuffer *buffer;
856 GtkTextIter start, end;
859 buffer = view->buffer->buffer;
861 gtk_text_buffer_get_bounds (buffer, &start, &end);
862 mark = gtk_text_buffer_create_mark (buffer, NULL, &end, /* right grav */ FALSE);
864 gtk_text_iter_backward_char (&end);
865 gtk_text_buffer_insert (buffer, &end,
866 "Hello this is multiple lines of text\n"
867 "Line 1\n" "Line 2\n"
868 "Line 3\n" "Line 4\n"
872 gtk_text_view_scroll_to_mark (GTK_TEXT_VIEW (view->text_view), mark,
874 gtk_text_buffer_delete_mark (buffer, mark);
878 do_wrap_changed (gpointer callback_data,
879 guint callback_action,
882 View *view = view_from_widget (widget);
884 gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (view->text_view), callback_action);
888 do_direction_changed (gpointer callback_data,
889 guint callback_action,
892 View *view = view_from_widget (widget);
894 gtk_widget_set_direction (view->text_view, callback_action);
895 gtk_widget_queue_resize (view->text_view);
900 do_spacing_changed (gpointer callback_data,
901 guint callback_action,
904 View *view = view_from_widget (widget);
908 gtk_text_view_set_pixels_above_lines (GTK_TEXT_VIEW (view->text_view),
910 gtk_text_view_set_pixels_below_lines (GTK_TEXT_VIEW (view->text_view),
912 gtk_text_view_set_pixels_inside_wrap (GTK_TEXT_VIEW (view->text_view),
917 gtk_text_view_set_pixels_above_lines (GTK_TEXT_VIEW (view->text_view),
919 gtk_text_view_set_pixels_below_lines (GTK_TEXT_VIEW (view->text_view),
921 gtk_text_view_set_pixels_inside_wrap (GTK_TEXT_VIEW (view->text_view),
927 do_editable_changed (gpointer callback_data,
928 guint callback_action,
931 View *view = view_from_widget (widget);
933 gtk_text_view_set_editable (GTK_TEXT_VIEW (view->text_view), callback_action);
937 do_cursor_visible_changed (gpointer callback_data,
938 guint callback_action,
941 View *view = view_from_widget (widget);
943 gtk_text_view_set_cursor_visible (GTK_TEXT_VIEW (view->text_view), callback_action);
947 do_color_cycle_changed (gpointer callback_data,
948 guint callback_action,
951 View *view = view_from_widget (widget);
953 buffer_set_colors (view->buffer, callback_action);
957 do_apply_editable (gpointer callback_data,
958 guint callback_action,
961 View *view = view_from_widget (widget);
965 if (gtk_text_buffer_get_selection_bounds (view->buffer->buffer,
970 gtk_text_buffer_remove_tag (view->buffer->buffer,
971 view->buffer->not_editable_tag,
976 gtk_text_buffer_apply_tag (view->buffer->buffer,
977 view->buffer->not_editable_tag,
984 do_apply_invisible (gpointer callback_data,
985 guint callback_action,
988 View *view = view_from_widget (widget);
992 if (gtk_text_buffer_get_selection_bounds (view->buffer->buffer,
997 gtk_text_buffer_remove_tag (view->buffer->buffer,
998 view->buffer->invisible_tag,
1003 gtk_text_buffer_apply_tag (view->buffer->buffer,
1004 view->buffer->invisible_tag,
1011 do_apply_tabs (gpointer callback_data,
1012 guint callback_action,
1015 View *view = view_from_widget (widget);
1019 if (gtk_text_buffer_get_selection_bounds (view->buffer->buffer,
1022 if (callback_action)
1024 gtk_text_buffer_remove_tag (view->buffer->buffer,
1025 view->buffer->custom_tabs_tag,
1030 gtk_text_buffer_apply_tag (view->buffer->buffer,
1031 view->buffer->custom_tabs_tag,
1038 do_apply_colors (gpointer callback_data,
1039 guint callback_action,
1042 View *view = view_from_widget (widget);
1043 Buffer *buffer = view->buffer;
1047 if (gtk_text_buffer_get_selection_bounds (view->buffer->buffer,
1050 if (!callback_action)
1054 tmp = buffer->color_tags;
1057 gtk_text_buffer_remove_tag (view->buffer->buffer,
1060 tmp = g_slist_next (tmp);
1067 tmp = buffer->color_tags;
1071 gboolean done = FALSE;
1074 gtk_text_iter_forward_char (&next);
1075 gtk_text_iter_forward_char (&next);
1077 if (gtk_text_iter_compare (&next, &end) > 0)
1083 gtk_text_buffer_apply_tag (view->buffer->buffer,
1092 tmp = g_slist_next (tmp);
1094 tmp = buffer->color_tags;
1101 do_remove_tags (gpointer callback_data,
1102 guint callback_action,
1105 View *view = view_from_widget (widget);
1109 if (gtk_text_buffer_get_selection_bounds (view->buffer->buffer,
1112 gtk_text_buffer_remove_all_tags (view->buffer->buffer,
1118 do_properties (gpointer callback_data,
1119 guint callback_action,
1122 View *view = view_from_widget (widget);
1124 create_prop_editor (G_OBJECT (view->text_view), 0);
1134 dialog_response_callback (GtkWidget *dialog, gint response_id, gpointer data)
1136 GtkTextBuffer *buffer;
1138 GtkTextIter start, end;
1139 gchar *search_string;
1141 if (response_id != RESPONSE_FORWARD &&
1142 response_id != RESPONSE_BACKWARD)
1144 gtk_widget_destroy (dialog);
1148 buffer = g_object_get_data (G_OBJECT (dialog), "buffer");
1150 gtk_text_buffer_get_bounds (buffer, &start, &end);
1152 /* Remove trailing newline */
1153 gtk_text_iter_backward_char (&end);
1155 search_string = gtk_text_iter_get_text (&start, &end);
1157 printf ("Searching for `%s'\n", search_string);
1159 if (response_id == RESPONSE_FORWARD)
1160 buffer_search_forward (view->buffer, search_string, view);
1161 else if (response_id == RESPONSE_BACKWARD)
1162 buffer_search_backward (view->buffer, search_string, view);
1164 g_free (search_string);
1166 gtk_widget_destroy (dialog);
1170 do_search (gpointer callback_data,
1171 guint callback_action,
1174 View *view = view_from_widget (widget);
1176 GtkWidget *search_text;
1177 GtkTextBuffer *buffer;
1179 dialog = gtk_dialog_new_with_buttons ("Search",
1180 GTK_WINDOW (view->window),
1181 GTK_DIALOG_DESTROY_WITH_PARENT,
1182 "Forward", RESPONSE_FORWARD,
1183 "Backward", RESPONSE_BACKWARD,
1185 GTK_RESPONSE_NONE, NULL);
1188 buffer = gtk_text_buffer_new (NULL);
1190 search_text = gtk_text_view_new_with_buffer (buffer);
1192 g_object_unref (G_OBJECT (buffer));
1194 gtk_box_pack_end (GTK_BOX (GTK_DIALOG (dialog)->vbox),
1198 g_object_set_data (G_OBJECT (dialog), "buffer", buffer);
1200 gtk_signal_connect (GTK_OBJECT (dialog),
1202 GTK_SIGNAL_FUNC (dialog_response_callback),
1205 gtk_widget_show (search_text);
1207 gtk_widget_grab_focus (search_text);
1209 gtk_widget_show_all (dialog);
1214 /* position is in coordinate system of text_view_move_child */
1223 movable_child_callback (GtkWidget *child,
1227 ChildMoveInfo *info;
1228 GtkTextView *text_view;
1230 text_view = GTK_TEXT_VIEW (data);
1232 g_return_val_if_fail (GTK_IS_EVENT_BOX (child), FALSE);
1233 g_return_val_if_fail (gtk_widget_get_parent (child) == GTK_WIDGET (text_view), FALSE);
1235 info = g_object_get_data (G_OBJECT (child),
1236 "testtext-move-info");
1240 info = g_new (ChildMoveInfo, 1);
1244 g_object_set_data_full (G_OBJECT (child),
1245 "testtext-move-info",
1250 switch (event->type)
1252 case GDK_BUTTON_PRESS:
1253 if (info->button < 0)
1255 if (gdk_pointer_grab (event->button.window,
1257 GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK |
1258 GDK_BUTTON_RELEASE_MASK,
1261 event->button.time) != GDK_GRAB_SUCCESS)
1264 info->button = event->button.button;
1266 info->start_x = child->allocation.x;
1267 info->start_y = child->allocation.y;
1268 info->click_x = child->allocation.x + event->button.x;
1269 info->click_y = child->allocation.y + event->button.y;
1273 case GDK_BUTTON_RELEASE:
1274 if (info->button < 0)
1277 if (info->button == event->button.button)
1281 gdk_pointer_ungrab (event->button.time);
1284 /* convert to window coords from event box coords */
1285 x = info->start_x + (event->button.x + child->allocation.x - info->click_x);
1286 y = info->start_y + (event->button.y + child->allocation.y - info->click_y);
1288 gtk_text_view_move_child (text_view,
1294 case GDK_MOTION_NOTIFY:
1298 if (info->button < 0)
1301 gdk_window_get_pointer (child->window, &x, &y, NULL); /* ensure more events */
1303 /* to window coords from event box coords */
1304 x += child->allocation.x;
1305 y += child->allocation.y;
1307 x = info->start_x + (x - info->click_x);
1308 y = info->start_y + (y - info->click_y);
1310 gtk_text_view_move_child (text_view,
1324 add_movable_child (GtkTextView *text_view,
1325 GtkTextWindowType window)
1327 GtkWidget *event_box;
1331 label = gtk_label_new ("Drag me around");
1333 event_box = gtk_event_box_new ();
1334 gtk_widget_add_events (event_box,
1335 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
1336 GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK);
1339 color.green = color.blue = 0;
1340 gtk_widget_modify_bg (event_box, GTK_STATE_NORMAL, &color);
1342 gtk_container_add (GTK_CONTAINER (event_box), label);
1344 gtk_widget_show_all (event_box);
1346 g_signal_connect (G_OBJECT (event_box), "event",
1347 G_CALLBACK (movable_child_callback),
1350 gtk_text_view_add_child_in_window (text_view,
1357 do_add_children (gpointer callback_data,
1358 guint callback_action,
1361 View *view = view_from_widget (widget);
1363 add_movable_child (GTK_TEXT_VIEW (view->text_view),
1364 GTK_TEXT_WINDOW_WIDGET);
1365 add_movable_child (GTK_TEXT_VIEW (view->text_view),
1366 GTK_TEXT_WINDOW_LEFT);
1367 add_movable_child (GTK_TEXT_VIEW (view->text_view),
1368 GTK_TEXT_WINDOW_RIGHT);
1372 view_init_menus (View *view)
1374 GtkTextDirection direction = gtk_widget_get_direction (view->text_view);
1375 GtkWrapMode wrap_mode = gtk_text_view_get_wrap_mode (GTK_TEXT_VIEW (view->text_view));
1376 GtkWidget *menu_item = NULL;
1380 case GTK_TEXT_DIR_LTR:
1381 menu_item = gtk_item_factory_get_widget (view->item_factory, "/Settings/Left-to-Right");
1383 case GTK_TEXT_DIR_RTL:
1384 menu_item = gtk_item_factory_get_widget (view->item_factory, "/Settings/Right-to-Left");
1391 gtk_menu_item_activate (GTK_MENU_ITEM (menu_item));
1396 menu_item = gtk_item_factory_get_widget (view->item_factory, "/Settings/Wrap Off");
1399 menu_item = gtk_item_factory_get_widget (view->item_factory, "/Settings/Wrap Words");
1402 menu_item = gtk_item_factory_get_widget (view->item_factory, "/Settings/Wrap Chars");
1409 gtk_menu_item_activate (GTK_MENU_ITEM (menu_item));
1412 static GtkItemFactoryEntry menu_items[] =
1414 { "/_File", NULL, 0, 0, "<Branch>" },
1415 { "/File/_New", "<control>N", do_new, 0, NULL },
1416 { "/File/New _View", NULL, do_new_view, 0, NULL },
1417 { "/File/_Open", "<control>O", do_open, 0, NULL },
1418 { "/File/_Save", "<control>S", do_save, 0, NULL },
1419 { "/File/Save _As...", NULL, do_save_as, 0, NULL },
1420 { "/File/sep1", NULL, 0, 0, "<Separator>" },
1421 { "/File/_Close", "<control>W" , do_close, 0, NULL },
1422 { "/File/E_xit", "<control>Q" , do_exit, 0, NULL },
1424 { "/_Edit", NULL, 0, 0, "<Branch>" },
1425 { "/Edit/Find...", NULL, do_search, 0, NULL },
1427 { "/_Settings", NULL, 0, 0, "<Branch>" },
1428 { "/Settings/Wrap _Off", NULL, do_wrap_changed, GTK_WRAP_NONE, "<RadioItem>" },
1429 { "/Settings/Wrap _Words", NULL, do_wrap_changed, GTK_WRAP_WORD, "/Settings/Wrap Off" },
1430 { "/Settings/Wrap _Chars", NULL, do_wrap_changed, GTK_WRAP_CHAR, "/Settings/Wrap Off" },
1431 { "/Settings/sep1", NULL, 0, 0, "<Separator>" },
1432 { "/Settings/Editable", NULL, do_editable_changed, TRUE, "<RadioItem>" },
1433 { "/Settings/Not editable", NULL, do_editable_changed, FALSE, "/Settings/Editable" },
1434 { "/Settings/sep1", NULL, 0, 0, "<Separator>" },
1436 { "/Settings/Cursor visible", NULL, do_cursor_visible_changed, TRUE, "<RadioItem>" },
1437 { "/Settings/Cursor not visible", NULL, do_cursor_visible_changed, FALSE, "/Settings/Cursor visible" },
1438 { "/Settings/sep1", NULL, 0, 0, "<Separator>" },
1440 { "/Settings/Left-to-Right", NULL, do_direction_changed, GTK_TEXT_DIR_LTR, "<RadioItem>" },
1441 { "/Settings/Right-to-Left", NULL, do_direction_changed, GTK_TEXT_DIR_RTL, "/Settings/Left-to-Right" },
1443 { "/Settings/sep1", NULL, 0, 0, "<Separator>" },
1444 { "/Settings/Sane spacing", NULL, do_spacing_changed, FALSE, "<RadioItem>" },
1445 { "/Settings/Funky spacing", NULL, do_spacing_changed, TRUE, "/Settings/Sane spacing" },
1446 { "/Settings/sep1", NULL, 0, 0, "<Separator>" },
1447 { "/Settings/Don't cycle color tags", NULL, do_color_cycle_changed, FALSE, "<RadioItem>" },
1448 { "/Settings/Cycle colors", NULL, do_color_cycle_changed, TRUE, "/Settings/Don't cycle color tags" },
1449 { "/_Attributes", NULL, 0, 0, "<Branch>" },
1450 { "/Attributes/Editable", NULL, do_apply_editable, TRUE, NULL },
1451 { "/Attributes/Not editable", NULL, do_apply_editable, FALSE, NULL },
1452 { "/Attributes/Invisible", NULL, do_apply_invisible, FALSE, NULL },
1453 { "/Attributes/Visible", NULL, do_apply_invisible, TRUE, NULL },
1454 { "/Attributes/Custom tabs", NULL, do_apply_tabs, FALSE, NULL },
1455 { "/Attributes/Default tabs", NULL, do_apply_tabs, TRUE, NULL },
1456 { "/Attributes/Color cycles", NULL, do_apply_colors, TRUE, NULL },
1457 { "/Attributes/No colors", NULL, do_apply_colors, FALSE, NULL },
1458 { "/Attributes/Remove all tags", NULL, do_remove_tags, 0, NULL },
1459 { "/Attributes/Properties", NULL, do_properties, 0, NULL },
1460 { "/_Test", NULL, 0, 0, "<Branch>" },
1461 { "/Test/_Example", NULL, do_example, 0, NULL },
1462 { "/Test/_Insert and scroll", NULL, do_insert_and_scroll, 0, NULL },
1463 { "/Test/_Add fixed children", NULL, do_add_children, 0, NULL },
1467 save_buffer (Buffer *buffer)
1469 GtkTextIter start, end;
1471 gboolean result = FALSE;
1472 gboolean have_backup = FALSE;
1473 gchar *bak_filename;
1476 g_return_val_if_fail (buffer->filename != NULL, FALSE);
1478 bak_filename = g_strconcat (buffer->filename, "~", NULL);
1480 if (rename (buffer->filename, bak_filename) != 0)
1482 if (errno != ENOENT)
1484 gchar *err = g_strdup_printf ("Cannot back up '%s' to '%s': %s",
1485 buffer->filename, bak_filename, g_strerror (errno));
1486 msgbox_run (NULL, err, "OK", NULL, NULL, 0);
1494 file = fopen (buffer->filename, "w");
1497 gchar *err = g_strdup_printf ("Cannot back up '%s' to '%s': %s",
1498 buffer->filename, bak_filename, g_strerror (errno));
1499 msgbox_run (NULL, err, "OK", NULL, NULL, 0);
1503 gtk_text_buffer_get_iter_at_offset (buffer->buffer, &start, 0);
1504 gtk_text_buffer_get_end_iter (buffer->buffer, &end);
1506 chars = gtk_text_buffer_get_slice (buffer->buffer, &start, &end, FALSE);
1508 if (fputs (chars, file) == EOF ||
1509 fclose (file) == EOF)
1511 gchar *err = g_strdup_printf ("Error writing to '%s': %s",
1512 buffer->filename, g_strerror (errno));
1513 msgbox_run (NULL, err, "OK", NULL, NULL, 0);
1521 gtk_text_buffer_set_modified (buffer->buffer, FALSE);
1527 if (!result && have_backup)
1529 if (rename (bak_filename, buffer->filename) != 0)
1531 gchar *err = g_strdup_printf ("Error restoring backup file '%s' to '%s': %s\nBackup left as '%s'",
1532 buffer->filename, bak_filename, g_strerror (errno), bak_filename);
1533 msgbox_run (NULL, err, "OK", NULL, NULL, 0);
1538 g_free (bak_filename);
1544 save_as_ok_func (const char *filename, gpointer data)
1546 Buffer *buffer = data;
1547 char *old_filename = buffer->filename;
1549 if (!buffer->filename || strcmp (filename, buffer->filename) != 0)
1551 struct stat statbuf;
1553 if (stat (filename, &statbuf) == 0)
1555 gchar *err = g_strdup_printf ("Ovewrite existing file '%s'?", filename);
1556 gint result = msgbox_run (NULL, err, "Yes", "No", NULL, 1);
1564 buffer->filename = g_strdup (filename);
1566 if (save_buffer (buffer))
1568 g_free (old_filename);
1569 buffer_filename_set (buffer);
1574 g_free (buffer->filename);
1575 buffer->filename = old_filename;
1581 save_as_buffer (Buffer *buffer)
1583 return filesel_run (NULL, "Save File", NULL, save_as_ok_func, buffer);
1587 check_buffer_saved (Buffer *buffer)
1589 if (gtk_text_buffer_get_modified (buffer->buffer))
1591 char *pretty_name = buffer_pretty_name (buffer);
1592 char *msg = g_strdup_printf ("Save changes to '%s'?", pretty_name);
1595 g_free (pretty_name);
1597 result = msgbox_run (NULL, msg, "Yes", "No", "Cancel", 0);
1601 return save_as_buffer (buffer);
1602 else if (result == 1)
1614 create_buffer (void)
1617 PangoTabArray *tabs;
1620 buffer = g_new (Buffer, 1);
1622 buffer->buffer = gtk_text_buffer_new (NULL);
1624 buffer->refcount = 1;
1625 buffer->filename = NULL;
1626 buffer->untitled_serial = -1;
1628 buffer->color_tags = NULL;
1629 buffer->color_cycle_timeout = 0;
1630 buffer->start_hue = 0.0;
1633 while (i < N_COLORS)
1637 tag = gtk_text_buffer_create_tag (buffer->buffer, NULL, NULL);
1639 buffer->color_tags = g_slist_prepend (buffer->color_tags, tag);
1644 buffer->invisible_tag = gtk_text_buffer_create_tag (buffer->buffer, NULL,
1645 "invisible", TRUE, NULL);
1647 buffer->not_editable_tag =
1648 gtk_text_buffer_create_tag (buffer->buffer, NULL,
1650 "foreground", "purple", NULL);
1652 buffer->found_text_tag = gtk_text_buffer_create_tag (buffer->buffer, NULL,
1653 "foreground", "red", NULL);
1655 tabs = pango_tab_array_new_with_positions (4,
1660 PANGO_TAB_LEFT, 120);
1662 buffer->custom_tabs_tag = gtk_text_buffer_create_tag (buffer->buffer, NULL,
1664 "foreground", "green", NULL);
1666 pango_tab_array_free (tabs);
1668 buffers = g_slist_prepend (buffers, buffer);
1674 buffer_pretty_name (Buffer *buffer)
1676 if (buffer->filename)
1679 char *result = g_path_get_basename (buffer->filename);
1680 p = strchr (result, '/');
1688 if (buffer->untitled_serial == -1)
1689 buffer->untitled_serial = untitled_serial++;
1691 if (buffer->untitled_serial == 1)
1692 return g_strdup ("Untitled");
1694 return g_strdup_printf ("Untitled #%d", buffer->untitled_serial);
1699 buffer_filename_set (Buffer *buffer)
1701 GSList *tmp_list = views;
1705 View *view = tmp_list->data;
1707 if (view->buffer == buffer)
1708 view_set_title (view);
1710 tmp_list = tmp_list->next;
1715 buffer_search (Buffer *buffer,
1721 GtkTextIter start, end;
1725 /* remove tag from whole buffer */
1726 gtk_text_buffer_get_bounds (buffer->buffer, &start, &end);
1727 gtk_text_buffer_remove_tag (buffer->buffer, buffer->found_text_tag,
1730 gtk_text_buffer_get_iter_at_mark (buffer->buffer, &iter,
1731 gtk_text_buffer_get_mark (buffer->buffer,
1737 GtkTextIter match_start, match_end;
1741 while (gtk_text_iter_forward_search (&iter, str,
1742 GTK_TEXT_SEARCH_VISIBLE_ONLY |
1743 GTK_TEXT_SEARCH_TEXT_ONLY,
1744 &match_start, &match_end,
1748 gtk_text_buffer_apply_tag (buffer->buffer, buffer->found_text_tag,
1749 &match_start, &match_end);
1756 while (gtk_text_iter_backward_search (&iter, str,
1757 GTK_TEXT_SEARCH_VISIBLE_ONLY |
1758 GTK_TEXT_SEARCH_TEXT_ONLY,
1759 &match_start, &match_end,
1763 gtk_text_buffer_apply_tag (buffer->buffer, buffer->found_text_tag,
1764 &match_start, &match_end);
1771 dialog = gtk_message_dialog_new (GTK_WINDOW (view->window),
1774 GTK_DIALOG_DESTROY_WITH_PARENT,
1775 "%d strings found and marked in red",
1778 gtk_signal_connect_object (GTK_OBJECT (dialog),
1780 GTK_SIGNAL_FUNC (gtk_widget_destroy),
1781 GTK_OBJECT (dialog));
1783 gtk_widget_show (dialog);
1787 buffer_search_forward (Buffer *buffer, const char *str,
1790 buffer_search (buffer, str, view, TRUE);
1794 buffer_search_backward (Buffer *buffer, const char *str,
1797 buffer_search (buffer, str, view, FALSE);
1801 buffer_ref (Buffer *buffer)
1807 buffer_unref (Buffer *buffer)
1810 if (buffer->refcount == 0)
1812 buffer_set_colors (buffer, FALSE);
1813 buffers = g_slist_remove (buffers, buffer);
1814 g_object_unref (G_OBJECT (buffer->buffer));
1815 g_free (buffer->filename);
1821 hsv_to_rgb (gdouble *h,
1825 gdouble hue, saturation, value;
1843 f = hue - (int) hue;
1844 p = value * (1.0 - saturation);
1845 q = value * (1.0 - saturation * f);
1846 t = value * (1.0 - saturation * (1.0 - f));
1887 g_assert_not_reached ();
1893 hue_to_color (gdouble hue,
1902 g_return_if_fail (hue <= 1.0);
1904 hsv_to_rgb (&h, &s, &v);
1906 color->red = h * 65535;
1907 color->green = s * 65535;
1908 color->blue = v * 65535;
1913 color_cycle_timeout (gpointer data)
1915 Buffer *buffer = data;
1917 buffer_cycle_colors (buffer);
1923 buffer_set_colors (Buffer *buffer,
1929 if (enabled && buffer->color_cycle_timeout == 0)
1930 buffer->color_cycle_timeout = gtk_timeout_add (200, color_cycle_timeout, buffer);
1931 else if (!enabled && buffer->color_cycle_timeout != 0)
1933 gtk_timeout_remove (buffer->color_cycle_timeout);
1934 buffer->color_cycle_timeout = 0;
1937 tmp = buffer->color_tags;
1944 hue_to_color (hue, &color);
1946 g_object_set (G_OBJECT (tmp->data),
1947 "foreground_gdk", &color,
1951 g_object_set (G_OBJECT (tmp->data),
1952 "foreground_set", FALSE,
1955 hue += 1.0 / N_COLORS;
1957 tmp = g_slist_next (tmp);
1962 buffer_cycle_colors (Buffer *buffer)
1965 gdouble hue = buffer->start_hue;
1967 tmp = buffer->color_tags;
1972 hue_to_color (hue, &color);
1974 g_object_set (G_OBJECT (tmp->data),
1975 "foreground_gdk", &color,
1978 hue += 1.0 / N_COLORS;
1982 tmp = g_slist_next (tmp);
1985 buffer->start_hue += 1.0 / N_COLORS;
1986 if (buffer->start_hue > 1.0)
1987 buffer->start_hue = 0.0;
1991 close_view (View *view)
1993 views = g_slist_remove (views, view);
1994 buffer_unref (view->buffer);
1995 gtk_widget_destroy (view->window);
1996 g_object_unref (G_OBJECT (view->item_factory));
2005 check_close_view (View *view)
2007 if (view->buffer->refcount > 1 ||
2008 check_buffer_saved (view->buffer))
2013 view_set_title (View *view)
2015 char *pretty_name = buffer_pretty_name (view->buffer);
2016 char *title = g_strconcat ("testtext - ", pretty_name, NULL);
2018 gtk_window_set_title (GTK_WINDOW (view->window), title);
2020 g_free (pretty_name);
2025 cursor_set_callback (GtkTextBuffer *buffer,
2026 const GtkTextIter *location,
2030 GtkTextView *text_view;
2032 /* Redraw tab windows if the cursor moves
2033 * on the mapped widget (windows may not exist before realization...
2036 text_view = GTK_TEXT_VIEW (user_data);
2038 if (GTK_WIDGET_MAPPED (text_view) &&
2039 mark == gtk_text_buffer_get_insert (buffer))
2041 GdkWindow *tab_window;
2043 tab_window = gtk_text_view_get_window (text_view,
2044 GTK_TEXT_WINDOW_TOP);
2046 gdk_window_invalidate_rect (tab_window, NULL, FALSE);
2048 tab_window = gtk_text_view_get_window (text_view,
2049 GTK_TEXT_WINDOW_BOTTOM);
2051 gdk_window_invalidate_rect (tab_window, NULL, FALSE);
2056 tab_stops_expose (GtkWidget *widget,
2057 GdkEventExpose *event,
2064 GdkWindow *bottom_win;
2065 GtkTextView *text_view;
2066 GtkTextWindowType type;
2067 GdkDrawable *target;
2068 gint *positions = NULL;
2070 GtkTextAttributes *attrs;
2072 GtkTextBuffer *buffer;
2075 text_view = GTK_TEXT_VIEW (widget);
2077 /* See if this expose is on the tab stop window */
2078 top_win = gtk_text_view_get_window (text_view,
2079 GTK_TEXT_WINDOW_TOP);
2081 bottom_win = gtk_text_view_get_window (text_view,
2082 GTK_TEXT_WINDOW_BOTTOM);
2084 if (event->window == top_win)
2086 type = GTK_TEXT_WINDOW_TOP;
2089 else if (event->window == bottom_win)
2091 type = GTK_TEXT_WINDOW_BOTTOM;
2092 target = bottom_win;
2097 first_x = event->area.x;
2098 last_x = first_x + event->area.width;
2100 gtk_text_view_window_to_buffer_coords (text_view,
2107 gtk_text_view_window_to_buffer_coords (text_view,
2114 buffer = gtk_text_view_get_buffer (text_view);
2116 gtk_text_buffer_get_iter_at_mark (buffer,
2118 gtk_text_buffer_get_mark (buffer,
2121 attrs = gtk_text_attributes_new ();
2123 gtk_text_iter_get_attributes (&insert, attrs);
2127 size = pango_tab_array_get_size (attrs->tabs);
2129 pango_tab_array_get_tabs (attrs->tabs,
2133 in_pixels = pango_tab_array_get_positions_in_pixels (attrs->tabs);
2141 gtk_text_attributes_unref (attrs);
2149 positions[i] = PANGO_PIXELS (positions[i]);
2151 gtk_text_view_buffer_to_window_coords (text_view,
2158 gdk_draw_line (target,
2159 widget->style->fg_gc [widget->state],
2172 get_lines (GtkTextView *text_view,
2175 GArray *buffer_coords,
2183 g_array_set_size (buffer_coords, 0);
2184 g_array_set_size (numbers, 0);
2186 /* Get iter at first y */
2187 gtk_text_view_get_line_at_y (text_view, &iter, first_y, NULL);
2189 /* For each iter, get its location and add it to the arrays.
2190 * Stop when we pass last_y
2195 while (!gtk_text_iter_is_end (&iter))
2200 gtk_text_view_get_line_yrange (text_view, &iter, &y, &height);
2202 g_array_append_val (buffer_coords, y);
2203 line_num = gtk_text_iter_get_line (&iter);
2204 g_array_append_val (numbers, line_num);
2208 if ((y + height) >= last_y)
2211 gtk_text_iter_forward_line (&iter);
2218 line_numbers_expose (GtkWidget *widget,
2219 GdkEventExpose *event,
2228 GdkWindow *left_win;
2229 GdkWindow *right_win;
2230 PangoLayout *layout;
2231 GtkTextView *text_view;
2232 GtkTextWindowType type;
2233 GdkDrawable *target;
2235 text_view = GTK_TEXT_VIEW (widget);
2237 /* See if this expose is on the line numbers window */
2238 left_win = gtk_text_view_get_window (text_view,
2239 GTK_TEXT_WINDOW_LEFT);
2241 right_win = gtk_text_view_get_window (text_view,
2242 GTK_TEXT_WINDOW_RIGHT);
2244 if (event->window == left_win)
2246 type = GTK_TEXT_WINDOW_LEFT;
2249 else if (event->window == right_win)
2251 type = GTK_TEXT_WINDOW_RIGHT;
2257 first_y = event->area.y;
2258 last_y = first_y + event->area.height;
2260 gtk_text_view_window_to_buffer_coords (text_view,
2267 gtk_text_view_window_to_buffer_coords (text_view,
2274 numbers = g_array_new (FALSE, FALSE, sizeof (gint));
2275 pixels = g_array_new (FALSE, FALSE, sizeof (gint));
2277 get_lines (text_view,
2284 /* Draw fully internationalized numbers! */
2286 layout = gtk_widget_create_pango_layout (widget, "");
2294 gtk_text_view_buffer_to_window_coords (text_view,
2297 g_array_index (pixels, gint, i),
2301 str = g_strdup_printf ("%d", g_array_index (numbers, gint, i));
2303 pango_layout_set_text (layout, str, -1);
2305 gtk_paint_layout (widget->style,
2307 GTK_WIDGET_STATE (widget),
2320 g_array_free (pixels, TRUE);
2321 g_array_free (numbers, TRUE);
2323 g_object_unref (G_OBJECT (layout));
2329 create_view (Buffer *buffer)
2336 view = g_new0 (View, 1);
2337 views = g_slist_prepend (views, view);
2339 view->buffer = buffer;
2340 buffer_ref (buffer);
2342 view->window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2343 g_object_set_data (G_OBJECT (view->window), "view", view);
2345 gtk_signal_connect (GTK_OBJECT (view->window), "delete_event",
2346 GTK_SIGNAL_FUNC (delete_event_cb), NULL);
2348 view->accel_group = gtk_accel_group_new ();
2349 view->item_factory = gtk_item_factory_new (GTK_TYPE_MENU_BAR, "<main>", view->accel_group);
2350 g_object_set_data (G_OBJECT (view->item_factory), "view", view);
2352 gtk_item_factory_create_items (view->item_factory, G_N_ELEMENTS (menu_items), menu_items, view);
2354 gtk_window_add_accel_group (GTK_WINDOW (view->window), view->accel_group);
2356 vbox = gtk_vbox_new (FALSE, 0);
2357 gtk_container_add (GTK_CONTAINER (view->window), vbox);
2359 gtk_box_pack_start (GTK_BOX (vbox),
2360 gtk_item_factory_get_widget (view->item_factory, "<main>"),
2363 sw = gtk_scrolled_window_new (NULL, NULL);
2364 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
2365 GTK_POLICY_AUTOMATIC,
2366 GTK_POLICY_AUTOMATIC);
2368 view->text_view = gtk_text_view_new_with_buffer (buffer->buffer);
2369 gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (view->text_view),
2372 /* Draw tab stops in the top and bottom windows. */
2374 gtk_text_view_set_border_window_size (GTK_TEXT_VIEW (view->text_view),
2375 GTK_TEXT_WINDOW_TOP,
2378 gtk_text_view_set_border_window_size (GTK_TEXT_VIEW (view->text_view),
2379 GTK_TEXT_WINDOW_BOTTOM,
2382 gtk_signal_connect (GTK_OBJECT (view->text_view),
2384 GTK_SIGNAL_FUNC (tab_stops_expose),
2387 g_signal_connect (G_OBJECT (view->buffer->buffer),
2389 GTK_SIGNAL_FUNC (cursor_set_callback),
2392 /* Draw line numbers in the side windows; we should really be
2393 * more scientific about what width we set them to.
2395 gtk_text_view_set_border_window_size (GTK_TEXT_VIEW (view->text_view),
2396 GTK_TEXT_WINDOW_RIGHT,
2399 gtk_text_view_set_border_window_size (GTK_TEXT_VIEW (view->text_view),
2400 GTK_TEXT_WINDOW_LEFT,
2403 gtk_signal_connect (GTK_OBJECT (view->text_view),
2405 GTK_SIGNAL_FUNC (line_numbers_expose),
2408 gtk_box_pack_start (GTK_BOX (vbox), sw, TRUE, TRUE, 0);
2409 gtk_container_add (GTK_CONTAINER (sw), view->text_view);
2411 gtk_window_set_default_size (GTK_WINDOW (view->window), 500, 500);
2413 gtk_widget_grab_focus (view->text_view);
2415 view_set_title (view);
2416 view_init_menus (view);
2418 view_add_example_widgets (view);
2420 gtk_widget_show_all (view->window);
2425 view_add_example_widgets (View *view)
2427 GtkTextChildAnchor *anchor;
2430 buffer = view->buffer;
2432 anchor = g_object_get_data (G_OBJECT (buffer->buffer),
2435 if (anchor && !gtk_text_child_anchor_get_deleted (anchor))
2439 widget = gtk_button_new_with_label ("Foo");
2441 gtk_text_view_add_child_at_anchor (GTK_TEXT_VIEW (view->text_view),
2445 gtk_widget_show (widget);
2450 file_exists (const char *filename)
2452 struct stat statbuf;
2454 return stat (filename, &statbuf) == 0;
2459 if (file_exists ("../gdk-pixbuf/.libs/libpixbufloader-pnm.so"))
2461 putenv ("GDK_PIXBUF_MODULEDIR=../gdk-pixbuf/.libs");
2462 putenv ("GTK_IM_MODULE_FILE=../modules/input/gtk.immodules");
2467 main (int argc, char** argv)
2474 gtk_init (&argc, &argv);
2476 buffer = create_buffer ();
2477 view = create_view (buffer);
2478 buffer_unref (buffer);
2480 push_active_window (GTK_WINDOW (view->window));
2481 for (i=1; i < argc; i++)
2485 /* Quick and dirty canonicalization - better should be in GLib
2488 if (!g_path_is_absolute (argv[i]))
2490 char *cwd = g_get_current_dir ();
2491 filename = g_strconcat (cwd, "/", argv[i], NULL);
2497 open_ok_func (filename, view);
2499 if (filename != argv[i])
2502 pop_active_window ();