Add powerbox hook
[gtk-with-powerbox.git] / tests / testtext.c
blob1224f75944754e6fe9e3efb827e41303e2929119
1 /* testtext.c
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.
21 #include "config.h"
22 #include <stdio.h>
23 #include <sys/stat.h>
24 #include <errno.h>
25 #include <stdlib.h>
26 #include <string.h>
28 #undef GTK_DISABLE_DEPRECATED
30 #include <gtk/gtk.h>
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;
42 struct _Buffer
44 gint refcount;
45 GtkTextBuffer *buffer;
46 char *filename;
47 gint untitled_serial;
48 GtkTextTag *invisible_tag;
49 GtkTextTag *not_editable_tag;
50 GtkTextTag *found_text_tag;
51 GtkTextTag *rise_tag;
52 GtkTextTag *large_tag;
53 GtkTextTag *indent_tag;
54 GtkTextTag *margin_tag;
55 GtkTextTag *custom_tabs_tag;
56 GSList *color_tags;
57 guint color_cycle_timeout;
58 gdouble start_hue;
61 struct _View
63 GtkWidget *window;
64 GtkWidget *text_view;
65 GtkAccelGroup *accel_group;
66 GtkItemFactory *item_factory;
67 Buffer *buffer;
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,
81 const char *str,
82 View *view);
83 static void buffer_search_backward (Buffer *buffer,
84 const char *str,
85 View *view);
86 static void buffer_set_colors (Buffer *buffer,
87 gboolean enabled);
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;
102 static void
103 push_active_window (GtkWindow *window)
105 g_object_ref (window);
106 active_window_stack = g_slist_prepend (active_window_stack, window);
109 static void
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);
116 static GtkWindow *
117 get_active_window (void)
119 if (active_window_stack)
120 return active_window_stack->data;
121 else
122 return NULL;
126 * Filesel utility function
129 typedef gboolean (*FileselOKFunc) (const char *filename, gpointer data);
131 static void
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);
143 *result = TRUE;
145 else
146 gtk_widget_show (filesel);
149 gboolean
150 filesel_run (GtkWindow *parent,
151 const char *title,
152 const char *start_file,
153 FileselOKFunc func,
154 gpointer data)
156 GtkWidget *filesel = gtk_file_selection_new (title);
157 gboolean result = FALSE;
159 if (!parent)
160 parent = get_active_window ();
162 if (parent)
163 gtk_window_set_transient_for (GTK_WINDOW (filesel), parent);
165 if (start_file)
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,
174 "clicked",
175 G_CALLBACK (filesel_ok_cb), filesel);
176 g_signal_connect_swapped (GTK_FILE_SELECTION (filesel)->cancel_button,
177 "clicked",
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);
185 gtk_main ();
187 return result;
191 * MsgBox utility functions
194 static void
195 msgbox_yes_cb (GtkWidget *widget, gboolean *result)
197 *result = 0;
198 gtk_object_destroy (GTK_OBJECT (gtk_widget_get_toplevel (widget)));
201 static void
202 msgbox_no_cb (GtkWidget *widget, gboolean *result)
204 *result = 1;
205 gtk_object_destroy (GTK_OBJECT (gtk_widget_get_toplevel (widget)));
208 static gboolean
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));
215 return TRUE;
218 return FALSE;
221 /* Don't copy this example, it's all crack-smoking - you can just use
222 * GtkMessageDialog now
224 gint
225 msgbox_run (GtkWindow *parent,
226 const char *message,
227 const char *yes_button,
228 const char *no_button,
229 const char *cancel_button,
230 gint default_index)
232 gboolean result = -1;
233 GtkWidget *dialog;
234 GtkWidget *button;
235 GtkWidget *label;
236 GtkWidget *vbox;
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);
243 if (!parent)
244 parent = get_active_window ();
246 /* Create a dialog
248 dialog = gtk_window_new (GTK_WINDOW_TOPLEVEL);
249 gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
250 if (parent)
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
285 if (yes_button)
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
301 if (no_button)
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
316 if (cancel_button)
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 */
333 gtk_main ();
335 return result;
338 #ifdef DO_BLINK
340 * Example buffer filling code
342 static gint
343 blink_timeout (gpointer data)
345 GtkTextTag *tag;
346 static gboolean flip = FALSE;
348 tag = GTK_TEXT_TAG (data);
350 g_object_set (tag,
351 "foreground", flip ? "blue" : "purple",
352 NULL);
354 flip = !flip;
356 return TRUE;
358 #endif
360 static gint
361 tag_event_handler (GtkTextTag *tag, GtkWidget *widget, GdkEvent *event,
362 const GtkTextIter *iter, gpointer user_data)
364 gint char_index;
366 char_index = gtk_text_iter_get_offset (iter);
368 switch (event->type)
370 case GDK_MOTION_NOTIFY:
371 printf ("Motion event at char %d tag `%s'\n",
372 char_index, tag->name);
373 break;
375 case GDK_BUTTON_PRESS:
376 printf ("Button press at char %d tag `%s'\n",
377 char_index, tag->name);
378 break;
380 case GDK_2BUTTON_PRESS:
381 printf ("Double click at char %d tag `%s'\n",
382 char_index, tag->name);
383 break;
385 case GDK_3BUTTON_PRESS:
386 printf ("Triple click at char %d tag `%s'\n",
387 char_index, tag->name);
388 break;
390 case GDK_BUTTON_RELEASE:
391 printf ("Button release at char %d tag `%s'\n",
392 char_index, tag->name);
393 break;
395 case GDK_KEY_PRESS:
396 case GDK_KEY_RELEASE:
397 printf ("Key event at char %d tag `%s'\n",
398 char_index, tag->name);
399 break;
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:
409 case GDK_DRAG_ENTER:
410 case GDK_DRAG_LEAVE:
411 case GDK_DRAG_MOTION:
412 case GDK_DRAG_STATUS:
413 case GDK_DROP_START:
414 case GDK_DROP_FINISHED:
415 default:
416 break;
419 return FALSE;
422 static void
423 setup_tag (GtkTextTag *tag)
425 g_signal_connect (tag,
426 "event",
427 G_CALLBACK (tag_event_handler),
428 NULL);
431 static const char *book_closed_xpm[] = {
432 "16 16 6 1",
433 " c None s None",
434 ". c black",
435 "X c red",
436 "o c yellow",
437 "O c #808080",
438 "# c white",
439 " ",
440 " .. ",
441 " ..XX. ",
442 " ..XXXXX. ",
443 " ..XXXXXXXX. ",
444 ".ooXXXXXXXXX. ",
445 "..ooXXXXXXXXX. ",
446 ".X.ooXXXXXXXXX. ",
447 ".XX.ooXXXXXX.. ",
448 " .XX.ooXXX..#O ",
449 " .XX.oo..##OO. ",
450 " .XX..##OO.. ",
451 " .X.#OO.. ",
452 " ..O.. ",
453 " .. ",
454 " "};
456 void
457 fill_example_buffer (GtkTextBuffer *buffer)
459 GtkTextIter iter, iter2;
460 GtkTextTag *tag;
461 GtkTextChildAnchor *anchor;
462 GdkColor color;
463 GdkColor color2;
464 GdkPixbuf *pixbuf;
465 int i;
466 char *str;
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);
474 #ifdef DO_BLINK
475 gtk_timeout_add (1000, blink_timeout, tag);
476 #endif
478 setup_tag (tag);
480 color.red = color.green = 0;
481 color.blue = 0xffff;
482 color2.red = 0xfff;
483 color2.blue = 0x0;
484 color2.green = 0;
485 g_object_set (tag,
486 "foreground_gdk", &color,
487 "background_gdk", &color2,
488 "size_points", 24.0,
489 NULL);
491 tag = gtk_text_buffer_create_tag (buffer, "fg_red", NULL);
493 setup_tag (tag);
495 color.blue = color.green = 0;
496 color.red = 0xffff;
497 g_object_set (tag,
498 "rise", -4 * PANGO_SCALE,
499 "foreground_gdk", &color,
500 NULL);
502 tag = gtk_text_buffer_create_tag (buffer, "bg_green", NULL);
504 setup_tag (tag);
506 color.blue = color.red = 0;
507 color.green = 0xffff;
508 g_object_set (tag,
509 "background_gdk", &color,
510 "size_points", 10.0,
511 NULL);
513 tag = gtk_text_buffer_create_tag (buffer, "strikethrough", NULL);
515 setup_tag (tag);
517 g_object_set (tag,
518 "strikethrough", TRUE,
519 NULL);
522 tag = gtk_text_buffer_create_tag (buffer, "underline", NULL);
524 setup_tag (tag);
526 g_object_set (tag,
527 "underline", PANGO_UNDERLINE_SINGLE,
528 NULL);
530 tag = gtk_text_buffer_create_tag (buffer, "underline_error", NULL);
532 setup_tag (tag);
534 g_object_set (tag,
535 "underline", PANGO_UNDERLINE_ERROR,
536 NULL);
538 tag = gtk_text_buffer_create_tag (buffer, "centered", NULL);
540 g_object_set (tag,
541 "justification", GTK_JUSTIFY_CENTER,
542 NULL);
544 tag = gtk_text_buffer_create_tag (buffer, "rtl_quote", NULL);
546 g_object_set (tag,
547 "wrap_mode", GTK_WRAP_WORD,
548 "direction", GTK_TEXT_DIR_RTL,
549 "indent", 30,
550 "left_margin", 20,
551 "right_margin", 20,
552 NULL);
555 tag = gtk_text_buffer_create_tag (buffer, "negative_indent", NULL);
557 g_object_set (tag,
558 "indent", -25,
559 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);
572 i = 0;
573 while (i < 100)
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);
586 g_free (str);
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);
596 temp_mark =
597 gtk_text_buffer_create_mark (buffer, "tmp_mark", &iter, TRUE);
599 #if 1
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);
634 #endif
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),
652 "negative_indent"),
653 NULL);
655 ++i;
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);
671 gboolean
672 fill_file_buffer (GtkTextBuffer *buffer, const char *filename)
674 FILE* f;
675 gchar buf[2048];
676 gint remaining = 0;
677 GtkTextIter iter, end;
679 f = fopen (filename, "r");
681 if (f == NULL)
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);
686 g_free (err);
687 return FALSE;
690 gtk_text_buffer_get_iter_at_offset (buffer, &iter, 0);
691 while (!feof (f))
693 gint count;
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)
709 break;
712 if (remaining)
714 gchar *err = g_strdup_printf ("Invalid UTF-8 data encountered reading file '%s'", filename);
715 msgbox_run (NULL, err, "OK", NULL, NULL, 0);
716 g_free (err);
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);
727 return TRUE;
730 static gint
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 ();
739 return TRUE;
743 * Menu callbacks
746 static View *
747 get_empty_view (View *view)
749 if (!view->buffer->filename &&
750 !gtk_text_buffer_get_modified (view->buffer->buffer))
751 return view;
752 else
753 return create_view (create_buffer ());
756 static View *
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");
764 else
766 GtkWidget *app = gtk_widget_get_toplevel (widget);
767 return g_object_get_data (G_OBJECT (app), "view");
771 static void
772 do_new (gpointer callback_data,
773 guint callback_action,
774 GtkWidget *widget)
776 create_view (create_buffer ());
779 static void
780 do_new_view (gpointer callback_data,
781 guint callback_action,
782 GtkWidget *widget)
784 View *view = view_from_widget (widget);
786 create_view (view->buffer);
789 gboolean
790 open_ok_func (const char *filename, gpointer data)
792 View *view = 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);
799 return FALSE;
801 else
803 g_free (new_view->buffer->filename);
804 new_view->buffer->filename = g_strdup (filename);
805 buffer_filename_set (new_view->buffer);
807 return TRUE;
811 static void
812 do_open (gpointer callback_data,
813 guint callback_action,
814 GtkWidget *widget)
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 ();
823 static void
824 do_save_as (gpointer callback_data,
825 guint callback_action,
826 GtkWidget *widget)
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 ();
835 static void
836 do_save (gpointer callback_data,
837 guint callback_action,
838 GtkWidget *widget)
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);
845 else
846 save_buffer (view->buffer);
847 pop_active_window ();
850 static void
851 do_close (gpointer callback_data,
852 guint callback_action,
853 GtkWidget *widget)
855 View *view = view_from_widget (widget);
857 push_active_window (GTK_WINDOW (view->window));
858 check_close_view (view);
859 pop_active_window ();
862 static void
863 do_exit (gpointer callback_data,
864 guint callback_action,
865 GtkWidget *widget)
867 View *view = view_from_widget (widget);
869 GSList *tmp_list = buffers;
871 push_active_window (GTK_WINDOW (view->window));
872 while (tmp_list)
874 if (!check_buffer_saved (tmp_list->data))
875 return;
877 tmp_list = tmp_list->next;
880 gtk_main_quit ();
881 pop_active_window ();
884 static void
885 do_example (gpointer callback_data,
886 guint callback_action,
887 GtkWidget *widget)
889 View *view = view_from_widget (widget);
890 View *new_view;
892 new_view = get_empty_view (view);
894 fill_example_buffer (new_view->buffer->buffer);
896 view_add_example_widgets (new_view);
900 static void
901 do_insert_and_scroll (gpointer callback_data,
902 guint callback_action,
903 GtkWidget *widget)
905 View *view = view_from_widget (widget);
906 GtkTextBuffer *buffer;
907 GtkTextIter start, end;
908 GtkTextMark *mark;
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"
919 "Line 5\n",
920 -1);
922 gtk_text_view_scroll_to_mark (GTK_TEXT_VIEW (view->text_view), mark,
923 0, TRUE, 0.0, 1.0);
924 gtk_text_buffer_delete_mark (buffer, mark);
927 static void
928 do_wrap_changed (gpointer callback_data,
929 guint callback_action,
930 GtkWidget *widget)
932 View *view = view_from_widget (widget);
934 gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (view->text_view), callback_action);
937 static void
938 do_direction_changed (gpointer callback_data,
939 guint callback_action,
940 GtkWidget *widget)
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);
949 static void
950 do_spacing_changed (gpointer callback_data,
951 guint callback_action,
952 GtkWidget *widget)
954 View *view = view_from_widget (widget);
956 if (callback_action)
958 gtk_text_view_set_pixels_above_lines (GTK_TEXT_VIEW (view->text_view),
959 23);
960 gtk_text_view_set_pixels_below_lines (GTK_TEXT_VIEW (view->text_view),
961 21);
962 gtk_text_view_set_pixels_inside_wrap (GTK_TEXT_VIEW (view->text_view),
965 else
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),
976 static void
977 do_editable_changed (gpointer callback_data,
978 guint callback_action,
979 GtkWidget *widget)
981 View *view = view_from_widget (widget);
983 gtk_text_view_set_editable (GTK_TEXT_VIEW (view->text_view), callback_action);
986 static void
987 change_cursor_color (GtkWidget *widget,
988 gboolean set)
990 if (set)
992 GdkColor red = {0, 65535, 0, 0};
993 gtk_widget_modify_cursor (widget, &red, &red);
995 else
996 gtk_widget_modify_cursor (widget, NULL, NULL);
999 static void
1000 do_cursor_visible_changed (gpointer callback_data,
1001 guint callback_action,
1002 GtkWidget *widget)
1004 View *view = view_from_widget (widget);
1006 switch (callback_action)
1008 case 0:
1009 gtk_text_view_set_cursor_visible (GTK_TEXT_VIEW (view->text_view), FALSE);
1010 break;
1011 case 1:
1012 gtk_text_view_set_cursor_visible (GTK_TEXT_VIEW (view->text_view), TRUE);
1013 change_cursor_color (view->text_view, FALSE);
1014 break;
1015 case 2:
1016 gtk_text_view_set_cursor_visible (GTK_TEXT_VIEW (view->text_view), TRUE);
1017 change_cursor_color (view->text_view, TRUE);
1018 break;
1022 static void
1023 do_color_cycle_changed (gpointer callback_data,
1024 guint callback_action,
1025 GtkWidget *widget)
1027 View *view = view_from_widget (widget);
1029 buffer_set_colors (view->buffer, callback_action);
1032 static void
1033 do_apply_editable (gpointer callback_data,
1034 guint callback_action,
1035 GtkWidget *widget)
1037 View *view = view_from_widget (widget);
1038 GtkTextIter start;
1039 GtkTextIter end;
1041 if (gtk_text_buffer_get_selection_bounds (view->buffer->buffer,
1042 &start, &end))
1044 if (callback_action)
1046 gtk_text_buffer_remove_tag (view->buffer->buffer,
1047 view->buffer->not_editable_tag,
1048 &start, &end);
1050 else
1052 gtk_text_buffer_apply_tag (view->buffer->buffer,
1053 view->buffer->not_editable_tag,
1054 &start, &end);
1059 static void
1060 do_apply_invisible (gpointer callback_data,
1061 guint callback_action,
1062 GtkWidget *widget)
1064 View *view = view_from_widget (widget);
1065 GtkTextIter start;
1066 GtkTextIter end;
1068 if (gtk_text_buffer_get_selection_bounds (view->buffer->buffer,
1069 &start, &end))
1071 if (callback_action)
1073 gtk_text_buffer_remove_tag (view->buffer->buffer,
1074 view->buffer->invisible_tag,
1075 &start, &end);
1077 else
1079 gtk_text_buffer_apply_tag (view->buffer->buffer,
1080 view->buffer->invisible_tag,
1081 &start, &end);
1086 static void
1087 do_apply_rise (gpointer callback_data,
1088 guint callback_action,
1089 GtkWidget *widget)
1091 View *view = view_from_widget (widget);
1092 GtkTextIter start;
1093 GtkTextIter end;
1095 if (gtk_text_buffer_get_selection_bounds (view->buffer->buffer,
1096 &start, &end))
1098 if (callback_action)
1100 gtk_text_buffer_remove_tag (view->buffer->buffer,
1101 view->buffer->rise_tag,
1102 &start, &end);
1104 else
1106 gtk_text_buffer_apply_tag (view->buffer->buffer,
1107 view->buffer->rise_tag,
1108 &start, &end);
1113 static void
1114 do_apply_large (gpointer callback_data,
1115 guint callback_action,
1116 GtkWidget *widget)
1118 View *view = view_from_widget (widget);
1119 GtkTextIter start;
1120 GtkTextIter end;
1122 if (gtk_text_buffer_get_selection_bounds (view->buffer->buffer,
1123 &start, &end))
1125 if (callback_action)
1127 gtk_text_buffer_remove_tag (view->buffer->buffer,
1128 view->buffer->large_tag,
1129 &start, &end);
1131 else
1133 gtk_text_buffer_apply_tag (view->buffer->buffer,
1134 view->buffer->large_tag,
1135 &start, &end);
1140 static void
1141 do_apply_indent (gpointer callback_data,
1142 guint callback_action,
1143 GtkWidget *widget)
1145 View *view = view_from_widget (widget);
1146 GtkTextIter start;
1147 GtkTextIter end;
1149 if (gtk_text_buffer_get_selection_bounds (view->buffer->buffer,
1150 &start, &end))
1152 if (callback_action)
1154 gtk_text_buffer_remove_tag (view->buffer->buffer,
1155 view->buffer->indent_tag,
1156 &start, &end);
1158 else
1160 gtk_text_buffer_apply_tag (view->buffer->buffer,
1161 view->buffer->indent_tag,
1162 &start, &end);
1167 static void
1168 do_apply_margin (gpointer callback_data,
1169 guint callback_action,
1170 GtkWidget *widget)
1172 View *view = view_from_widget (widget);
1173 GtkTextIter start;
1174 GtkTextIter end;
1176 if (gtk_text_buffer_get_selection_bounds (view->buffer->buffer,
1177 &start, &end))
1179 if (callback_action)
1181 gtk_text_buffer_remove_tag (view->buffer->buffer,
1182 view->buffer->margin_tag,
1183 &start, &end);
1185 else
1187 gtk_text_buffer_apply_tag (view->buffer->buffer,
1188 view->buffer->margin_tag,
1189 &start, &end);
1194 static void
1195 do_apply_tabs (gpointer callback_data,
1196 guint callback_action,
1197 GtkWidget *widget)
1199 View *view = view_from_widget (widget);
1200 GtkTextIter start;
1201 GtkTextIter end;
1203 if (gtk_text_buffer_get_selection_bounds (view->buffer->buffer,
1204 &start, &end))
1206 if (callback_action)
1208 gtk_text_buffer_remove_tag (view->buffer->buffer,
1209 view->buffer->custom_tabs_tag,
1210 &start, &end);
1212 else
1214 gtk_text_buffer_apply_tag (view->buffer->buffer,
1215 view->buffer->custom_tabs_tag,
1216 &start, &end);
1221 static void
1222 do_apply_colors (gpointer callback_data,
1223 guint callback_action,
1224 GtkWidget *widget)
1226 View *view = view_from_widget (widget);
1227 Buffer *buffer = view->buffer;
1228 GtkTextIter start;
1229 GtkTextIter end;
1231 if (gtk_text_buffer_get_selection_bounds (view->buffer->buffer,
1232 &start, &end))
1234 if (!callback_action)
1236 GSList *tmp;
1238 tmp = buffer->color_tags;
1239 while (tmp != NULL)
1241 gtk_text_buffer_remove_tag (view->buffer->buffer,
1242 tmp->data,
1243 &start, &end);
1244 tmp = g_slist_next (tmp);
1247 else
1249 GSList *tmp;
1251 tmp = buffer->color_tags;
1252 while (TRUE)
1254 GtkTextIter next;
1255 gboolean done = FALSE;
1257 next = start;
1258 gtk_text_iter_forward_char (&next);
1259 gtk_text_iter_forward_char (&next);
1261 if (gtk_text_iter_compare (&next, &end) >= 0)
1263 next = end;
1264 done = TRUE;
1267 gtk_text_buffer_apply_tag (view->buffer->buffer,
1268 tmp->data,
1269 &start, &next);
1271 start = next;
1273 if (done)
1274 return;
1276 tmp = g_slist_next (tmp);
1277 if (tmp == NULL)
1278 tmp = buffer->color_tags;
1284 static void
1285 do_remove_tags (gpointer callback_data,
1286 guint callback_action,
1287 GtkWidget *widget)
1289 View *view = view_from_widget (widget);
1290 GtkTextIter start;
1291 GtkTextIter end;
1293 if (gtk_text_buffer_get_selection_bounds (view->buffer->buffer,
1294 &start, &end))
1296 gtk_text_buffer_remove_all_tags (view->buffer->buffer,
1297 &start, &end);
1301 static void
1302 do_properties (gpointer callback_data,
1303 guint callback_action,
1304 GtkWidget *widget)
1306 View *view = view_from_widget (widget);
1308 create_prop_editor (G_OBJECT (view->text_view), 0);
1311 static void
1312 rich_text_store_populate (GtkListStore *store,
1313 GtkTextBuffer *buffer,
1314 gboolean deserialize)
1316 GdkAtom *formats;
1317 gint n_formats;
1318 gint i;
1320 gtk_list_store_clear (store);
1322 if (deserialize)
1323 formats = gtk_text_buffer_get_deserialize_formats (buffer, &n_formats);
1324 else
1325 formats = gtk_text_buffer_get_serialize_formats (buffer, &n_formats);
1327 for (i = 0; i < n_formats; i++)
1329 GtkTreeIter iter;
1330 gchar *mime_type;
1331 gboolean can_create_tags = FALSE;
1333 mime_type = gdk_atom_name (formats[i]);
1335 if (deserialize)
1336 can_create_tags =
1337 gtk_text_buffer_deserialize_get_can_create_tags (buffer, formats[i]);
1339 gtk_list_store_append (store, &iter);
1340 gtk_list_store_set (store, &iter,
1341 0, formats[i],
1342 1, mime_type,
1343 2, can_create_tags,
1344 -1);
1346 g_free (mime_type);
1349 g_free (formats);
1352 static void
1353 rich_text_paste_target_list_notify (GtkTextBuffer *buffer,
1354 const GParamSpec *pspec,
1355 GtkListStore *store)
1357 rich_text_store_populate (store, buffer, TRUE);
1360 static void
1361 rich_text_copy_target_list_notify (GtkTextBuffer *buffer,
1362 const GParamSpec *pspec,
1363 GtkListStore *store)
1365 rich_text_store_populate (store, buffer, FALSE);
1368 static void
1369 rich_text_can_create_tags_toggled (GtkCellRendererToggle *toggle,
1370 const gchar *path,
1371 GtkTreeModel *model)
1373 GtkTreeIter iter;
1375 if (gtk_tree_model_get_iter_from_string (model, &iter, path))
1377 GtkTextBuffer *buffer;
1378 GdkAtom format;
1379 gboolean can_create_tags;
1381 buffer = g_object_get_data (G_OBJECT (model), "buffer");
1383 gtk_tree_model_get (model, &iter,
1384 0, &format,
1385 2, &can_create_tags,
1386 -1);
1388 gtk_text_buffer_deserialize_set_can_create_tags (buffer, format,
1389 !can_create_tags);
1391 gtk_list_store_set (GTK_LIST_STORE (model), &iter,
1392 2, !can_create_tags,
1393 -1);
1397 static void
1398 rich_text_unregister_clicked (GtkWidget *button,
1399 GtkTreeView *tv)
1401 GtkTreeSelection *sel = gtk_tree_view_get_selection (tv);
1402 GtkTreeModel *model;
1403 GtkTreeIter iter;
1405 if (gtk_tree_selection_get_selected (sel, &model, &iter))
1407 GtkTextBuffer *buffer;
1408 gboolean deserialize;
1409 GdkAtom format;
1411 buffer = g_object_get_data (G_OBJECT (model), "buffer");
1412 deserialize = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (model),
1413 "deserialize"));
1415 gtk_tree_model_get (model, &iter,
1416 0, &format,
1417 -1);
1419 if (deserialize)
1420 gtk_text_buffer_unregister_deserialize_format (buffer, format);
1421 else
1422 gtk_text_buffer_unregister_serialize_format (buffer, format);
1426 static void
1427 rich_text_register_clicked (GtkWidget *button,
1428 GtkTreeView *tv)
1430 GtkWidget *dialog;
1431 GtkWidget *label;
1432 GtkWidget *entry;
1434 dialog = gtk_dialog_new_with_buttons ("Register new Tagset",
1435 GTK_WINDOW (gtk_widget_get_toplevel (button)),
1436 GTK_DIALOG_DESTROY_WITH_PARENT,
1437 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1438 GTK_STOCK_OK, GTK_RESPONSE_OK,
1439 NULL);
1440 label = gtk_label_new ("Enter tagset name or leave blank for "
1441 "unrestricted internal format:");
1442 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), label,
1443 FALSE, FALSE, 0);
1445 entry = gtk_entry_new ();
1446 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), entry,
1447 FALSE, FALSE, 0);
1449 gtk_widget_show_all (dialog);
1451 if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK)
1453 GtkTreeModel *model = gtk_tree_view_get_model (tv);
1454 GtkTextBuffer *buffer = g_object_get_data (G_OBJECT (model), "buffer");
1455 const gchar *tagset = gtk_entry_get_text (GTK_ENTRY (entry));
1456 gboolean deserialize;
1458 deserialize = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (model),
1459 "deserialize"));
1461 if (tagset && ! strlen (tagset))
1462 tagset = NULL;
1464 if (deserialize)
1465 gtk_text_buffer_register_deserialize_tagset (buffer, tagset);
1466 else
1467 gtk_text_buffer_register_serialize_tagset (buffer, tagset);
1470 gtk_widget_destroy (dialog);
1473 static void
1474 do_rich_text (gpointer callback_data,
1475 guint deserialize,
1476 GtkWidget *widget)
1478 View *view = view_from_widget (widget);
1479 GtkTextBuffer *buffer;
1480 GtkWidget *dialog;
1481 GtkWidget *tv;
1482 GtkWidget *sw;
1483 GtkWidget *hbox;
1484 GtkWidget *button;
1485 GtkListStore *store;
1487 buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view->text_view));
1489 dialog = gtk_dialog_new_with_buttons (deserialize ?
1490 "Rich Text Paste & Drop" :
1491 "Rich Text Copy & Drag",
1492 GTK_WINDOW (view->window),
1493 GTK_DIALOG_DESTROY_WITH_PARENT,
1494 GTK_STOCK_CLOSE, 0,
1495 NULL);
1496 g_signal_connect (dialog, "response",
1497 G_CALLBACK (gtk_widget_destroy),
1498 NULL);
1500 store = gtk_list_store_new (3,
1501 G_TYPE_POINTER,
1502 G_TYPE_STRING,
1503 G_TYPE_BOOLEAN);
1505 g_object_set_data (G_OBJECT (store), "buffer", buffer);
1506 g_object_set_data (G_OBJECT (store), "deserialize",
1507 GUINT_TO_POINTER (deserialize));
1509 tv = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store));
1510 g_object_unref (store);
1512 gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (tv),
1513 0, "Rich Text Format",
1514 gtk_cell_renderer_text_new (),
1515 "text", 1,
1516 NULL);
1518 if (deserialize)
1520 GtkCellRenderer *renderer = gtk_cell_renderer_toggle_new ();
1522 gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (tv),
1523 1, "Can Create Tags",
1524 renderer,
1525 "active", 2,
1526 NULL);
1528 g_signal_connect (renderer, "toggled",
1529 G_CALLBACK (rich_text_can_create_tags_toggled),
1530 store);
1533 sw = gtk_scrolled_window_new (NULL, NULL);
1534 gtk_widget_set_size_request (sw, 300, 100);
1535 gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), sw);
1537 gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (sw), tv);
1539 hbox = gtk_hbox_new (FALSE, 6);
1540 gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), hbox,
1541 FALSE, FALSE, 0);
1543 button = gtk_button_new_with_label ("Unregister Selected Format");
1544 gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
1546 g_signal_connect (button, "clicked",
1547 G_CALLBACK (rich_text_unregister_clicked),
1548 tv);
1550 button = gtk_button_new_with_label ("Register New Tagset\n"
1551 "for the Internal Format");
1552 gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
1554 g_signal_connect (button, "clicked",
1555 G_CALLBACK (rich_text_register_clicked),
1556 tv);
1558 if (deserialize)
1559 g_signal_connect_object (buffer, "notify::paste-target-list",
1560 G_CALLBACK (rich_text_paste_target_list_notify),
1561 G_OBJECT (store), 0);
1562 else
1563 g_signal_connect_object (buffer, "notify::copy-target-list",
1564 G_CALLBACK (rich_text_copy_target_list_notify),
1565 G_OBJECT (store), 0);
1567 rich_text_store_populate (store, buffer, deserialize);
1569 gtk_widget_show_all (dialog);
1572 enum
1574 RESPONSE_FORWARD,
1575 RESPONSE_BACKWARD
1578 static void
1579 dialog_response_callback (GtkWidget *dialog, gint response_id, gpointer data)
1581 GtkTextBuffer *buffer;
1582 View *view = data;
1583 GtkTextIter start, end;
1584 gchar *search_string;
1586 if (response_id != RESPONSE_FORWARD &&
1587 response_id != RESPONSE_BACKWARD)
1589 gtk_widget_destroy (dialog);
1590 return;
1593 buffer = g_object_get_data (G_OBJECT (dialog), "buffer");
1595 gtk_text_buffer_get_bounds (buffer, &start, &end);
1597 search_string = gtk_text_iter_get_text (&start, &end);
1599 g_print ("Searching for `%s'\n", search_string);
1601 if (response_id == RESPONSE_FORWARD)
1602 buffer_search_forward (view->buffer, search_string, view);
1603 else if (response_id == RESPONSE_BACKWARD)
1604 buffer_search_backward (view->buffer, search_string, view);
1606 g_free (search_string);
1608 gtk_widget_destroy (dialog);
1611 static void
1612 do_copy (gpointer callback_data,
1613 guint callback_action,
1614 GtkWidget *widget)
1616 View *view = view_from_widget (widget);
1617 GtkTextBuffer *buffer;
1619 buffer = view->buffer->buffer;
1621 gtk_text_buffer_copy_clipboard (buffer,
1622 gtk_clipboard_get (GDK_NONE));
1625 static void
1626 do_search (gpointer callback_data,
1627 guint callback_action,
1628 GtkWidget *widget)
1630 View *view = view_from_widget (widget);
1631 GtkWidget *dialog;
1632 GtkWidget *search_text;
1633 GtkTextBuffer *buffer;
1635 dialog = gtk_dialog_new_with_buttons ("Search",
1636 GTK_WINDOW (view->window),
1637 GTK_DIALOG_DESTROY_WITH_PARENT,
1638 "Forward", RESPONSE_FORWARD,
1639 "Backward", RESPONSE_BACKWARD,
1640 GTK_STOCK_CANCEL,
1641 GTK_RESPONSE_NONE, NULL);
1644 buffer = gtk_text_buffer_new (NULL);
1646 search_text = gtk_text_view_new_with_buffer (buffer);
1648 g_object_unref (buffer);
1650 gtk_box_pack_end (GTK_BOX (GTK_DIALOG (dialog)->vbox),
1651 search_text,
1652 TRUE, TRUE, 0);
1654 g_object_set_data (G_OBJECT (dialog), "buffer", buffer);
1656 g_signal_connect (dialog,
1657 "response",
1658 G_CALLBACK (dialog_response_callback),
1659 view);
1661 gtk_widget_show (search_text);
1663 gtk_widget_grab_focus (search_text);
1665 gtk_widget_show_all (dialog);
1668 static void
1669 do_select_all (gpointer callback_data,
1670 guint callback_action,
1671 GtkWidget *widget)
1673 View *view = view_from_widget (widget);
1674 GtkTextBuffer *buffer;
1675 GtkTextIter start, end;
1677 buffer = view->buffer->buffer;
1679 gtk_text_buffer_get_bounds (buffer, &start, &end);
1680 gtk_text_buffer_select_range (buffer, &start, &end);
1683 typedef struct
1685 /* position is in coordinate system of text_view_move_child */
1686 int click_x;
1687 int click_y;
1688 int start_x;
1689 int start_y;
1690 int button;
1691 } ChildMoveInfo;
1693 static gboolean
1694 movable_child_callback (GtkWidget *child,
1695 GdkEvent *event,
1696 gpointer data)
1698 ChildMoveInfo *info;
1699 GtkTextView *text_view;
1701 text_view = GTK_TEXT_VIEW (data);
1703 g_return_val_if_fail (GTK_IS_EVENT_BOX (child), FALSE);
1704 g_return_val_if_fail (gtk_widget_get_parent (child) == GTK_WIDGET (text_view), FALSE);
1706 info = g_object_get_data (G_OBJECT (child),
1707 "testtext-move-info");
1709 if (info == NULL)
1711 info = g_new (ChildMoveInfo, 1);
1712 info->start_x = -1;
1713 info->start_y = -1;
1714 info->button = -1;
1715 g_object_set_data_full (G_OBJECT (child),
1716 "testtext-move-info",
1717 info,
1718 g_free);
1721 switch (event->type)
1723 case GDK_BUTTON_PRESS:
1724 if (info->button < 0)
1726 if (gdk_pointer_grab (event->button.window,
1727 FALSE,
1728 GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK |
1729 GDK_BUTTON_RELEASE_MASK,
1730 NULL,
1731 NULL,
1732 event->button.time) != GDK_GRAB_SUCCESS)
1733 return FALSE;
1735 info->button = event->button.button;
1737 info->start_x = child->allocation.x;
1738 info->start_y = child->allocation.y;
1739 info->click_x = child->allocation.x + event->button.x;
1740 info->click_y = child->allocation.y + event->button.y;
1742 break;
1744 case GDK_BUTTON_RELEASE:
1745 if (info->button < 0)
1746 return FALSE;
1748 if (info->button == event->button.button)
1750 int x, y;
1752 gdk_pointer_ungrab (event->button.time);
1753 info->button = -1;
1755 /* convert to window coords from event box coords */
1756 x = info->start_x + (event->button.x + child->allocation.x - info->click_x);
1757 y = info->start_y + (event->button.y + child->allocation.y - info->click_y);
1759 gtk_text_view_move_child (text_view,
1760 child,
1761 x, y);
1763 break;
1765 case GDK_MOTION_NOTIFY:
1767 int x, y;
1769 if (info->button < 0)
1770 return FALSE;
1772 gdk_window_get_pointer (child->window, &x, &y, NULL); /* ensure more events */
1774 /* to window coords from event box coords */
1775 x += child->allocation.x;
1776 y += child->allocation.y;
1778 x = info->start_x + (x - info->click_x);
1779 y = info->start_y + (y - info->click_y);
1781 gtk_text_view_move_child (text_view,
1782 child,
1783 x, y);
1785 break;
1787 default:
1788 break;
1791 return FALSE;
1794 static void
1795 add_movable_child (GtkTextView *text_view,
1796 GtkTextWindowType window)
1798 GtkWidget *event_box;
1799 GtkWidget *label;
1800 GdkColor color;
1802 label = gtk_label_new ("Drag me around");
1804 event_box = gtk_event_box_new ();
1805 gtk_widget_add_events (event_box,
1806 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
1807 GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK);
1809 color.red = 0xffff;
1810 color.green = color.blue = 0;
1811 gtk_widget_modify_bg (event_box, GTK_STATE_NORMAL, &color);
1813 gtk_container_add (GTK_CONTAINER (event_box), label);
1815 gtk_widget_show_all (event_box);
1817 g_signal_connect (event_box, "event",
1818 G_CALLBACK (movable_child_callback),
1819 text_view);
1821 gtk_text_view_add_child_in_window (text_view,
1822 event_box,
1823 window,
1824 0, 0);
1827 static void
1828 do_add_children (gpointer callback_data,
1829 guint callback_action,
1830 GtkWidget *widget)
1832 View *view = view_from_widget (widget);
1834 add_movable_child (GTK_TEXT_VIEW (view->text_view),
1835 GTK_TEXT_WINDOW_WIDGET);
1836 add_movable_child (GTK_TEXT_VIEW (view->text_view),
1837 GTK_TEXT_WINDOW_LEFT);
1838 add_movable_child (GTK_TEXT_VIEW (view->text_view),
1839 GTK_TEXT_WINDOW_RIGHT);
1842 static void
1843 do_add_focus_children (gpointer callback_data,
1844 guint callback_action,
1845 GtkWidget *widget)
1847 View *view = view_from_widget (widget);
1848 GtkWidget *child;
1849 GtkTextChildAnchor *anchor;
1850 GtkTextIter iter;
1851 GtkTextView *text_view;
1853 text_view = GTK_TEXT_VIEW (view->text_view);
1855 child = gtk_button_new_with_mnemonic ("Button _A in widget->window");
1857 gtk_text_view_add_child_in_window (text_view,
1858 child,
1859 GTK_TEXT_WINDOW_WIDGET,
1860 200, 200);
1862 child = gtk_button_new_with_mnemonic ("Button _B in widget->window");
1864 gtk_text_view_add_child_in_window (text_view,
1865 child,
1866 GTK_TEXT_WINDOW_WIDGET,
1867 350, 300);
1869 child = gtk_button_new_with_mnemonic ("Button _C in left window");
1871 gtk_text_view_add_child_in_window (text_view,
1872 child,
1873 GTK_TEXT_WINDOW_LEFT,
1874 0, 0);
1876 child = gtk_button_new_with_mnemonic ("Button _D in right window");
1878 gtk_text_view_add_child_in_window (text_view,
1879 child,
1880 GTK_TEXT_WINDOW_RIGHT,
1881 0, 0);
1883 gtk_text_buffer_get_start_iter (view->buffer->buffer, &iter);
1885 anchor = gtk_text_buffer_create_child_anchor (view->buffer->buffer, &iter);
1887 child = gtk_button_new_with_mnemonic ("Button _E in buffer");
1889 gtk_text_view_add_child_at_anchor (text_view, child, anchor);
1891 anchor = gtk_text_buffer_create_child_anchor (view->buffer->buffer, &iter);
1893 child = gtk_button_new_with_mnemonic ("Button _F in buffer");
1895 gtk_text_view_add_child_at_anchor (text_view, child, anchor);
1897 anchor = gtk_text_buffer_create_child_anchor (view->buffer->buffer, &iter);
1899 child = gtk_button_new_with_mnemonic ("Button _G in buffer");
1901 gtk_text_view_add_child_at_anchor (text_view, child, anchor);
1903 /* show all the buttons */
1904 gtk_widget_show_all (view->text_view);
1907 static void
1908 view_init_menus (View *view)
1910 GtkTextDirection direction = gtk_widget_get_direction (view->text_view);
1911 GtkWrapMode wrap_mode = gtk_text_view_get_wrap_mode (GTK_TEXT_VIEW (view->text_view));
1912 GtkWidget *menu_item = NULL;
1914 switch (direction)
1916 case GTK_TEXT_DIR_LTR:
1917 menu_item = gtk_item_factory_get_widget (view->item_factory, "/Settings/Left-to-Right");
1918 break;
1919 case GTK_TEXT_DIR_RTL:
1920 menu_item = gtk_item_factory_get_widget (view->item_factory, "/Settings/Right-to-Left");
1921 break;
1922 default:
1923 break;
1926 if (menu_item)
1927 gtk_menu_item_activate (GTK_MENU_ITEM (menu_item));
1929 switch (wrap_mode)
1931 case GTK_WRAP_NONE:
1932 menu_item = gtk_item_factory_get_widget (view->item_factory, "/Settings/Wrap Off");
1933 break;
1934 case GTK_WRAP_WORD:
1935 menu_item = gtk_item_factory_get_widget (view->item_factory, "/Settings/Wrap Words");
1936 break;
1937 case GTK_WRAP_CHAR:
1938 menu_item = gtk_item_factory_get_widget (view->item_factory, "/Settings/Wrap Chars");
1939 break;
1940 default:
1941 break;
1944 if (menu_item)
1945 gtk_menu_item_activate (GTK_MENU_ITEM (menu_item));
1948 static GtkItemFactoryEntry menu_items[] =
1950 { "/_File", NULL, NULL, 0, "<Branch>" },
1951 { "/File/_New", "<control>N", do_new, 0, NULL },
1952 { "/File/New _View", NULL, do_new_view, 0, NULL },
1953 { "/File/_Open", "<control>O", do_open, 0, NULL },
1954 { "/File/_Save", "<control>S", do_save, 0, NULL },
1955 { "/File/Save _As...", NULL, do_save_as, 0, NULL },
1956 { "/File/sep1", NULL, NULL, 0, "<Separator>" },
1957 { "/File/_Close", "<control>W" , do_close, 0, NULL },
1958 { "/File/E_xit", "<control>Q" , do_exit, 0, NULL },
1960 { "/_Edit", NULL, 0, 0, "<Branch>" },
1961 { "/Edit/Copy", NULL, do_copy, 0, NULL },
1962 { "/Edit/sep1", NULL, NULL, 0, "<Separator>" },
1963 { "/Edit/Find...", NULL, do_search, 0, NULL },
1964 { "/Edit/Select All", "<control>A", do_select_all, 0, NULL },
1966 { "/_Settings", NULL, NULL, 0, "<Branch>" },
1967 { "/Settings/Wrap _Off", NULL, do_wrap_changed, GTK_WRAP_NONE, "<RadioItem>" },
1968 { "/Settings/Wrap _Words", NULL, do_wrap_changed, GTK_WRAP_WORD, "/Settings/Wrap Off" },
1969 { "/Settings/Wrap _Chars", NULL, do_wrap_changed, GTK_WRAP_CHAR, "/Settings/Wrap Off" },
1970 { "/Settings/sep1", NULL, NULL, 0, "<Separator>" },
1971 { "/Settings/Editable", NULL, do_editable_changed, TRUE, "<RadioItem>" },
1972 { "/Settings/Not editable", NULL, do_editable_changed, FALSE, "/Settings/Editable" },
1973 { "/Settings/sep1", NULL, NULL, 0, "<Separator>" },
1975 { "/Settings/Cursor normal", NULL, do_cursor_visible_changed, 1, "<RadioItem>" },
1976 { "/Settings/Cursor not visible", NULL, do_cursor_visible_changed, 0, "/Settings/Cursor normal" },
1977 { "/Settings/Cursor colored", NULL, do_cursor_visible_changed, 2, "/Settings/Cursor normal" },
1978 { "/Settings/sep1", NULL, NULL, 0, "<Separator>" },
1980 { "/Settings/Left-to-Right", NULL, do_direction_changed, GTK_TEXT_DIR_LTR, "<RadioItem>" },
1981 { "/Settings/Right-to-Left", NULL, do_direction_changed, GTK_TEXT_DIR_RTL, "/Settings/Left-to-Right" },
1983 { "/Settings/sep1", NULL, NULL, 0, "<Separator>" },
1984 { "/Settings/Sane spacing", NULL, do_spacing_changed, FALSE, "<RadioItem>" },
1985 { "/Settings/Funky spacing", NULL, do_spacing_changed, TRUE, "/Settings/Sane spacing" },
1986 { "/Settings/sep1", NULL, NULL, 0, "<Separator>" },
1987 { "/Settings/Don't cycle color tags", NULL, do_color_cycle_changed, FALSE, "<RadioItem>" },
1988 { "/Settings/Cycle colors", NULL, do_color_cycle_changed, TRUE, "/Settings/Don't cycle color tags" },
1989 { "/_Attributes", NULL, NULL, 0, "<Branch>" },
1990 { "/Attributes/Editable", NULL, do_apply_editable, TRUE, NULL },
1991 { "/Attributes/Not editable", NULL, do_apply_editable, FALSE, NULL },
1992 { "/Attributes/Invisible", NULL, do_apply_invisible, FALSE, NULL },
1993 { "/Attributes/Visible", NULL, do_apply_invisible, TRUE, NULL },
1994 { "/Attributes/Rise", NULL, do_apply_rise, FALSE, NULL },
1995 { "/Attributes/Large", NULL, do_apply_large, FALSE, NULL },
1996 { "/Attributes/Indent", NULL, do_apply_indent, FALSE, NULL },
1997 { "/Attributes/Margins", NULL, do_apply_margin, FALSE, NULL },
1998 { "/Attributes/Custom tabs", NULL, do_apply_tabs, FALSE, NULL },
1999 { "/Attributes/Default tabs", NULL, do_apply_tabs, TRUE, NULL },
2000 { "/Attributes/Color cycles", NULL, do_apply_colors, TRUE, NULL },
2001 { "/Attributes/No colors", NULL, do_apply_colors, FALSE, NULL },
2002 { "/Attributes/Remove all tags", NULL, do_remove_tags, 0, NULL },
2003 { "/Attributes/Properties", NULL, do_properties, 0, NULL },
2004 { "/Attributes/Rich Text copy & drag", NULL, do_rich_text, 0, NULL },
2005 { "/Attributes/Rich Text paste & drop", NULL, do_rich_text, 1, NULL },
2006 { "/_Test", NULL, NULL, 0, "<Branch>" },
2007 { "/Test/_Example", NULL, do_example, 0, NULL },
2008 { "/Test/_Insert and scroll", NULL, do_insert_and_scroll, 0, NULL },
2009 { "/Test/_Add fixed children", NULL, do_add_children, 0, NULL },
2010 { "/Test/A_dd focusable children", NULL, do_add_focus_children, 0, NULL },
2013 static gboolean
2014 save_buffer (Buffer *buffer)
2016 GtkTextIter start, end;
2017 gchar *chars;
2018 gboolean result = FALSE;
2019 gboolean have_backup = FALSE;
2020 gchar *bak_filename;
2021 FILE *file;
2023 g_return_val_if_fail (buffer->filename != NULL, FALSE);
2025 bak_filename = g_strconcat (buffer->filename, "~", NULL);
2027 if (rename (buffer->filename, bak_filename) != 0)
2029 if (errno != ENOENT)
2031 gchar *err = g_strdup_printf ("Cannot back up '%s' to '%s': %s",
2032 buffer->filename, bak_filename, g_strerror (errno));
2033 msgbox_run (NULL, err, "OK", NULL, NULL, 0);
2034 g_free (err);
2035 return FALSE;
2038 else
2039 have_backup = TRUE;
2041 file = fopen (buffer->filename, "w");
2042 if (!file)
2044 gchar *err = g_strdup_printf ("Cannot back up '%s' to '%s': %s",
2045 buffer->filename, bak_filename, g_strerror (errno));
2046 msgbox_run (NULL, err, "OK", NULL, NULL, 0);
2048 else
2050 gtk_text_buffer_get_iter_at_offset (buffer->buffer, &start, 0);
2051 gtk_text_buffer_get_end_iter (buffer->buffer, &end);
2053 chars = gtk_text_buffer_get_slice (buffer->buffer, &start, &end, FALSE);
2055 if (fputs (chars, file) == EOF ||
2056 fclose (file) == EOF)
2058 gchar *err = g_strdup_printf ("Error writing to '%s': %s",
2059 buffer->filename, g_strerror (errno));
2060 msgbox_run (NULL, err, "OK", NULL, NULL, 0);
2061 g_free (err);
2063 else
2065 /* Success
2067 result = TRUE;
2068 gtk_text_buffer_set_modified (buffer->buffer, FALSE);
2071 g_free (chars);
2074 if (!result && have_backup)
2076 if (rename (bak_filename, buffer->filename) != 0)
2078 gchar *err = g_strdup_printf ("Error restoring backup file '%s' to '%s': %s\nBackup left as '%s'",
2079 buffer->filename, bak_filename, g_strerror (errno), bak_filename);
2080 msgbox_run (NULL, err, "OK", NULL, NULL, 0);
2081 g_free (err);
2085 g_free (bak_filename);
2087 return result;
2090 static gboolean
2091 save_as_ok_func (const char *filename, gpointer data)
2093 Buffer *buffer = data;
2094 char *old_filename = buffer->filename;
2096 if (!buffer->filename || strcmp (filename, buffer->filename) != 0)
2098 struct stat statbuf;
2100 if (stat (filename, &statbuf) == 0)
2102 gchar *err = g_strdup_printf ("Ovewrite existing file '%s'?", filename);
2103 gint result = msgbox_run (NULL, err, "Yes", "No", NULL, 1);
2104 g_free (err);
2106 if (result != 0)
2107 return FALSE;
2111 buffer->filename = g_strdup (filename);
2113 if (save_buffer (buffer))
2115 g_free (old_filename);
2116 buffer_filename_set (buffer);
2117 return TRUE;
2119 else
2121 g_free (buffer->filename);
2122 buffer->filename = old_filename;
2123 return FALSE;
2127 static gboolean
2128 save_as_buffer (Buffer *buffer)
2130 return filesel_run (NULL, "Save File", NULL, save_as_ok_func, buffer);
2133 static gboolean
2134 check_buffer_saved (Buffer *buffer)
2136 if (gtk_text_buffer_get_modified (buffer->buffer))
2138 char *pretty_name = buffer_pretty_name (buffer);
2139 char *msg = g_strdup_printf ("Save changes to '%s'?", pretty_name);
2140 gint result;
2142 g_free (pretty_name);
2144 result = msgbox_run (NULL, msg, "Yes", "No", "Cancel", 0);
2145 g_free (msg);
2147 if (result == 0)
2148 return save_as_buffer (buffer);
2149 else if (result == 1)
2150 return TRUE;
2151 else
2152 return FALSE;
2154 else
2155 return TRUE;
2158 #define N_COLORS 16
2160 static Buffer *
2161 create_buffer (void)
2163 Buffer *buffer;
2164 PangoTabArray *tabs;
2165 gint i;
2167 buffer = g_new (Buffer, 1);
2169 buffer->buffer = gtk_text_buffer_new (NULL);
2171 buffer->refcount = 1;
2172 buffer->filename = NULL;
2173 buffer->untitled_serial = -1;
2175 buffer->color_tags = NULL;
2176 buffer->color_cycle_timeout = 0;
2177 buffer->start_hue = 0.0;
2179 i = 0;
2180 while (i < N_COLORS)
2182 GtkTextTag *tag;
2184 tag = gtk_text_buffer_create_tag (buffer->buffer, NULL, NULL);
2186 buffer->color_tags = g_slist_prepend (buffer->color_tags, tag);
2188 ++i;
2191 #if 1
2192 buffer->invisible_tag = gtk_text_buffer_create_tag (buffer->buffer, NULL,
2193 "invisible", TRUE, NULL);
2194 #endif
2196 buffer->not_editable_tag =
2197 gtk_text_buffer_create_tag (buffer->buffer, NULL,
2198 "editable", FALSE,
2199 "foreground", "purple", NULL);
2201 buffer->found_text_tag = gtk_text_buffer_create_tag (buffer->buffer, NULL,
2202 "foreground", "red", NULL);
2204 buffer->rise_tag = gtk_text_buffer_create_tag (buffer->buffer, NULL,
2205 "rise", 10 * PANGO_SCALE, NULL);
2207 buffer->large_tag = gtk_text_buffer_create_tag (buffer->buffer, NULL,
2208 "scale", PANGO_SCALE_X_LARGE, NULL);
2210 buffer->indent_tag = gtk_text_buffer_create_tag (buffer->buffer, NULL,
2211 "indent", 20, NULL);
2213 buffer->margin_tag = gtk_text_buffer_create_tag (buffer->buffer, NULL,
2214 "left_margin", 20, "right_margin", 20, NULL);
2216 tabs = pango_tab_array_new_with_positions (4,
2217 TRUE,
2218 PANGO_TAB_LEFT, 10,
2219 PANGO_TAB_LEFT, 30,
2220 PANGO_TAB_LEFT, 60,
2221 PANGO_TAB_LEFT, 120);
2223 buffer->custom_tabs_tag = gtk_text_buffer_create_tag (buffer->buffer, NULL,
2224 "tabs", tabs,
2225 "foreground", "green", NULL);
2227 pango_tab_array_free (tabs);
2229 buffers = g_slist_prepend (buffers, buffer);
2231 return buffer;
2234 static char *
2235 buffer_pretty_name (Buffer *buffer)
2237 if (buffer->filename)
2239 char *p;
2240 char *result = g_path_get_basename (buffer->filename);
2241 p = strchr (result, '/');
2242 if (p)
2243 *p = '\0';
2245 return result;
2247 else
2249 if (buffer->untitled_serial == -1)
2250 buffer->untitled_serial = untitled_serial++;
2252 if (buffer->untitled_serial == 1)
2253 return g_strdup ("Untitled");
2254 else
2255 return g_strdup_printf ("Untitled #%d", buffer->untitled_serial);
2259 static void
2260 buffer_filename_set (Buffer *buffer)
2262 GSList *tmp_list = views;
2264 while (tmp_list)
2266 View *view = tmp_list->data;
2268 if (view->buffer == buffer)
2269 view_set_title (view);
2271 tmp_list = tmp_list->next;
2275 static void
2276 buffer_search (Buffer *buffer,
2277 const char *str,
2278 View *view,
2279 gboolean forward)
2281 GtkTextIter iter;
2282 GtkTextIter start, end;
2283 GtkWidget *dialog;
2284 int i;
2286 /* remove tag from whole buffer */
2287 gtk_text_buffer_get_bounds (buffer->buffer, &start, &end);
2288 gtk_text_buffer_remove_tag (buffer->buffer, buffer->found_text_tag,
2289 &start, &end );
2291 gtk_text_buffer_get_iter_at_mark (buffer->buffer, &iter,
2292 gtk_text_buffer_get_mark (buffer->buffer,
2293 "insert"));
2295 i = 0;
2296 if (*str != '\0')
2298 GtkTextIter match_start, match_end;
2300 if (forward)
2302 while (gtk_text_iter_forward_search (&iter, str,
2303 GTK_TEXT_SEARCH_VISIBLE_ONLY |
2304 GTK_TEXT_SEARCH_TEXT_ONLY,
2305 &match_start, &match_end,
2306 NULL))
2308 ++i;
2309 gtk_text_buffer_apply_tag (buffer->buffer, buffer->found_text_tag,
2310 &match_start, &match_end);
2312 iter = match_end;
2315 else
2317 while (gtk_text_iter_backward_search (&iter, str,
2318 GTK_TEXT_SEARCH_VISIBLE_ONLY |
2319 GTK_TEXT_SEARCH_TEXT_ONLY,
2320 &match_start, &match_end,
2321 NULL))
2323 ++i;
2324 gtk_text_buffer_apply_tag (buffer->buffer, buffer->found_text_tag,
2325 &match_start, &match_end);
2327 iter = match_start;
2332 dialog = gtk_message_dialog_new (GTK_WINDOW (view->window),
2333 GTK_DIALOG_DESTROY_WITH_PARENT,
2334 GTK_MESSAGE_INFO,
2335 GTK_BUTTONS_OK,
2336 "%d strings found and marked in red",
2339 g_signal_connect_swapped (dialog,
2340 "response",
2341 G_CALLBACK (gtk_widget_destroy), dialog);
2343 gtk_widget_show (dialog);
2346 static void
2347 buffer_search_forward (Buffer *buffer, const char *str,
2348 View *view)
2350 buffer_search (buffer, str, view, TRUE);
2353 static void
2354 buffer_search_backward (Buffer *buffer, const char *str,
2355 View *view)
2357 buffer_search (buffer, str, view, FALSE);
2360 static void
2361 buffer_ref (Buffer *buffer)
2363 buffer->refcount++;
2366 static void
2367 buffer_unref (Buffer *buffer)
2369 buffer->refcount--;
2370 if (buffer->refcount == 0)
2372 buffer_set_colors (buffer, FALSE);
2373 buffers = g_slist_remove (buffers, buffer);
2374 g_object_unref (buffer->buffer);
2375 g_free (buffer->filename);
2376 g_free (buffer);
2380 static void
2381 hsv_to_rgb (gdouble *h,
2382 gdouble *s,
2383 gdouble *v)
2385 gdouble hue, saturation, value;
2386 gdouble f, p, q, t;
2388 if (*s == 0.0)
2390 *h = *v;
2391 *s = *v;
2392 *v = *v; /* heh */
2394 else
2396 hue = *h * 6.0;
2397 saturation = *s;
2398 value = *v;
2400 if (hue >= 6.0)
2401 hue = 0.0;
2403 f = hue - (int) hue;
2404 p = value * (1.0 - saturation);
2405 q = value * (1.0 - saturation * f);
2406 t = value * (1.0 - saturation * (1.0 - f));
2408 switch ((int) hue)
2410 case 0:
2411 *h = value;
2412 *s = t;
2413 *v = p;
2414 break;
2416 case 1:
2417 *h = q;
2418 *s = value;
2419 *v = p;
2420 break;
2422 case 2:
2423 *h = p;
2424 *s = value;
2425 *v = t;
2426 break;
2428 case 3:
2429 *h = p;
2430 *s = q;
2431 *v = value;
2432 break;
2434 case 4:
2435 *h = t;
2436 *s = p;
2437 *v = value;
2438 break;
2440 case 5:
2441 *h = value;
2442 *s = p;
2443 *v = q;
2444 break;
2446 default:
2447 g_assert_not_reached ();
2452 static void
2453 hue_to_color (gdouble hue,
2454 GdkColor *color)
2456 gdouble h, s, v;
2458 h = hue;
2459 s = 1.0;
2460 v = 1.0;
2462 g_return_if_fail (hue <= 1.0);
2464 hsv_to_rgb (&h, &s, &v);
2466 color->red = h * 65535;
2467 color->green = s * 65535;
2468 color->blue = v * 65535;
2472 static gint
2473 color_cycle_timeout (gpointer data)
2475 Buffer *buffer = data;
2477 buffer_cycle_colors (buffer);
2479 return TRUE;
2482 static void
2483 buffer_set_colors (Buffer *buffer,
2484 gboolean enabled)
2486 GSList *tmp;
2487 gdouble hue = 0.0;
2489 if (enabled && buffer->color_cycle_timeout == 0)
2490 buffer->color_cycle_timeout = gdk_threads_add_timeout (200, color_cycle_timeout, buffer);
2491 else if (!enabled && buffer->color_cycle_timeout != 0)
2493 g_source_remove (buffer->color_cycle_timeout);
2494 buffer->color_cycle_timeout = 0;
2497 tmp = buffer->color_tags;
2498 while (tmp != NULL)
2500 if (enabled)
2502 GdkColor color;
2504 hue_to_color (hue, &color);
2506 g_object_set (tmp->data,
2507 "foreground_gdk", &color,
2508 NULL);
2510 else
2511 g_object_set (tmp->data,
2512 "foreground_set", FALSE,
2513 NULL);
2515 hue += 1.0 / N_COLORS;
2517 tmp = g_slist_next (tmp);
2521 static void
2522 buffer_cycle_colors (Buffer *buffer)
2524 GSList *tmp;
2525 gdouble hue = buffer->start_hue;
2527 tmp = buffer->color_tags;
2528 while (tmp != NULL)
2530 GdkColor color;
2532 hue_to_color (hue, &color);
2534 g_object_set (tmp->data,
2535 "foreground_gdk", &color,
2536 NULL);
2538 hue += 1.0 / N_COLORS;
2539 if (hue > 1.0)
2540 hue = 0.0;
2542 tmp = g_slist_next (tmp);
2545 buffer->start_hue += 1.0 / N_COLORS;
2546 if (buffer->start_hue > 1.0)
2547 buffer->start_hue = 0.0;
2550 static void
2551 close_view (View *view)
2553 views = g_slist_remove (views, view);
2554 buffer_unref (view->buffer);
2555 gtk_widget_destroy (view->window);
2556 g_object_unref (view->item_factory);
2558 g_free (view);
2560 if (!views)
2561 gtk_main_quit ();
2564 static void
2565 check_close_view (View *view)
2567 if (view->buffer->refcount > 1 ||
2568 check_buffer_saved (view->buffer))
2569 close_view (view);
2572 static void
2573 view_set_title (View *view)
2575 char *pretty_name = buffer_pretty_name (view->buffer);
2576 char *title = g_strconcat ("testtext - ", pretty_name, NULL);
2578 gtk_window_set_title (GTK_WINDOW (view->window), title);
2580 g_free (pretty_name);
2581 g_free (title);
2584 static void
2585 cursor_set_callback (GtkTextBuffer *buffer,
2586 const GtkTextIter *location,
2587 GtkTextMark *mark,
2588 gpointer user_data)
2590 GtkTextView *text_view;
2592 /* Redraw tab windows if the cursor moves
2593 * on the mapped widget (windows may not exist before realization...
2596 text_view = GTK_TEXT_VIEW (user_data);
2598 if (GTK_WIDGET_MAPPED (text_view) &&
2599 mark == gtk_text_buffer_get_insert (buffer))
2601 GdkWindow *tab_window;
2603 tab_window = gtk_text_view_get_window (text_view,
2604 GTK_TEXT_WINDOW_TOP);
2606 gdk_window_invalidate_rect (tab_window, NULL, FALSE);
2608 tab_window = gtk_text_view_get_window (text_view,
2609 GTK_TEXT_WINDOW_BOTTOM);
2611 gdk_window_invalidate_rect (tab_window, NULL, FALSE);
2615 static gint
2616 tab_stops_expose (GtkWidget *widget,
2617 GdkEventExpose *event,
2618 gpointer user_data)
2620 gint first_x;
2621 gint last_x;
2622 gint i;
2623 GdkWindow *top_win;
2624 GdkWindow *bottom_win;
2625 GtkTextView *text_view;
2626 GtkTextWindowType type;
2627 GdkDrawable *target;
2628 gint *positions = NULL;
2629 gint size;
2630 GtkTextAttributes *attrs;
2631 GtkTextIter insert;
2632 GtkTextBuffer *buffer;
2633 gboolean in_pixels;
2635 text_view = GTK_TEXT_VIEW (widget);
2637 /* See if this expose is on the tab stop window */
2638 top_win = gtk_text_view_get_window (text_view,
2639 GTK_TEXT_WINDOW_TOP);
2641 bottom_win = gtk_text_view_get_window (text_view,
2642 GTK_TEXT_WINDOW_BOTTOM);
2644 if (event->window == top_win)
2646 type = GTK_TEXT_WINDOW_TOP;
2647 target = top_win;
2649 else if (event->window == bottom_win)
2651 type = GTK_TEXT_WINDOW_BOTTOM;
2652 target = bottom_win;
2654 else
2655 return FALSE;
2657 first_x = event->area.x;
2658 last_x = first_x + event->area.width;
2660 gtk_text_view_window_to_buffer_coords (text_view,
2661 type,
2662 first_x,
2664 &first_x,
2665 NULL);
2667 gtk_text_view_window_to_buffer_coords (text_view,
2668 type,
2669 last_x,
2671 &last_x,
2672 NULL);
2674 buffer = gtk_text_view_get_buffer (text_view);
2676 gtk_text_buffer_get_iter_at_mark (buffer,
2677 &insert,
2678 gtk_text_buffer_get_mark (buffer,
2679 "insert"));
2681 attrs = gtk_text_attributes_new ();
2683 gtk_text_iter_get_attributes (&insert, attrs);
2685 if (attrs->tabs)
2687 size = pango_tab_array_get_size (attrs->tabs);
2689 pango_tab_array_get_tabs (attrs->tabs,
2690 NULL,
2691 &positions);
2693 in_pixels = pango_tab_array_get_positions_in_pixels (attrs->tabs);
2695 else
2697 size = 0;
2698 in_pixels = FALSE;
2701 gtk_text_attributes_unref (attrs);
2703 i = 0;
2704 while (i < size)
2706 gint pos;
2708 if (!in_pixels)
2709 positions[i] = PANGO_PIXELS (positions[i]);
2711 gtk_text_view_buffer_to_window_coords (text_view,
2712 type,
2713 positions[i],
2715 &pos,
2716 NULL);
2718 gdk_draw_line (target,
2719 widget->style->fg_gc [widget->state],
2720 pos, 0,
2721 pos, 15);
2723 ++i;
2726 g_free (positions);
2728 return TRUE;
2731 static void
2732 get_lines (GtkTextView *text_view,
2733 gint first_y,
2734 gint last_y,
2735 GArray *buffer_coords,
2736 GArray *numbers,
2737 gint *countp)
2739 GtkTextIter iter;
2740 gint count;
2741 gint size;
2743 g_array_set_size (buffer_coords, 0);
2744 g_array_set_size (numbers, 0);
2746 /* Get iter at first y */
2747 gtk_text_view_get_line_at_y (text_view, &iter, first_y, NULL);
2749 /* For each iter, get its location and add it to the arrays.
2750 * Stop when we pass last_y
2752 count = 0;
2753 size = 0;
2755 while (!gtk_text_iter_is_end (&iter))
2757 gint y, height;
2758 gint line_num;
2760 gtk_text_view_get_line_yrange (text_view, &iter, &y, &height);
2762 g_array_append_val (buffer_coords, y);
2763 line_num = gtk_text_iter_get_line (&iter);
2764 g_array_append_val (numbers, line_num);
2766 ++count;
2768 if ((y + height) >= last_y)
2769 break;
2771 gtk_text_iter_forward_line (&iter);
2774 *countp = count;
2777 static gint
2778 line_numbers_expose (GtkWidget *widget,
2779 GdkEventExpose *event,
2780 gpointer user_data)
2782 gint count;
2783 GArray *numbers;
2784 GArray *pixels;
2785 gint first_y;
2786 gint last_y;
2787 gint i;
2788 GdkWindow *left_win;
2789 GdkWindow *right_win;
2790 PangoLayout *layout;
2791 GtkTextView *text_view;
2792 GtkTextWindowType type;
2793 GdkDrawable *target;
2795 text_view = GTK_TEXT_VIEW (widget);
2797 /* See if this expose is on the line numbers window */
2798 left_win = gtk_text_view_get_window (text_view,
2799 GTK_TEXT_WINDOW_LEFT);
2801 right_win = gtk_text_view_get_window (text_view,
2802 GTK_TEXT_WINDOW_RIGHT);
2804 if (event->window == left_win)
2806 type = GTK_TEXT_WINDOW_LEFT;
2807 target = left_win;
2809 else if (event->window == right_win)
2811 type = GTK_TEXT_WINDOW_RIGHT;
2812 target = right_win;
2814 else
2815 return FALSE;
2817 first_y = event->area.y;
2818 last_y = first_y + event->area.height;
2820 gtk_text_view_window_to_buffer_coords (text_view,
2821 type,
2823 first_y,
2824 NULL,
2825 &first_y);
2827 gtk_text_view_window_to_buffer_coords (text_view,
2828 type,
2830 last_y,
2831 NULL,
2832 &last_y);
2834 numbers = g_array_new (FALSE, FALSE, sizeof (gint));
2835 pixels = g_array_new (FALSE, FALSE, sizeof (gint));
2837 get_lines (text_view,
2838 first_y,
2839 last_y,
2840 pixels,
2841 numbers,
2842 &count);
2844 /* Draw fully internationalized numbers! */
2846 layout = gtk_widget_create_pango_layout (widget, "");
2848 i = 0;
2849 while (i < count)
2851 gint pos;
2852 gchar *str;
2854 gtk_text_view_buffer_to_window_coords (text_view,
2855 type,
2857 g_array_index (pixels, gint, i),
2858 NULL,
2859 &pos);
2861 str = g_strdup_printf ("%d", g_array_index (numbers, gint, i));
2863 pango_layout_set_text (layout, str, -1);
2865 gtk_paint_layout (widget->style,
2866 target,
2867 GTK_WIDGET_STATE (widget),
2868 FALSE,
2869 NULL,
2870 widget,
2871 NULL,
2872 2, pos + 2,
2873 layout);
2875 g_free (str);
2877 ++i;
2880 g_array_free (pixels, TRUE);
2881 g_array_free (numbers, TRUE);
2883 g_object_unref (layout);
2885 /* don't stop emission, need to draw children */
2886 return FALSE;
2889 static void
2890 selection_changed (GtkTextBuffer *buffer,
2891 GParamSpec *pspec,
2892 GtkWidget *copy_menu)
2894 gtk_widget_set_sensitive (copy_menu, gtk_text_buffer_get_has_selection (buffer));
2897 static View *
2898 create_view (Buffer *buffer)
2900 View *view;
2901 GtkWidget *copy_menu;
2902 GtkWidget *sw;
2903 GtkWidget *vbox;
2905 view = g_new0 (View, 1);
2906 views = g_slist_prepend (views, view);
2908 view->buffer = buffer;
2909 buffer_ref (buffer);
2911 view->window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2912 g_object_set_data (G_OBJECT (view->window), "view", view);
2914 g_signal_connect (view->window, "delete_event",
2915 G_CALLBACK (delete_event_cb), NULL);
2917 view->accel_group = gtk_accel_group_new ();
2918 view->item_factory = gtk_item_factory_new (GTK_TYPE_MENU_BAR, "<main>", view->accel_group);
2919 g_object_set_data (G_OBJECT (view->item_factory), "view", view);
2921 gtk_item_factory_create_items (view->item_factory, G_N_ELEMENTS (menu_items), menu_items, view);
2923 /* make the Copy menu item sensitivity update according to the selection */
2924 copy_menu = gtk_item_factory_get_item (view->item_factory, "<main>/Edit/Copy");
2925 gtk_widget_set_sensitive (copy_menu, gtk_text_buffer_get_has_selection (view->buffer->buffer));
2926 g_signal_connect (view->buffer->buffer,
2927 "notify::has-selection",
2928 G_CALLBACK (selection_changed),
2929 copy_menu);
2931 gtk_window_add_accel_group (GTK_WINDOW (view->window), view->accel_group);
2933 vbox = gtk_vbox_new (FALSE, 0);
2934 gtk_container_add (GTK_CONTAINER (view->window), vbox);
2936 gtk_box_pack_start (GTK_BOX (vbox),
2937 gtk_item_factory_get_widget (view->item_factory, "<main>"),
2938 FALSE, FALSE, 0);
2940 sw = gtk_scrolled_window_new (NULL, NULL);
2941 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
2942 GTK_POLICY_AUTOMATIC,
2943 GTK_POLICY_AUTOMATIC);
2945 view->text_view = gtk_text_view_new_with_buffer (buffer->buffer);
2946 gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (view->text_view),
2947 GTK_WRAP_WORD);
2949 /* Make sure border width works, no real reason to do this other than testing */
2950 gtk_container_set_border_width (GTK_CONTAINER (view->text_view),
2951 10);
2953 /* Draw tab stops in the top and bottom windows. */
2955 gtk_text_view_set_border_window_size (GTK_TEXT_VIEW (view->text_view),
2956 GTK_TEXT_WINDOW_TOP,
2957 15);
2959 gtk_text_view_set_border_window_size (GTK_TEXT_VIEW (view->text_view),
2960 GTK_TEXT_WINDOW_BOTTOM,
2961 15);
2963 g_signal_connect (view->text_view,
2964 "expose_event",
2965 G_CALLBACK (tab_stops_expose),
2966 NULL);
2968 g_signal_connect (view->buffer->buffer,
2969 "mark_set",
2970 G_CALLBACK (cursor_set_callback),
2971 view->text_view);
2973 /* Draw line numbers in the side windows; we should really be
2974 * more scientific about what width we set them to.
2976 gtk_text_view_set_border_window_size (GTK_TEXT_VIEW (view->text_view),
2977 GTK_TEXT_WINDOW_RIGHT,
2978 30);
2980 gtk_text_view_set_border_window_size (GTK_TEXT_VIEW (view->text_view),
2981 GTK_TEXT_WINDOW_LEFT,
2982 30);
2984 g_signal_connect (view->text_view,
2985 "expose_event",
2986 G_CALLBACK (line_numbers_expose),
2987 NULL);
2989 gtk_box_pack_start (GTK_BOX (vbox), sw, TRUE, TRUE, 0);
2990 gtk_container_add (GTK_CONTAINER (sw), view->text_view);
2992 gtk_window_set_default_size (GTK_WINDOW (view->window), 500, 500);
2994 gtk_widget_grab_focus (view->text_view);
2996 view_set_title (view);
2997 view_init_menus (view);
2999 view_add_example_widgets (view);
3001 gtk_widget_show_all (view->window);
3002 return view;
3005 static void
3006 view_add_example_widgets (View *view)
3008 GtkTextChildAnchor *anchor;
3009 Buffer *buffer;
3011 buffer = view->buffer;
3013 anchor = g_object_get_data (G_OBJECT (buffer->buffer),
3014 "anchor");
3016 if (anchor && !gtk_text_child_anchor_get_deleted (anchor))
3018 GtkWidget *widget;
3020 widget = gtk_button_new_with_label ("Foo");
3022 gtk_text_view_add_child_at_anchor (GTK_TEXT_VIEW (view->text_view),
3023 widget,
3024 anchor);
3026 gtk_widget_show (widget);
3030 void
3031 test_init (void)
3033 if (g_file_test ("../gdk-pixbuf/libpixbufloader-pnm.la",
3034 G_FILE_TEST_EXISTS))
3036 g_setenv ("GDK_PIXBUF_MODULE_FILE", "../gdk-pixbuf/gdk-pixbuf.loaders", TRUE);
3037 g_setenv ("GTK_IM_MODULE_FILE", "../modules/input/gtk.immodules", TRUE);
3042 main (int argc, char** argv)
3044 Buffer *buffer;
3045 View *view;
3046 int i;
3048 test_init ();
3049 gtk_init (&argc, &argv);
3051 buffer = create_buffer ();
3052 view = create_view (buffer);
3053 buffer_unref (buffer);
3055 push_active_window (GTK_WINDOW (view->window));
3056 for (i=1; i < argc; i++)
3058 char *filename;
3060 /* Quick and dirty canonicalization - better should be in GLib
3063 if (!g_path_is_absolute (argv[i]))
3065 char *cwd = g_get_current_dir ();
3066 filename = g_strconcat (cwd, "/", argv[i], NULL);
3067 g_free (cwd);
3069 else
3070 filename = argv[i];
3072 open_ok_func (filename, view);
3074 if (filename != argv[i])
3075 g_free (filename);
3077 pop_active_window ();
3079 gtk_main ();
3081 return 0;