2 * Copyright (C) 2000 Red Hat, Inc
3 * Author: Havoc Pennington
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
28 #undef GTK_DISABLE_DEPRECATED
31 #include <gdk/gdkkeysyms.h>
33 #include "prop-editor.h"
35 typedef struct _Buffer Buffer;
36 typedef struct _View View;
38 static gint untitled_serial = 1;
40 GSList *active_window_stack = NULL;
45 GtkTextBuffer *buffer;
48 GtkTextTag *invisible_tag;
49 GtkTextTag *not_editable_tag;
50 GtkTextTag *found_text_tag;
52 GtkTextTag *large_tag;
53 GtkTextTag *indent_tag;
54 GtkTextTag *margin_tag;
55 GtkTextTag *custom_tabs_tag;
57 guint color_cycle_timeout;
65 GtkAccelGroup *accel_group;
66 GtkItemFactory *item_factory;
70 static void push_active_window (GtkWindow *window);
71 static void pop_active_window (void);
72 static GtkWindow *get_active_window (void);
74 static Buffer * create_buffer (void);
75 static gboolean check_buffer_saved (Buffer *buffer);
76 static gboolean save_buffer (Buffer *buffer);
77 static gboolean save_as_buffer (Buffer *buffer);
78 static char * buffer_pretty_name (Buffer *buffer);
79 static void buffer_filename_set (Buffer *buffer);
80 static void buffer_search_forward (Buffer *buffer,
83 static void buffer_search_backward (Buffer *buffer,
86 static void buffer_set_colors (Buffer *buffer,
88 static void buffer_cycle_colors (Buffer *buffer);
90 static View *view_from_widget (GtkWidget *widget);
92 static View *create_view (Buffer *buffer);
93 static void check_close_view (View *view);
94 static void close_view (View *view);
95 static void view_set_title (View *view);
96 static void view_init_menus (View *view);
97 static void view_add_example_widgets (View *view);
99 GSList *buffers = NULL;
100 GSList *views = NULL;
103 push_active_window (GtkWindow *window)
105 g_object_ref (window);
106 active_window_stack = g_slist_prepend (active_window_stack, window);
110 pop_active_window (void)
112 g_object_unref (active_window_stack->data);
113 active_window_stack = g_slist_delete_link (active_window_stack, active_window_stack);
117 get_active_window (void)
119 if (active_window_stack)
120 return active_window_stack->data;
126 * Filesel utility function
129 typedef gboolean (*FileselOKFunc) (const char *filename, gpointer data);
132 filesel_ok_cb (GtkWidget *button, GtkWidget *filesel)
134 FileselOKFunc ok_func = (FileselOKFunc)g_object_get_data (G_OBJECT (filesel), "ok-func");
135 gpointer data = g_object_get_data (G_OBJECT (filesel), "ok-data");
136 gint *result = g_object_get_data (G_OBJECT (filesel), "ok-result");
138 gtk_widget_hide (filesel);
140 if ((*ok_func) (gtk_file_selection_get_filename (GTK_FILE_SELECTION (filesel)), data))
142 gtk_widget_destroy (filesel);
146 gtk_widget_show (filesel);
150 filesel_run (GtkWindow *parent,
152 const char *start_file,
156 GtkWidget *filesel = gtk_file_selection_new (title);
157 gboolean result = FALSE;
160 parent = get_active_window ();
163 gtk_window_set_transient_for (GTK_WINDOW (filesel), parent);
166 gtk_file_selection_set_filename (GTK_FILE_SELECTION (filesel), start_file);
169 g_object_set_data (G_OBJECT (filesel), "ok-func", func);
170 g_object_set_data (G_OBJECT (filesel), "ok-data", data);
171 g_object_set_data (G_OBJECT (filesel), "ok-result", &result);
173 g_signal_connect (GTK_FILE_SELECTION (filesel)->ok_button,
175 G_CALLBACK (filesel_ok_cb), filesel);
176 g_signal_connect_swapped (GTK_FILE_SELECTION (filesel)->cancel_button,
178 G_CALLBACK (gtk_widget_destroy), filesel);
180 g_signal_connect (filesel, "destroy",
181 G_CALLBACK (gtk_main_quit), NULL);
182 gtk_window_set_modal (GTK_WINDOW (filesel), TRUE);
184 gtk_widget_show (filesel);
191 * MsgBox utility functions
195 msgbox_yes_cb (GtkWidget *widget, gboolean *result)
198 gtk_object_destroy (GTK_OBJECT (gtk_widget_get_toplevel (widget)));
202 msgbox_no_cb (GtkWidget *widget, gboolean *result)
205 gtk_object_destroy (GTK_OBJECT (gtk_widget_get_toplevel (widget)));
209 msgbox_key_press_cb (GtkWidget *widget, GdkEventKey *event, gpointer data)
211 if (event->keyval == GDK_Escape)
213 g_signal_stop_emission_by_name (widget, "key_press_event");
214 gtk_object_destroy (GTK_OBJECT (widget));
221 /* Don't copy this example, it's all crack-smoking - you can just use
222 * GtkMessageDialog now
225 msgbox_run (GtkWindow *parent,
227 const char *yes_button,
228 const char *no_button,
229 const char *cancel_button,
232 gboolean result = -1;
237 GtkWidget *button_box;
238 GtkWidget *separator;
240 g_return_val_if_fail (message != NULL, FALSE);
241 g_return_val_if_fail (default_index >= 0 && default_index <= 1, FALSE);
244 parent = get_active_window ();
248 dialog = gtk_window_new (GTK_WINDOW_TOPLEVEL);
249 gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
251 gtk_window_set_transient_for (GTK_WINDOW (dialog), parent);
252 gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
254 /* Quit our recursive main loop when the dialog is destroyed.
256 g_signal_connect (dialog, "destroy",
257 G_CALLBACK (gtk_main_quit), NULL);
259 /* Catch Escape key presses and have them destroy the dialog
261 g_signal_connect (dialog, "key_press_event",
262 G_CALLBACK (msgbox_key_press_cb), NULL);
264 /* Fill in the contents of the widget
266 vbox = gtk_vbox_new (FALSE, 0);
267 gtk_container_add (GTK_CONTAINER (dialog), vbox);
269 label = gtk_label_new (message);
270 gtk_misc_set_padding (GTK_MISC (label), 12, 12);
271 gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
272 gtk_box_pack_start (GTK_BOX (vbox), label, TRUE, TRUE, 0);
274 separator = gtk_hseparator_new ();
275 gtk_box_pack_start (GTK_BOX (vbox), separator, FALSE, FALSE, 0);
277 button_box = gtk_hbutton_box_new ();
278 gtk_box_pack_start (GTK_BOX (vbox), button_box, FALSE, FALSE, 0);
279 gtk_container_set_border_width (GTK_CONTAINER (button_box), 8);
282 /* When Yes is clicked, call the msgbox_yes_cb
283 * This sets the result variable and destroys the dialog
287 button = gtk_button_new_with_label (yes_button);
288 GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
289 gtk_container_add (GTK_CONTAINER (button_box), button);
291 if (default_index == 0)
292 gtk_widget_grab_default (button);
294 g_signal_connect (button, "clicked",
295 G_CALLBACK (msgbox_yes_cb), &result);
298 /* When No is clicked, call the msgbox_no_cb
299 * This sets the result variable and destroys the dialog
303 button = gtk_button_new_with_label (no_button);
304 GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
305 gtk_container_add (GTK_CONTAINER (button_box), button);
307 if (default_index == 0)
308 gtk_widget_grab_default (button);
310 g_signal_connect (button, "clicked",
311 G_CALLBACK (msgbox_no_cb), &result);
314 /* When Cancel is clicked, destroy the dialog
318 button = gtk_button_new_with_label (cancel_button);
319 GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
320 gtk_container_add (GTK_CONTAINER (button_box), button);
322 if (default_index == 1)
323 gtk_widget_grab_default (button);
325 g_signal_connect_swapped (button, "clicked",
326 G_CALLBACK (gtk_object_destroy), dialog);
329 gtk_widget_show_all (dialog);
331 /* Run a recursive main loop until a button is clicked
332 * or the user destroys the dialog through the window mananger */
340 * Example buffer filling code
343 blink_timeout (gpointer data)
346 static gboolean flip = FALSE;
348 tag = GTK_TEXT_TAG (data);
351 "foreground", flip ? "blue" : "purple",
361 tag_event_handler (GtkTextTag *tag, GtkWidget *widget, GdkEvent *event,
362 const GtkTextIter *iter, gpointer user_data)
366 char_index = gtk_text_iter_get_offset (iter);
370 case GDK_MOTION_NOTIFY:
371 printf ("Motion event at char %d tag `%s'\n",
372 char_index, tag->name);
375 case GDK_BUTTON_PRESS:
376 printf ("Button press at char %d tag `%s'\n",
377 char_index, tag->name);
380 case GDK_2BUTTON_PRESS:
381 printf ("Double click at char %d tag `%s'\n",
382 char_index, tag->name);
385 case GDK_3BUTTON_PRESS:
386 printf ("Triple click at char %d tag `%s'\n",
387 char_index, tag->name);
390 case GDK_BUTTON_RELEASE:
391 printf ("Button release at char %d tag `%s'\n",
392 char_index, tag->name);
396 case GDK_KEY_RELEASE:
397 printf ("Key event at char %d tag `%s'\n",
398 char_index, tag->name);
401 case GDK_ENTER_NOTIFY:
402 case GDK_LEAVE_NOTIFY:
403 case GDK_PROPERTY_NOTIFY:
404 case GDK_SELECTION_CLEAR:
405 case GDK_SELECTION_REQUEST:
406 case GDK_SELECTION_NOTIFY:
407 case GDK_PROXIMITY_IN:
408 case GDK_PROXIMITY_OUT:
411 case GDK_DRAG_MOTION:
412 case GDK_DRAG_STATUS:
414 case GDK_DROP_FINISHED:
423 setup_tag (GtkTextTag *tag)
425 g_signal_connect (tag,
427 G_CALLBACK (tag_event_handler),
431 static const char *book_closed_xpm[] = {
457 fill_example_buffer (GtkTextBuffer *buffer)
459 GtkTextIter iter, iter2;
461 GtkTextChildAnchor *anchor;
468 /* FIXME this is broken if called twice on a buffer, since
469 * we try to create tags a second time.
472 tag = gtk_text_buffer_create_tag (buffer, "fg_blue", NULL);
475 gtk_timeout_add (1000, blink_timeout, tag);
480 color.red = color.green = 0;
486 "foreground_gdk", &color,
487 "background_gdk", &color2,
491 tag = gtk_text_buffer_create_tag (buffer, "fg_red", NULL);
495 color.blue = color.green = 0;
498 "rise", -4 * PANGO_SCALE,
499 "foreground_gdk", &color,
502 tag = gtk_text_buffer_create_tag (buffer, "bg_green", NULL);
506 color.blue = color.red = 0;
507 color.green = 0xffff;
509 "background_gdk", &color,
513 tag = gtk_text_buffer_create_tag (buffer, "strikethrough", NULL);
518 "strikethrough", TRUE,
522 tag = gtk_text_buffer_create_tag (buffer, "underline", NULL);
527 "underline", PANGO_UNDERLINE_SINGLE,
530 tag = gtk_text_buffer_create_tag (buffer, "underline_error", NULL);
535 "underline", PANGO_UNDERLINE_ERROR,
538 tag = gtk_text_buffer_create_tag (buffer, "centered", NULL);
541 "justification", GTK_JUSTIFY_CENTER,
544 tag = gtk_text_buffer_create_tag (buffer, "rtl_quote", NULL);
547 "wrap_mode", GTK_WRAP_WORD,
548 "direction", GTK_TEXT_DIR_RTL,
555 tag = gtk_text_buffer_create_tag (buffer, "negative_indent", NULL);
561 gtk_text_buffer_get_iter_at_offset (buffer, &iter, 0);
563 anchor = gtk_text_buffer_create_child_anchor (buffer, &iter);
565 g_object_ref (anchor);
567 g_object_set_data_full (G_OBJECT (buffer), "anchor", anchor,
568 (GDestroyNotify) g_object_unref);
570 pixbuf = gdk_pixbuf_new_from_xpm_data (book_closed_xpm);
575 GtkTextMark * temp_mark;
577 gtk_text_buffer_get_iter_at_offset (buffer, &iter, 0);
579 gtk_text_buffer_insert_pixbuf (buffer, &iter, pixbuf);
581 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",
584 gtk_text_buffer_insert (buffer, &iter, str, -1);
588 gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, 0, 5);
590 gtk_text_buffer_insert (buffer, &iter,
591 "(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"
592 /* This is UTF8 stuff, Emacs doesn't
593 really know how to display it */
594 "German (Deutsch S\303\274d) Gr\303\274\303\237 Gott Greek (\316\225\316\273\316\273\316\267\316\275\316\271\316\272\316\254) \316\223\316\265\316\271\316\254 \317\203\316\261\317\202 Hebrew(\327\251\327\234\327\225\327\235) Hebrew punctuation(\xd6\xbf\327\251\xd6\xbb\xd6\xbc\xd6\xbb\xd6\xbf\327\234\xd6\xbc\327\225\xd6\xbc\xd6\xbb\xd6\xbb\xd6\xbf\327\235\xd6\xbc\xd6\xbb\xd6\xbf) Japanese (\346\227\245\346\234\254\350\252\236) Thai (\340\270\252\340\270\247\340\270\261\340\270\252\340\270\224\340\270\265\340\270\204\340\270\243\340\270\261\340\270\232) Thai wrong spelling (\340\270\204\340\270\263\340\270\225\340\271\210\340\270\255\340\271\204\340\270\233\340\270\231\340\270\267\340\271\210\340\270\252\340\270\260\340\270\201\340\270\224\340\270\234\340\270\264\340\270\224 \340\270\236\340\270\261\340\270\261\340\271\211\340\270\261\340\270\261\340\271\210\340\270\207\340\271\202\340\270\201\340\270\260)\n", -1);
597 gtk_text_buffer_create_mark (buffer, "tmp_mark", &iter, TRUE);
600 gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, 0, 6);
601 gtk_text_buffer_get_iter_at_line_offset (buffer, &iter2, 0, 13);
603 gtk_text_buffer_apply_tag_by_name (buffer, "fg_blue", &iter, &iter2);
605 gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, 1, 10);
606 gtk_text_buffer_get_iter_at_line_offset (buffer, &iter2, 1, 16);
608 gtk_text_buffer_apply_tag_by_name (buffer, "underline", &iter, &iter2);
610 gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, 1, 4);
611 gtk_text_buffer_get_iter_at_line_offset (buffer, &iter2, 1, 7);
613 gtk_text_buffer_apply_tag_by_name (buffer, "underline_error", &iter, &iter2);
615 gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, 1, 14);
616 gtk_text_buffer_get_iter_at_line_offset (buffer, &iter2, 1, 24);
618 gtk_text_buffer_apply_tag_by_name (buffer, "strikethrough", &iter, &iter2);
620 gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, 0, 9);
621 gtk_text_buffer_get_iter_at_line_offset (buffer, &iter2, 0, 16);
623 gtk_text_buffer_apply_tag_by_name (buffer, "bg_green", &iter, &iter2);
625 gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, 4, 2);
626 gtk_text_buffer_get_iter_at_line_offset (buffer, &iter2, 4, 10);
628 gtk_text_buffer_apply_tag_by_name (buffer, "bg_green", &iter, &iter2);
630 gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, 4, 8);
631 gtk_text_buffer_get_iter_at_line_offset (buffer, &iter2, 4, 15);
633 gtk_text_buffer_apply_tag_by_name (buffer, "fg_red", &iter, &iter2);
636 gtk_text_buffer_get_iter_at_mark (buffer, &iter, temp_mark);
637 gtk_text_buffer_insert (buffer, &iter, "Centered text!\n", -1);
639 gtk_text_buffer_get_iter_at_mark (buffer, &iter2, temp_mark);
640 gtk_text_buffer_apply_tag_by_name (buffer, "centered", &iter2, &iter);
642 gtk_text_buffer_move_mark (buffer, temp_mark, &iter);
643 gtk_text_buffer_insert (buffer, &iter, "Word wrapped, Right-to-left Quote\n", -1);
644 gtk_text_buffer_insert (buffer, &iter, "\331\210\331\202\330\257 \330\250\330\257\330\243 \330\253\331\204\330\247\330\253 \331\205\331\206 \330\243\331\203\330\253\330\261 \330\247\331\204\331\205\330\244\330\263\330\263\330\247\330\252 \330\252\331\202\330\257\331\205\330\247 \331\201\331\212 \330\264\330\250\331\203\330\251 \330\247\331\203\330\263\331\212\331\210\331\206 \330\250\330\261\330\247\331\205\330\254\331\207\330\247 \331\203\331\205\331\206\330\270\331\205\330\247\330\252 \331\204\330\247 \330\252\330\263\330\271\331\211 \331\204\331\204\330\261\330\250\330\255\330\214 \330\253\331\205 \330\252\330\255\331\210\331\204\330\252 \331\201\331\212 \330\247\331\204\330\263\331\206\331\210\330\247\330\252 \330\247\331\204\330\256\331\205\330\263 \330\247\331\204\331\205\330\247\330\266\331\212\330\251 \330\245\331\204\331\211 \331\205\330\244\330\263\330\263\330\247\330\252 \331\205\330\247\331\204\331\212\330\251 \331\205\331\206\330\270\331\205\330\251\330\214 \331\210\330\250\330\247\330\252\330\252 \330\254\330\262\330\241\330\247 \331\205\331\206 \330\247\331\204\331\206\330\270\330\247\331\205 \330\247\331\204\331\205\330\247\331\204\331\212 \331\201\331\212 \330\250\331\204\330\257\330\247\331\206\331\207\330\247\330\214 \331\210\331\204\331\203\331\206\331\207\330\247 \330\252\330\252\330\256\330\265\330\265 \331\201\331\212 \330\256\330\257\331\205\330\251 \331\202\330\267\330\247\330\271 \330\247\331\204\331\205\330\264\330\261\331\210\330\271\330\247\330\252 \330\247\331\204\330\265\330\272\331\212\330\261\330\251. \331\210\330\243\330\255\330\257 \330\243\331\203\330\253\330\261 \331\207\330\260\331\207 \330\247\331\204\331\205\330\244\330\263\330\263\330\247\330\252 \331\206\330\254\330\247\330\255\330\247 \331\207\331\210 \302\273\330\250\330\247\331\206\331\203\331\210\330\263\331\210\331\204\302\253 \331\201\331\212 \330\250\331\210\331\204\331\212\331\201\331\212\330\247.\n", -1);
645 gtk_text_buffer_get_iter_at_mark (buffer, &iter2, temp_mark);
646 gtk_text_buffer_apply_tag_by_name (buffer, "rtl_quote", &iter2, &iter);
648 gtk_text_buffer_insert_with_tags (buffer, &iter,
649 "Paragraph with negative indentation. blah blah blah blah blah. The quick brown fox jumped over the lazy dog.\n",
651 gtk_text_tag_table_lookup (gtk_text_buffer_get_tag_table (buffer),
658 g_object_unref (pixbuf);
660 printf ("%d lines %d chars\n",
661 gtk_text_buffer_get_line_count (buffer),
662 gtk_text_buffer_get_char_count (buffer));
664 /* Move cursor to start */
665 gtk_text_buffer_get_iter_at_offset (buffer, &iter, 0);
666 gtk_text_buffer_place_cursor (buffer, &iter);
668 gtk_text_buffer_set_modified (buffer, FALSE);
672 fill_file_buffer (GtkTextBuffer *buffer, const char *filename)
677 GtkTextIter iter, end;
679 f = fopen (filename, "r");
683 gchar *err = g_strdup_printf ("Cannot open file '%s': %s",
684 filename, g_strerror (errno));
685 msgbox_run (NULL, err, "OK", NULL, NULL, 0);
690 gtk_text_buffer_get_iter_at_offset (buffer, &iter, 0);
694 const char *leftover;
695 int to_read = 2047 - remaining;
697 count = fread (buf + remaining, 1, to_read, f);
698 buf[count + remaining] = '\0';
700 g_utf8_validate (buf, count + remaining, &leftover);
702 g_assert (g_utf8_validate (buf, leftover - buf, NULL));
703 gtk_text_buffer_insert (buffer, &iter, buf, leftover - buf);
705 remaining = (buf + remaining + count) - leftover;
706 g_memmove (buf, leftover, remaining);
708 if (remaining > 6 || count < to_read)
714 gchar *err = g_strdup_printf ("Invalid UTF-8 data encountered reading file '%s'", filename);
715 msgbox_run (NULL, err, "OK", NULL, NULL, 0);
719 /* We had a newline in the buffer to begin with. (The buffer always contains
720 * a newline, so we delete to the end of the buffer to clean up.
722 gtk_text_buffer_get_end_iter (buffer, &end);
723 gtk_text_buffer_delete (buffer, &iter, &end);
725 gtk_text_buffer_set_modified (buffer, FALSE);
731 delete_event_cb (GtkWidget *window, GdkEventAny *event, gpointer data)
733 View *view = view_from_widget (window);
735 push_active_window (GTK_WINDOW (window));
736 check_close_view (view);
737 pop_active_window ();
747 get_empty_view (View *view)
749 if (!view->buffer->filename &&
750 !gtk_text_buffer_get_modified (view->buffer->buffer))
753 return create_view (create_buffer ());
757 view_from_widget (GtkWidget *widget)
759 if (GTK_IS_MENU_ITEM (widget))
761 GtkItemFactory *item_factory = gtk_item_factory_from_widget (widget);
762 return g_object_get_data (G_OBJECT (item_factory), "view");
766 GtkWidget *app = gtk_widget_get_toplevel (widget);
767 return g_object_get_data (G_OBJECT (app), "view");
772 do_new (gpointer callback_data,
773 guint callback_action,
776 create_view (create_buffer ());
780 do_new_view (gpointer callback_data,
781 guint callback_action,
784 View *view = view_from_widget (widget);
786 create_view (view->buffer);
790 open_ok_func (const char *filename, gpointer data)
793 View *new_view = get_empty_view (view);
795 if (!fill_file_buffer (new_view->buffer->buffer, filename))
797 if (new_view != view)
798 close_view (new_view);
803 g_free (new_view->buffer->filename);
804 new_view->buffer->filename = g_strdup (filename);
805 buffer_filename_set (new_view->buffer);
812 do_open (gpointer callback_data,
813 guint callback_action,
816 View *view = view_from_widget (widget);
818 push_active_window (GTK_WINDOW (view->window));
819 filesel_run (NULL, "Open File", NULL, open_ok_func, view);
820 pop_active_window ();
824 do_save_as (gpointer callback_data,
825 guint callback_action,
828 View *view = view_from_widget (widget);
830 push_active_window (GTK_WINDOW (view->window));
831 save_as_buffer (view->buffer);
832 pop_active_window ();
836 do_save (gpointer callback_data,
837 guint callback_action,
840 View *view = view_from_widget (widget);
842 push_active_window (GTK_WINDOW (view->window));
843 if (!view->buffer->filename)
844 do_save_as (callback_data, callback_action, widget);
846 save_buffer (view->buffer);
847 pop_active_window ();
851 do_close (gpointer callback_data,
852 guint callback_action,
855 View *view = view_from_widget (widget);
857 push_active_window (GTK_WINDOW (view->window));
858 check_close_view (view);
859 pop_active_window ();
863 do_exit (gpointer callback_data,
864 guint callback_action,
867 View *view = view_from_widget (widget);
869 GSList *tmp_list = buffers;
871 push_active_window (GTK_WINDOW (view->window));
874 if (!check_buffer_saved (tmp_list->data))
877 tmp_list = tmp_list->next;
881 pop_active_window ();
885 do_example (gpointer callback_data,
886 guint callback_action,
889 View *view = view_from_widget (widget);
892 new_view = get_empty_view (view);
894 fill_example_buffer (new_view->buffer->buffer);
896 view_add_example_widgets (new_view);
901 do_insert_and_scroll (gpointer callback_data,
902 guint callback_action,
905 View *view = view_from_widget (widget);
906 GtkTextBuffer *buffer;
907 GtkTextIter start, end;
910 buffer = view->buffer->buffer;
912 gtk_text_buffer_get_bounds (buffer, &start, &end);
913 mark = gtk_text_buffer_create_mark (buffer, NULL, &end, /* right grav */ FALSE);
915 gtk_text_buffer_insert (buffer, &end,
916 "Hello this is multiple lines of text\n"
917 "Line 1\n" "Line 2\n"
918 "Line 3\n" "Line 4\n"
922 gtk_text_view_scroll_to_mark (GTK_TEXT_VIEW (view->text_view), mark,
924 gtk_text_buffer_delete_mark (buffer, mark);
928 do_wrap_changed (gpointer callback_data,
929 guint callback_action,
932 View *view = view_from_widget (widget);
934 gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (view->text_view), callback_action);
938 do_direction_changed (gpointer callback_data,
939 guint callback_action,
942 View *view = view_from_widget (widget);
944 gtk_widget_set_direction (view->text_view, callback_action);
945 gtk_widget_queue_resize (view->text_view);
950 do_spacing_changed (gpointer callback_data,
951 guint callback_action,
954 View *view = view_from_widget (widget);
958 gtk_text_view_set_pixels_above_lines (GTK_TEXT_VIEW (view->text_view),
960 gtk_text_view_set_pixels_below_lines (GTK_TEXT_VIEW (view->text_view),
962 gtk_text_view_set_pixels_inside_wrap (GTK_TEXT_VIEW (view->text_view),
967 gtk_text_view_set_pixels_above_lines (GTK_TEXT_VIEW (view->text_view),
969 gtk_text_view_set_pixels_below_lines (GTK_TEXT_VIEW (view->text_view),
971 gtk_text_view_set_pixels_inside_wrap (GTK_TEXT_VIEW (view->text_view),
977 do_editable_changed (gpointer callback_data,
978 guint callback_action,
981 View *view = view_from_widget (widget);
983 gtk_text_view_set_editable (GTK_TEXT_VIEW (view->text_view), callback_action);
987 do_cursor_visible_changed (gpointer callback_data,
988 guint callback_action,
991 View *view = view_from_widget (widget);
993 gtk_text_view_set_cursor_visible (GTK_TEXT_VIEW (view->text_view), callback_action);
997 do_color_cycle_changed (gpointer callback_data,
998 guint callback_action,
1001 View *view = view_from_widget (widget);
1003 buffer_set_colors (view->buffer, callback_action);
1007 do_apply_editable (gpointer callback_data,
1008 guint callback_action,
1011 View *view = view_from_widget (widget);
1015 if (gtk_text_buffer_get_selection_bounds (view->buffer->buffer,
1018 if (callback_action)
1020 gtk_text_buffer_remove_tag (view->buffer->buffer,
1021 view->buffer->not_editable_tag,
1026 gtk_text_buffer_apply_tag (view->buffer->buffer,
1027 view->buffer->not_editable_tag,
1034 do_apply_invisible (gpointer callback_data,
1035 guint callback_action,
1038 View *view = view_from_widget (widget);
1042 if (gtk_text_buffer_get_selection_bounds (view->buffer->buffer,
1045 if (callback_action)
1047 gtk_text_buffer_remove_tag (view->buffer->buffer,
1048 view->buffer->invisible_tag,
1053 gtk_text_buffer_apply_tag (view->buffer->buffer,
1054 view->buffer->invisible_tag,
1061 do_apply_rise (gpointer callback_data,
1062 guint callback_action,
1065 View *view = view_from_widget (widget);
1069 if (gtk_text_buffer_get_selection_bounds (view->buffer->buffer,
1072 if (callback_action)
1074 gtk_text_buffer_remove_tag (view->buffer->buffer,
1075 view->buffer->rise_tag,
1080 gtk_text_buffer_apply_tag (view->buffer->buffer,
1081 view->buffer->rise_tag,
1088 do_apply_large (gpointer callback_data,
1089 guint callback_action,
1092 View *view = view_from_widget (widget);
1096 if (gtk_text_buffer_get_selection_bounds (view->buffer->buffer,
1099 if (callback_action)
1101 gtk_text_buffer_remove_tag (view->buffer->buffer,
1102 view->buffer->large_tag,
1107 gtk_text_buffer_apply_tag (view->buffer->buffer,
1108 view->buffer->large_tag,
1115 do_apply_indent (gpointer callback_data,
1116 guint callback_action,
1119 View *view = view_from_widget (widget);
1123 if (gtk_text_buffer_get_selection_bounds (view->buffer->buffer,
1126 if (callback_action)
1128 gtk_text_buffer_remove_tag (view->buffer->buffer,
1129 view->buffer->indent_tag,
1134 gtk_text_buffer_apply_tag (view->buffer->buffer,
1135 view->buffer->indent_tag,
1142 do_apply_margin (gpointer callback_data,
1143 guint callback_action,
1146 View *view = view_from_widget (widget);
1150 if (gtk_text_buffer_get_selection_bounds (view->buffer->buffer,
1153 if (callback_action)
1155 gtk_text_buffer_remove_tag (view->buffer->buffer,
1156 view->buffer->margin_tag,
1161 gtk_text_buffer_apply_tag (view->buffer->buffer,
1162 view->buffer->margin_tag,
1169 do_apply_tabs (gpointer callback_data,
1170 guint callback_action,
1173 View *view = view_from_widget (widget);
1177 if (gtk_text_buffer_get_selection_bounds (view->buffer->buffer,
1180 if (callback_action)
1182 gtk_text_buffer_remove_tag (view->buffer->buffer,
1183 view->buffer->custom_tabs_tag,
1188 gtk_text_buffer_apply_tag (view->buffer->buffer,
1189 view->buffer->custom_tabs_tag,
1196 do_apply_colors (gpointer callback_data,
1197 guint callback_action,
1200 View *view = view_from_widget (widget);
1201 Buffer *buffer = view->buffer;
1205 if (gtk_text_buffer_get_selection_bounds (view->buffer->buffer,
1208 if (!callback_action)
1212 tmp = buffer->color_tags;
1215 gtk_text_buffer_remove_tag (view->buffer->buffer,
1218 tmp = g_slist_next (tmp);
1225 tmp = buffer->color_tags;
1229 gboolean done = FALSE;
1232 gtk_text_iter_forward_char (&next);
1233 gtk_text_iter_forward_char (&next);
1235 if (gtk_text_iter_compare (&next, &end) >= 0)
1241 gtk_text_buffer_apply_tag (view->buffer->buffer,
1250 tmp = g_slist_next (tmp);
1252 tmp = buffer->color_tags;
1259 do_remove_tags (gpointer callback_data,
1260 guint callback_action,
1263 View *view = view_from_widget (widget);
1267 if (gtk_text_buffer_get_selection_bounds (view->buffer->buffer,
1270 gtk_text_buffer_remove_all_tags (view->buffer->buffer,
1276 do_properties (gpointer callback_data,
1277 guint callback_action,
1280 View *view = view_from_widget (widget);
1282 create_prop_editor (G_OBJECT (view->text_view), 0);
1292 dialog_response_callback (GtkWidget *dialog, gint response_id, gpointer data)
1294 GtkTextBuffer *buffer;
1296 GtkTextIter start, end;
1297 gchar *search_string;
1299 if (response_id != RESPONSE_FORWARD &&
1300 response_id != RESPONSE_BACKWARD)
1302 gtk_widget_destroy (dialog);
1306 buffer = g_object_get_data (G_OBJECT (dialog), "buffer");
1308 gtk_text_buffer_get_bounds (buffer, &start, &end);
1310 search_string = gtk_text_iter_get_text (&start, &end);
1312 g_print ("Searching for `%s'\n", search_string);
1314 if (response_id == RESPONSE_FORWARD)
1315 buffer_search_forward (view->buffer, search_string, view);
1316 else if (response_id == RESPONSE_BACKWARD)
1317 buffer_search_backward (view->buffer, search_string, view);
1319 g_free (search_string);
1321 gtk_widget_destroy (dialog);
1325 do_search (gpointer callback_data,
1326 guint callback_action,
1329 View *view = view_from_widget (widget);
1331 GtkWidget *search_text;
1332 GtkTextBuffer *buffer;
1334 dialog = gtk_dialog_new_with_buttons ("Search",
1335 GTK_WINDOW (view->window),
1336 GTK_DIALOG_DESTROY_WITH_PARENT,
1337 "Forward", RESPONSE_FORWARD,
1338 "Backward", RESPONSE_BACKWARD,
1340 GTK_RESPONSE_NONE, NULL);
1343 buffer = gtk_text_buffer_new (NULL);
1345 search_text = gtk_text_view_new_with_buffer (buffer);
1347 g_object_unref (buffer);
1349 gtk_box_pack_end (GTK_BOX (GTK_DIALOG (dialog)->vbox),
1353 g_object_set_data (G_OBJECT (dialog), "buffer", buffer);
1355 g_signal_connect (dialog,
1357 G_CALLBACK (dialog_response_callback),
1360 gtk_widget_show (search_text);
1362 gtk_widget_grab_focus (search_text);
1364 gtk_widget_show_all (dialog);
1368 do_select_all (gpointer callback_data,
1369 guint callback_action,
1372 View *view = view_from_widget (widget);
1373 GtkTextBuffer *buffer;
1374 GtkTextIter start, end;
1376 buffer = view->buffer->buffer;
1378 gtk_text_buffer_get_bounds (buffer, &start, &end);
1379 gtk_text_buffer_select_range (buffer, &start, &end);
1384 /* position is in coordinate system of text_view_move_child */
1393 movable_child_callback (GtkWidget *child,
1397 ChildMoveInfo *info;
1398 GtkTextView *text_view;
1400 text_view = GTK_TEXT_VIEW (data);
1402 g_return_val_if_fail (GTK_IS_EVENT_BOX (child), FALSE);
1403 g_return_val_if_fail (gtk_widget_get_parent (child) == GTK_WIDGET (text_view), FALSE);
1405 info = g_object_get_data (G_OBJECT (child),
1406 "testtext-move-info");
1410 info = g_new (ChildMoveInfo, 1);
1414 g_object_set_data_full (G_OBJECT (child),
1415 "testtext-move-info",
1420 switch (event->type)
1422 case GDK_BUTTON_PRESS:
1423 if (info->button < 0)
1425 if (gdk_pointer_grab (event->button.window,
1427 GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK |
1428 GDK_BUTTON_RELEASE_MASK,
1431 event->button.time) != GDK_GRAB_SUCCESS)
1434 info->button = event->button.button;
1436 info->start_x = child->allocation.x;
1437 info->start_y = child->allocation.y;
1438 info->click_x = child->allocation.x + event->button.x;
1439 info->click_y = child->allocation.y + event->button.y;
1443 case GDK_BUTTON_RELEASE:
1444 if (info->button < 0)
1447 if (info->button == event->button.button)
1451 gdk_pointer_ungrab (event->button.time);
1454 /* convert to window coords from event box coords */
1455 x = info->start_x + (event->button.x + child->allocation.x - info->click_x);
1456 y = info->start_y + (event->button.y + child->allocation.y - info->click_y);
1458 gtk_text_view_move_child (text_view,
1464 case GDK_MOTION_NOTIFY:
1468 if (info->button < 0)
1471 gdk_window_get_pointer (child->window, &x, &y, NULL); /* ensure more events */
1473 /* to window coords from event box coords */
1474 x += child->allocation.x;
1475 y += child->allocation.y;
1477 x = info->start_x + (x - info->click_x);
1478 y = info->start_y + (y - info->click_y);
1480 gtk_text_view_move_child (text_view,
1494 add_movable_child (GtkTextView *text_view,
1495 GtkTextWindowType window)
1497 GtkWidget *event_box;
1501 label = gtk_label_new ("Drag me around");
1503 event_box = gtk_event_box_new ();
1504 gtk_widget_add_events (event_box,
1505 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
1506 GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK);
1509 color.green = color.blue = 0;
1510 gtk_widget_modify_bg (event_box, GTK_STATE_NORMAL, &color);
1512 gtk_container_add (GTK_CONTAINER (event_box), label);
1514 gtk_widget_show_all (event_box);
1516 g_signal_connect (event_box, "event",
1517 G_CALLBACK (movable_child_callback),
1520 gtk_text_view_add_child_in_window (text_view,
1527 do_add_children (gpointer callback_data,
1528 guint callback_action,
1531 View *view = view_from_widget (widget);
1533 add_movable_child (GTK_TEXT_VIEW (view->text_view),
1534 GTK_TEXT_WINDOW_WIDGET);
1535 add_movable_child (GTK_TEXT_VIEW (view->text_view),
1536 GTK_TEXT_WINDOW_LEFT);
1537 add_movable_child (GTK_TEXT_VIEW (view->text_view),
1538 GTK_TEXT_WINDOW_RIGHT);
1542 do_add_focus_children (gpointer callback_data,
1543 guint callback_action,
1546 View *view = view_from_widget (widget);
1548 GtkTextChildAnchor *anchor;
1550 GtkTextView *text_view;
1552 text_view = GTK_TEXT_VIEW (view->text_view);
1554 child = gtk_button_new_with_mnemonic ("Button _A in widget->window");
1556 gtk_text_view_add_child_in_window (text_view,
1558 GTK_TEXT_WINDOW_WIDGET,
1561 child = gtk_button_new_with_mnemonic ("Button _B in widget->window");
1563 gtk_text_view_add_child_in_window (text_view,
1565 GTK_TEXT_WINDOW_WIDGET,
1568 child = gtk_button_new_with_mnemonic ("Button _C in left window");
1570 gtk_text_view_add_child_in_window (text_view,
1572 GTK_TEXT_WINDOW_LEFT,
1575 child = gtk_button_new_with_mnemonic ("Button _D in right window");
1577 gtk_text_view_add_child_in_window (text_view,
1579 GTK_TEXT_WINDOW_RIGHT,
1582 gtk_text_buffer_get_start_iter (view->buffer->buffer, &iter);
1584 anchor = gtk_text_buffer_create_child_anchor (view->buffer->buffer, &iter);
1586 child = gtk_button_new_with_mnemonic ("Button _E in buffer");
1588 gtk_text_view_add_child_at_anchor (text_view, child, anchor);
1590 anchor = gtk_text_buffer_create_child_anchor (view->buffer->buffer, &iter);
1592 child = gtk_button_new_with_mnemonic ("Button _F in buffer");
1594 gtk_text_view_add_child_at_anchor (text_view, child, anchor);
1596 anchor = gtk_text_buffer_create_child_anchor (view->buffer->buffer, &iter);
1598 child = gtk_button_new_with_mnemonic ("Button _G in buffer");
1600 gtk_text_view_add_child_at_anchor (text_view, child, anchor);
1602 /* show all the buttons */
1603 gtk_widget_show_all (view->text_view);
1607 view_init_menus (View *view)
1609 GtkTextDirection direction = gtk_widget_get_direction (view->text_view);
1610 GtkWrapMode wrap_mode = gtk_text_view_get_wrap_mode (GTK_TEXT_VIEW (view->text_view));
1611 GtkWidget *menu_item = NULL;
1615 case GTK_TEXT_DIR_LTR:
1616 menu_item = gtk_item_factory_get_widget (view->item_factory, "/Settings/Left-to-Right");
1618 case GTK_TEXT_DIR_RTL:
1619 menu_item = gtk_item_factory_get_widget (view->item_factory, "/Settings/Right-to-Left");
1626 gtk_menu_item_activate (GTK_MENU_ITEM (menu_item));
1631 menu_item = gtk_item_factory_get_widget (view->item_factory, "/Settings/Wrap Off");
1634 menu_item = gtk_item_factory_get_widget (view->item_factory, "/Settings/Wrap Words");
1637 menu_item = gtk_item_factory_get_widget (view->item_factory, "/Settings/Wrap Chars");
1644 gtk_menu_item_activate (GTK_MENU_ITEM (menu_item));
1647 static GtkItemFactoryEntry menu_items[] =
1649 { "/_File", NULL, NULL, 0, "<Branch>" },
1650 { "/File/_New", "<control>N", do_new, 0, NULL },
1651 { "/File/New _View", NULL, do_new_view, 0, NULL },
1652 { "/File/_Open", "<control>O", do_open, 0, NULL },
1653 { "/File/_Save", "<control>S", do_save, 0, NULL },
1654 { "/File/Save _As...", NULL, do_save_as, 0, NULL },
1655 { "/File/sep1", NULL, NULL, 0, "<Separator>" },
1656 { "/File/_Close", "<control>W" , do_close, 0, NULL },
1657 { "/File/E_xit", "<control>Q" , do_exit, 0, NULL },
1659 { "/_Edit", NULL, 0, 0, "<Branch>" },
1660 { "/Edit/Find...", NULL, do_search, 0, NULL },
1661 { "/Edit/Select All", "<control>A", do_select_all, 0, NULL },
1663 { "/_Settings", NULL, NULL, 0, "<Branch>" },
1664 { "/Settings/Wrap _Off", NULL, do_wrap_changed, GTK_WRAP_NONE, "<RadioItem>" },
1665 { "/Settings/Wrap _Words", NULL, do_wrap_changed, GTK_WRAP_WORD, "/Settings/Wrap Off" },
1666 { "/Settings/Wrap _Chars", NULL, do_wrap_changed, GTK_WRAP_CHAR, "/Settings/Wrap Off" },
1667 { "/Settings/sep1", NULL, NULL, 0, "<Separator>" },
1668 { "/Settings/Editable", NULL, do_editable_changed, TRUE, "<RadioItem>" },
1669 { "/Settings/Not editable", NULL, do_editable_changed, FALSE, "/Settings/Editable" },
1670 { "/Settings/sep1", NULL, NULL, 0, "<Separator>" },
1672 { "/Settings/Cursor visible", NULL, do_cursor_visible_changed, TRUE, "<RadioItem>" },
1673 { "/Settings/Cursor not visible", NULL, do_cursor_visible_changed, FALSE, "/Settings/Cursor visible" },
1674 { "/Settings/sep1", NULL, NULL, 0, "<Separator>" },
1676 { "/Settings/Left-to-Right", NULL, do_direction_changed, GTK_TEXT_DIR_LTR, "<RadioItem>" },
1677 { "/Settings/Right-to-Left", NULL, do_direction_changed, GTK_TEXT_DIR_RTL, "/Settings/Left-to-Right" },
1679 { "/Settings/sep1", NULL, NULL, 0, "<Separator>" },
1680 { "/Settings/Sane spacing", NULL, do_spacing_changed, FALSE, "<RadioItem>" },
1681 { "/Settings/Funky spacing", NULL, do_spacing_changed, TRUE, "/Settings/Sane spacing" },
1682 { "/Settings/sep1", NULL, NULL, 0, "<Separator>" },
1683 { "/Settings/Don't cycle color tags", NULL, do_color_cycle_changed, FALSE, "<RadioItem>" },
1684 { "/Settings/Cycle colors", NULL, do_color_cycle_changed, TRUE, "/Settings/Don't cycle color tags" },
1685 { "/_Attributes", NULL, NULL, 0, "<Branch>" },
1686 { "/Attributes/Editable", NULL, do_apply_editable, TRUE, NULL },
1687 { "/Attributes/Not editable", NULL, do_apply_editable, FALSE, NULL },
1688 { "/Attributes/Invisible", NULL, do_apply_invisible, FALSE, NULL },
1689 { "/Attributes/Visible", NULL, do_apply_invisible, TRUE, NULL },
1690 { "/Attributes/Rise", NULL, do_apply_rise, FALSE, NULL },
1691 { "/Attributes/Large", NULL, do_apply_large, FALSE, NULL },
1692 { "/Attributes/Indent", NULL, do_apply_indent, FALSE, NULL },
1693 { "/Attributes/Margins", NULL, do_apply_margin, FALSE, NULL },
1694 { "/Attributes/Custom tabs", NULL, do_apply_tabs, FALSE, NULL },
1695 { "/Attributes/Default tabs", NULL, do_apply_tabs, TRUE, NULL },
1696 { "/Attributes/Color cycles", NULL, do_apply_colors, TRUE, NULL },
1697 { "/Attributes/No colors", NULL, do_apply_colors, FALSE, NULL },
1698 { "/Attributes/Remove all tags", NULL, do_remove_tags, 0, NULL },
1699 { "/Attributes/Properties", NULL, do_properties, 0, NULL },
1700 { "/_Test", NULL, NULL, 0, "<Branch>" },
1701 { "/Test/_Example", NULL, do_example, 0, NULL },
1702 { "/Test/_Insert and scroll", NULL, do_insert_and_scroll, 0, NULL },
1703 { "/Test/_Add fixed children", NULL, do_add_children, 0, NULL },
1704 { "/Test/A_dd focusable children", NULL, do_add_focus_children, 0, NULL },
1708 save_buffer (Buffer *buffer)
1710 GtkTextIter start, end;
1712 gboolean result = FALSE;
1713 gboolean have_backup = FALSE;
1714 gchar *bak_filename;
1717 g_return_val_if_fail (buffer->filename != NULL, FALSE);
1719 bak_filename = g_strconcat (buffer->filename, "~", NULL);
1721 if (rename (buffer->filename, bak_filename) != 0)
1723 if (errno != ENOENT)
1725 gchar *err = g_strdup_printf ("Cannot back up '%s' to '%s': %s",
1726 buffer->filename, bak_filename, g_strerror (errno));
1727 msgbox_run (NULL, err, "OK", NULL, NULL, 0);
1735 file = fopen (buffer->filename, "w");
1738 gchar *err = g_strdup_printf ("Cannot back up '%s' to '%s': %s",
1739 buffer->filename, bak_filename, g_strerror (errno));
1740 msgbox_run (NULL, err, "OK", NULL, NULL, 0);
1744 gtk_text_buffer_get_iter_at_offset (buffer->buffer, &start, 0);
1745 gtk_text_buffer_get_end_iter (buffer->buffer, &end);
1747 chars = gtk_text_buffer_get_slice (buffer->buffer, &start, &end, FALSE);
1749 if (fputs (chars, file) == EOF ||
1750 fclose (file) == EOF)
1752 gchar *err = g_strdup_printf ("Error writing to '%s': %s",
1753 buffer->filename, g_strerror (errno));
1754 msgbox_run (NULL, err, "OK", NULL, NULL, 0);
1762 gtk_text_buffer_set_modified (buffer->buffer, FALSE);
1768 if (!result && have_backup)
1770 if (rename (bak_filename, buffer->filename) != 0)
1772 gchar *err = g_strdup_printf ("Error restoring backup file '%s' to '%s': %s\nBackup left as '%s'",
1773 buffer->filename, bak_filename, g_strerror (errno), bak_filename);
1774 msgbox_run (NULL, err, "OK", NULL, NULL, 0);
1779 g_free (bak_filename);
1785 save_as_ok_func (const char *filename, gpointer data)
1787 Buffer *buffer = data;
1788 char *old_filename = buffer->filename;
1790 if (!buffer->filename || strcmp (filename, buffer->filename) != 0)
1792 struct stat statbuf;
1794 if (stat (filename, &statbuf) == 0)
1796 gchar *err = g_strdup_printf ("Ovewrite existing file '%s'?", filename);
1797 gint result = msgbox_run (NULL, err, "Yes", "No", NULL, 1);
1805 buffer->filename = g_strdup (filename);
1807 if (save_buffer (buffer))
1809 g_free (old_filename);
1810 buffer_filename_set (buffer);
1815 g_free (buffer->filename);
1816 buffer->filename = old_filename;
1822 save_as_buffer (Buffer *buffer)
1824 return filesel_run (NULL, "Save File", NULL, save_as_ok_func, buffer);
1828 check_buffer_saved (Buffer *buffer)
1830 if (gtk_text_buffer_get_modified (buffer->buffer))
1832 char *pretty_name = buffer_pretty_name (buffer);
1833 char *msg = g_strdup_printf ("Save changes to '%s'?", pretty_name);
1836 g_free (pretty_name);
1838 result = msgbox_run (NULL, msg, "Yes", "No", "Cancel", 0);
1842 return save_as_buffer (buffer);
1843 else if (result == 1)
1855 create_buffer (void)
1858 PangoTabArray *tabs;
1861 buffer = g_new (Buffer, 1);
1863 buffer->buffer = gtk_text_buffer_new (NULL);
1865 buffer->refcount = 1;
1866 buffer->filename = NULL;
1867 buffer->untitled_serial = -1;
1869 buffer->color_tags = NULL;
1870 buffer->color_cycle_timeout = 0;
1871 buffer->start_hue = 0.0;
1874 while (i < N_COLORS)
1878 tag = gtk_text_buffer_create_tag (buffer->buffer, NULL, NULL);
1880 buffer->color_tags = g_slist_prepend (buffer->color_tags, tag);
1886 buffer->invisible_tag = gtk_text_buffer_create_tag (buffer->buffer, NULL,
1887 "invisible", TRUE, NULL);
1890 buffer->not_editable_tag =
1891 gtk_text_buffer_create_tag (buffer->buffer, NULL,
1893 "foreground", "purple", NULL);
1895 buffer->found_text_tag = gtk_text_buffer_create_tag (buffer->buffer, NULL,
1896 "foreground", "red", NULL);
1898 buffer->rise_tag = gtk_text_buffer_create_tag (buffer->buffer, NULL,
1899 "rise", 10 * PANGO_SCALE, NULL);
1901 buffer->large_tag = gtk_text_buffer_create_tag (buffer->buffer, NULL,
1902 "scale", PANGO_SCALE_X_LARGE, NULL);
1904 buffer->indent_tag = gtk_text_buffer_create_tag (buffer->buffer, NULL,
1905 "indent", 20, NULL);
1907 buffer->margin_tag = gtk_text_buffer_create_tag (buffer->buffer, NULL,
1908 "left_margin", 20, "right_margin", 20, NULL);
1910 tabs = pango_tab_array_new_with_positions (4,
1915 PANGO_TAB_LEFT, 120);
1917 buffer->custom_tabs_tag = gtk_text_buffer_create_tag (buffer->buffer, NULL,
1919 "foreground", "green", NULL);
1921 pango_tab_array_free (tabs);
1923 buffers = g_slist_prepend (buffers, buffer);
1929 buffer_pretty_name (Buffer *buffer)
1931 if (buffer->filename)
1934 char *result = g_path_get_basename (buffer->filename);
1935 p = strchr (result, '/');
1943 if (buffer->untitled_serial == -1)
1944 buffer->untitled_serial = untitled_serial++;
1946 if (buffer->untitled_serial == 1)
1947 return g_strdup ("Untitled");
1949 return g_strdup_printf ("Untitled #%d", buffer->untitled_serial);
1954 buffer_filename_set (Buffer *buffer)
1956 GSList *tmp_list = views;
1960 View *view = tmp_list->data;
1962 if (view->buffer == buffer)
1963 view_set_title (view);
1965 tmp_list = tmp_list->next;
1970 buffer_search (Buffer *buffer,
1976 GtkTextIter start, end;
1980 /* remove tag from whole buffer */
1981 gtk_text_buffer_get_bounds (buffer->buffer, &start, &end);
1982 gtk_text_buffer_remove_tag (buffer->buffer, buffer->found_text_tag,
1985 gtk_text_buffer_get_iter_at_mark (buffer->buffer, &iter,
1986 gtk_text_buffer_get_mark (buffer->buffer,
1992 GtkTextIter match_start, match_end;
1996 while (gtk_text_iter_forward_search (&iter, str,
1997 GTK_TEXT_SEARCH_VISIBLE_ONLY |
1998 GTK_TEXT_SEARCH_TEXT_ONLY,
1999 &match_start, &match_end,
2003 gtk_text_buffer_apply_tag (buffer->buffer, buffer->found_text_tag,
2004 &match_start, &match_end);
2011 while (gtk_text_iter_backward_search (&iter, str,
2012 GTK_TEXT_SEARCH_VISIBLE_ONLY |
2013 GTK_TEXT_SEARCH_TEXT_ONLY,
2014 &match_start, &match_end,
2018 gtk_text_buffer_apply_tag (buffer->buffer, buffer->found_text_tag,
2019 &match_start, &match_end);
2026 dialog = gtk_message_dialog_new (GTK_WINDOW (view->window),
2027 GTK_DIALOG_DESTROY_WITH_PARENT,
2030 "%d strings found and marked in red",
2033 g_signal_connect_swapped (dialog,
2035 G_CALLBACK (gtk_widget_destroy), dialog);
2037 gtk_widget_show (dialog);
2041 buffer_search_forward (Buffer *buffer, const char *str,
2044 buffer_search (buffer, str, view, TRUE);
2048 buffer_search_backward (Buffer *buffer, const char *str,
2051 buffer_search (buffer, str, view, FALSE);
2055 buffer_ref (Buffer *buffer)
2061 buffer_unref (Buffer *buffer)
2064 if (buffer->refcount == 0)
2066 buffer_set_colors (buffer, FALSE);
2067 buffers = g_slist_remove (buffers, buffer);
2068 g_object_unref (buffer->buffer);
2069 g_free (buffer->filename);
2075 hsv_to_rgb (gdouble *h,
2079 gdouble hue, saturation, value;
2097 f = hue - (int) hue;
2098 p = value * (1.0 - saturation);
2099 q = value * (1.0 - saturation * f);
2100 t = value * (1.0 - saturation * (1.0 - f));
2141 g_assert_not_reached ();
2147 hue_to_color (gdouble hue,
2156 g_return_if_fail (hue <= 1.0);
2158 hsv_to_rgb (&h, &s, &v);
2160 color->red = h * 65535;
2161 color->green = s * 65535;
2162 color->blue = v * 65535;
2167 color_cycle_timeout (gpointer data)
2169 Buffer *buffer = data;
2171 buffer_cycle_colors (buffer);
2177 buffer_set_colors (Buffer *buffer,
2183 if (enabled && buffer->color_cycle_timeout == 0)
2184 buffer->color_cycle_timeout = g_timeout_add (200, color_cycle_timeout, buffer);
2185 else if (!enabled && buffer->color_cycle_timeout != 0)
2187 g_source_remove (buffer->color_cycle_timeout);
2188 buffer->color_cycle_timeout = 0;
2191 tmp = buffer->color_tags;
2198 hue_to_color (hue, &color);
2200 g_object_set (tmp->data,
2201 "foreground_gdk", &color,
2205 g_object_set (tmp->data,
2206 "foreground_set", FALSE,
2209 hue += 1.0 / N_COLORS;
2211 tmp = g_slist_next (tmp);
2216 buffer_cycle_colors (Buffer *buffer)
2219 gdouble hue = buffer->start_hue;
2221 tmp = buffer->color_tags;
2226 hue_to_color (hue, &color);
2228 g_object_set (tmp->data,
2229 "foreground_gdk", &color,
2232 hue += 1.0 / N_COLORS;
2236 tmp = g_slist_next (tmp);
2239 buffer->start_hue += 1.0 / N_COLORS;
2240 if (buffer->start_hue > 1.0)
2241 buffer->start_hue = 0.0;
2245 close_view (View *view)
2247 views = g_slist_remove (views, view);
2248 buffer_unref (view->buffer);
2249 gtk_widget_destroy (view->window);
2250 g_object_unref (view->item_factory);
2259 check_close_view (View *view)
2261 if (view->buffer->refcount > 1 ||
2262 check_buffer_saved (view->buffer))
2267 view_set_title (View *view)
2269 char *pretty_name = buffer_pretty_name (view->buffer);
2270 char *title = g_strconcat ("testtext - ", pretty_name, NULL);
2272 gtk_window_set_title (GTK_WINDOW (view->window), title);
2274 g_free (pretty_name);
2279 cursor_set_callback (GtkTextBuffer *buffer,
2280 const GtkTextIter *location,
2284 GtkTextView *text_view;
2286 /* Redraw tab windows if the cursor moves
2287 * on the mapped widget (windows may not exist before realization...
2290 text_view = GTK_TEXT_VIEW (user_data);
2292 if (GTK_WIDGET_MAPPED (text_view) &&
2293 mark == gtk_text_buffer_get_insert (buffer))
2295 GdkWindow *tab_window;
2297 tab_window = gtk_text_view_get_window (text_view,
2298 GTK_TEXT_WINDOW_TOP);
2300 gdk_window_invalidate_rect (tab_window, NULL, FALSE);
2302 tab_window = gtk_text_view_get_window (text_view,
2303 GTK_TEXT_WINDOW_BOTTOM);
2305 gdk_window_invalidate_rect (tab_window, NULL, FALSE);
2310 tab_stops_expose (GtkWidget *widget,
2311 GdkEventExpose *event,
2318 GdkWindow *bottom_win;
2319 GtkTextView *text_view;
2320 GtkTextWindowType type;
2321 GdkDrawable *target;
2322 gint *positions = NULL;
2324 GtkTextAttributes *attrs;
2326 GtkTextBuffer *buffer;
2329 text_view = GTK_TEXT_VIEW (widget);
2331 /* See if this expose is on the tab stop window */
2332 top_win = gtk_text_view_get_window (text_view,
2333 GTK_TEXT_WINDOW_TOP);
2335 bottom_win = gtk_text_view_get_window (text_view,
2336 GTK_TEXT_WINDOW_BOTTOM);
2338 if (event->window == top_win)
2340 type = GTK_TEXT_WINDOW_TOP;
2343 else if (event->window == bottom_win)
2345 type = GTK_TEXT_WINDOW_BOTTOM;
2346 target = bottom_win;
2351 first_x = event->area.x;
2352 last_x = first_x + event->area.width;
2354 gtk_text_view_window_to_buffer_coords (text_view,
2361 gtk_text_view_window_to_buffer_coords (text_view,
2368 buffer = gtk_text_view_get_buffer (text_view);
2370 gtk_text_buffer_get_iter_at_mark (buffer,
2372 gtk_text_buffer_get_mark (buffer,
2375 attrs = gtk_text_attributes_new ();
2377 gtk_text_iter_get_attributes (&insert, attrs);
2381 size = pango_tab_array_get_size (attrs->tabs);
2383 pango_tab_array_get_tabs (attrs->tabs,
2387 in_pixels = pango_tab_array_get_positions_in_pixels (attrs->tabs);
2395 gtk_text_attributes_unref (attrs);
2403 positions[i] = PANGO_PIXELS (positions[i]);
2405 gtk_text_view_buffer_to_window_coords (text_view,
2412 gdk_draw_line (target,
2413 widget->style->fg_gc [widget->state],
2426 get_lines (GtkTextView *text_view,
2429 GArray *buffer_coords,
2437 g_array_set_size (buffer_coords, 0);
2438 g_array_set_size (numbers, 0);
2440 /* Get iter at first y */
2441 gtk_text_view_get_line_at_y (text_view, &iter, first_y, NULL);
2443 /* For each iter, get its location and add it to the arrays.
2444 * Stop when we pass last_y
2449 while (!gtk_text_iter_is_end (&iter))
2454 gtk_text_view_get_line_yrange (text_view, &iter, &y, &height);
2456 g_array_append_val (buffer_coords, y);
2457 line_num = gtk_text_iter_get_line (&iter);
2458 g_array_append_val (numbers, line_num);
2462 if ((y + height) >= last_y)
2465 gtk_text_iter_forward_line (&iter);
2472 line_numbers_expose (GtkWidget *widget,
2473 GdkEventExpose *event,
2482 GdkWindow *left_win;
2483 GdkWindow *right_win;
2484 PangoLayout *layout;
2485 GtkTextView *text_view;
2486 GtkTextWindowType type;
2487 GdkDrawable *target;
2489 text_view = GTK_TEXT_VIEW (widget);
2491 /* See if this expose is on the line numbers window */
2492 left_win = gtk_text_view_get_window (text_view,
2493 GTK_TEXT_WINDOW_LEFT);
2495 right_win = gtk_text_view_get_window (text_view,
2496 GTK_TEXT_WINDOW_RIGHT);
2498 if (event->window == left_win)
2500 type = GTK_TEXT_WINDOW_LEFT;
2503 else if (event->window == right_win)
2505 type = GTK_TEXT_WINDOW_RIGHT;
2511 first_y = event->area.y;
2512 last_y = first_y + event->area.height;
2514 gtk_text_view_window_to_buffer_coords (text_view,
2521 gtk_text_view_window_to_buffer_coords (text_view,
2528 numbers = g_array_new (FALSE, FALSE, sizeof (gint));
2529 pixels = g_array_new (FALSE, FALSE, sizeof (gint));
2531 get_lines (text_view,
2538 /* Draw fully internationalized numbers! */
2540 layout = gtk_widget_create_pango_layout (widget, "");
2548 gtk_text_view_buffer_to_window_coords (text_view,
2551 g_array_index (pixels, gint, i),
2555 str = g_strdup_printf ("%d", g_array_index (numbers, gint, i));
2557 pango_layout_set_text (layout, str, -1);
2559 gtk_paint_layout (widget->style,
2561 GTK_WIDGET_STATE (widget),
2574 g_array_free (pixels, TRUE);
2575 g_array_free (numbers, TRUE);
2577 g_object_unref (layout);
2579 /* don't stop emission, need to draw children */
2584 create_view (Buffer *buffer)
2591 view = g_new0 (View, 1);
2592 views = g_slist_prepend (views, view);
2594 view->buffer = buffer;
2595 buffer_ref (buffer);
2597 view->window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2598 g_object_set_data (G_OBJECT (view->window), "view", view);
2600 g_signal_connect (view->window, "delete_event",
2601 G_CALLBACK (delete_event_cb), NULL);
2603 view->accel_group = gtk_accel_group_new ();
2604 view->item_factory = gtk_item_factory_new (GTK_TYPE_MENU_BAR, "<main>", view->accel_group);
2605 g_object_set_data (G_OBJECT (view->item_factory), "view", view);
2607 gtk_item_factory_create_items (view->item_factory, G_N_ELEMENTS (menu_items), menu_items, view);
2609 gtk_window_add_accel_group (GTK_WINDOW (view->window), view->accel_group);
2611 vbox = gtk_vbox_new (FALSE, 0);
2612 gtk_container_add (GTK_CONTAINER (view->window), vbox);
2614 gtk_box_pack_start (GTK_BOX (vbox),
2615 gtk_item_factory_get_widget (view->item_factory, "<main>"),
2618 sw = gtk_scrolled_window_new (NULL, NULL);
2619 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
2620 GTK_POLICY_AUTOMATIC,
2621 GTK_POLICY_AUTOMATIC);
2623 view->text_view = gtk_text_view_new_with_buffer (buffer->buffer);
2624 gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (view->text_view),
2627 /* Make sure border width works, no real reason to do this other than testing */
2628 gtk_container_set_border_width (GTK_CONTAINER (view->text_view),
2631 /* Draw tab stops in the top and bottom windows. */
2633 gtk_text_view_set_border_window_size (GTK_TEXT_VIEW (view->text_view),
2634 GTK_TEXT_WINDOW_TOP,
2637 gtk_text_view_set_border_window_size (GTK_TEXT_VIEW (view->text_view),
2638 GTK_TEXT_WINDOW_BOTTOM,
2641 g_signal_connect (view->text_view,
2643 G_CALLBACK (tab_stops_expose),
2646 g_signal_connect (view->buffer->buffer,
2648 G_CALLBACK (cursor_set_callback),
2651 /* Draw line numbers in the side windows; we should really be
2652 * more scientific about what width we set them to.
2654 gtk_text_view_set_border_window_size (GTK_TEXT_VIEW (view->text_view),
2655 GTK_TEXT_WINDOW_RIGHT,
2658 gtk_text_view_set_border_window_size (GTK_TEXT_VIEW (view->text_view),
2659 GTK_TEXT_WINDOW_LEFT,
2662 g_signal_connect (view->text_view,
2664 G_CALLBACK (line_numbers_expose),
2667 gtk_box_pack_start (GTK_BOX (vbox), sw, TRUE, TRUE, 0);
2668 gtk_container_add (GTK_CONTAINER (sw), view->text_view);
2670 gtk_window_set_default_size (GTK_WINDOW (view->window), 500, 500);
2672 gtk_widget_grab_focus (view->text_view);
2674 view_set_title (view);
2675 view_init_menus (view);
2677 view_add_example_widgets (view);
2679 gtk_widget_show_all (view->window);
2684 view_add_example_widgets (View *view)
2686 GtkTextChildAnchor *anchor;
2689 buffer = view->buffer;
2691 anchor = g_object_get_data (G_OBJECT (buffer->buffer),
2694 if (anchor && !gtk_text_child_anchor_get_deleted (anchor))
2698 widget = gtk_button_new_with_label ("Foo");
2700 gtk_text_view_add_child_at_anchor (GTK_TEXT_VIEW (view->text_view),
2704 gtk_widget_show (widget);
2711 if (g_file_test ("../gdk-pixbuf/libpixbufloader-pnm.la",
2712 G_FILE_TEST_EXISTS))
2714 g_setenv ("GDK_PIXBUF_MODULE_FILE", "../gdk-pixbuf/gdk-pixbuf.loaders", TRUE);
2715 g_setenv ("GTK_IM_MODULE_FILE", "../modules/input/gtk.immodules", TRUE);
2720 main (int argc, char** argv)
2727 gtk_init (&argc, &argv);
2729 buffer = create_buffer ();
2730 view = create_view (buffer);
2731 buffer_unref (buffer);
2733 push_active_window (GTK_WINDOW (view->window));
2734 for (i=1; i < argc; i++)
2738 /* Quick and dirty canonicalization - better should be in GLib
2741 if (!g_path_is_absolute (argv[i]))
2743 char *cwd = g_get_current_dir ();
2744 filename = g_strconcat (cwd, "/", argv[i], NULL);
2750 open_ok_func (filename, view);
2752 if (filename != argv[i])
2755 pop_active_window ();