]> Pileus Git - ~andy/gtk/blob - tests/testtext.c
Use gtk_box_new() instead gtk_[v|h]box_new()
[~andy/gtk] / tests / testtext.c
1 /* testtext.c
2  * Copyright (C) 2000 Red Hat, Inc
3  * Author: Havoc Pennington
4  *
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.
9  *
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.
14  *
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.
19  */
20
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>
27
28 #include <gtk/gtk.h>
29 #include <gdk/gdkkeysyms.h>
30
31 #include "prop-editor.h"
32
33 typedef struct _Buffer Buffer;
34 typedef struct _View View;
35
36 static gint untitled_serial = 1;
37
38 GSList *active_window_stack = NULL;
39
40 struct _Buffer
41 {
42   gint refcount;
43   GtkTextBuffer *buffer;
44   char *filename;
45   gint untitled_serial;
46   GtkTextTag *invisible_tag;
47   GtkTextTag *not_editable_tag;
48   GtkTextTag *found_text_tag;
49   GtkTextTag *rise_tag;
50   GtkTextTag *large_tag;
51   GtkTextTag *indent_tag;
52   GtkTextTag *margin_tag;
53   GtkTextTag *custom_tabs_tag;
54   GSList *color_tags;
55   guint color_cycle_timeout;
56   gdouble start_hue;
57 };
58
59 struct _View
60 {
61   GtkWidget *window;
62   GtkWidget *text_view;
63   GtkAccelGroup *accel_group;
64   GtkItemFactory *item_factory;
65   Buffer *buffer;
66 };
67
68 static void push_active_window (GtkWindow *window);
69 static void pop_active_window (void);
70 static GtkWindow *get_active_window (void);
71
72 static Buffer * create_buffer      (void);
73 static gboolean check_buffer_saved (Buffer *buffer);
74 static gboolean save_buffer        (Buffer *buffer);
75 static gboolean save_as_buffer     (Buffer *buffer);
76 static char *   buffer_pretty_name (Buffer *buffer);
77 static void     buffer_filename_set (Buffer *buffer);
78 static void     buffer_search_forward (Buffer *buffer,
79                                        const char *str,
80                                        View *view);
81 static void     buffer_search_backward (Buffer *buffer,
82                                        const char *str,
83                                        View *view);
84 static void     buffer_set_colors      (Buffer  *buffer,
85                                         gboolean enabled);
86 static void     buffer_cycle_colors    (Buffer  *buffer);
87
88 static View *view_from_widget (GtkWidget *widget);
89
90 static View *create_view      (Buffer *buffer);
91 static void  check_close_view (View   *view);
92 static void  close_view       (View   *view);
93 static void  view_set_title   (View   *view);
94 static void  view_init_menus  (View   *view);
95 static void  view_add_example_widgets (View *view);
96
97 GSList *buffers = NULL;
98 GSList *views = NULL;
99
100 static void
101 push_active_window (GtkWindow *window)
102 {
103   g_object_ref (window);
104   active_window_stack = g_slist_prepend (active_window_stack, window);
105 }
106
107 static void
108 pop_active_window (void)
109 {
110   g_object_unref (active_window_stack->data);
111   active_window_stack = g_slist_delete_link (active_window_stack, active_window_stack);
112 }
113
114 static GtkWindow *
115 get_active_window (void)
116 {
117   if (active_window_stack)
118     return active_window_stack->data;
119   else
120     return NULL;
121 }
122
123 /*
124  * Filesel utility function
125  */
126
127 typedef gboolean (*FileselOKFunc) (const char *filename, gpointer data);
128
129 static void
130 filesel_ok_cb (GtkWidget *button, GtkWidget *filesel)
131 {
132   FileselOKFunc ok_func = (FileselOKFunc)g_object_get_data (G_OBJECT (filesel), "ok-func");
133   gpointer data = g_object_get_data (G_OBJECT (filesel), "ok-data");
134   gint *result = g_object_get_data (G_OBJECT (filesel), "ok-result");
135   
136   gtk_widget_hide (filesel);
137   
138   if ((*ok_func) (gtk_file_selection_get_filename (GTK_FILE_SELECTION (filesel)), data))
139     {
140       gtk_widget_destroy (filesel);
141       *result = TRUE;
142     }
143   else
144     gtk_widget_show (filesel);
145 }
146
147 gboolean
148 filesel_run (GtkWindow    *parent, 
149              const char   *title,
150              const char   *start_file,
151              FileselOKFunc func,
152              gpointer      data)
153 {
154   GtkWidget *filesel = gtk_file_selection_new (title);
155   gboolean result = FALSE;
156
157   if (!parent)
158     parent = get_active_window ();
159   
160   if (parent)
161     gtk_window_set_transient_for (GTK_WINDOW (filesel), parent);
162
163   if (start_file)
164     gtk_file_selection_set_filename (GTK_FILE_SELECTION (filesel), start_file);
165
166   
167   g_object_set_data (G_OBJECT (filesel), "ok-func", func);
168   g_object_set_data (G_OBJECT (filesel), "ok-data", data);
169   g_object_set_data (G_OBJECT (filesel), "ok-result", &result);
170
171   g_signal_connect (GTK_FILE_SELECTION (filesel)->ok_button,
172                     "clicked",
173                     G_CALLBACK (filesel_ok_cb), filesel);
174   g_signal_connect_swapped (GTK_FILE_SELECTION (filesel)->cancel_button,
175                             "clicked",
176                             G_CALLBACK (gtk_widget_destroy), filesel);
177
178   g_signal_connect (filesel, "destroy",
179                     G_CALLBACK (gtk_main_quit), NULL);
180   gtk_window_set_modal (GTK_WINDOW (filesel), TRUE);
181
182   gtk_widget_show (filesel);
183   gtk_main ();
184
185   return result;
186 }
187
188 /*
189  * MsgBox utility functions
190  */
191
192 static void
193 msgbox_yes_cb (GtkWidget *widget, gboolean *result)
194 {
195   *result = 0;
196   gtk_widget_destroy (gtk_widget_get_toplevel (widget));
197 }
198
199 static void
200 msgbox_no_cb (GtkWidget *widget, gboolean *result)
201 {
202   *result = 1;
203   gtk_widget_destroy (gtk_widget_get_toplevel (widget));
204 }
205
206 static gboolean
207 msgbox_key_press_cb (GtkWidget *widget, GdkEventKey *event, gpointer data)
208 {
209   if (event->keyval == GDK_Escape)
210     {
211       g_signal_stop_emission_by_name (widget, "key_press_event");
212       gtk_widget_destroy (widget);
213       return TRUE;
214     }
215
216   return FALSE;
217 }
218
219 /* Don't copy this example, it's all crack-smoking - you can just use
220  * GtkMessageDialog now
221  */
222 gint
223 msgbox_run (GtkWindow  *parent,
224             const char *message,
225             const char *yes_button,
226             const char *no_button,
227             const char *cancel_button,
228             gint default_index)
229 {
230   gboolean result = -1;
231   GtkWidget *dialog;
232   GtkWidget *button;
233   GtkWidget *label;
234   GtkWidget *vbox;
235   GtkWidget *button_box;
236   GtkWidget *separator;
237
238   g_return_val_if_fail (message != NULL, FALSE);
239   g_return_val_if_fail (default_index >= 0 && default_index <= 1, FALSE);
240
241   if (!parent)
242     parent = get_active_window ();
243   
244   /* Create a dialog
245    */
246   dialog = gtk_window_new (GTK_WINDOW_TOPLEVEL);
247   gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
248   if (parent)
249     gtk_window_set_transient_for (GTK_WINDOW (dialog), parent);
250   gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
251
252   /* Quit our recursive main loop when the dialog is destroyed.
253    */
254   g_signal_connect (dialog, "destroy",
255                     G_CALLBACK (gtk_main_quit), NULL);
256
257   /* Catch Escape key presses and have them destroy the dialog
258    */
259   g_signal_connect (dialog, "key_press_event",
260                     G_CALLBACK (msgbox_key_press_cb), NULL);
261
262   /* Fill in the contents of the widget
263    */
264   vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, FALSE, 0);
265   gtk_container_add (GTK_CONTAINER (dialog), vbox);
266   
267   label = gtk_label_new (message);
268   gtk_misc_set_padding (GTK_MISC (label), 12, 12);
269   gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
270   gtk_box_pack_start (GTK_BOX (vbox), label, TRUE, TRUE, 0);
271
272   separator = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL);
273   gtk_box_pack_start (GTK_BOX (vbox), separator, FALSE, FALSE, 0);
274
275   button_box = gtk_button_box_new (GTK_ORIENTATION_HORIZONTAL);
276   gtk_box_pack_start (GTK_BOX (vbox), button_box, FALSE, FALSE, 0);
277   gtk_container_set_border_width (GTK_CONTAINER (button_box), 8);
278   
279
280   /* When Yes is clicked, call the msgbox_yes_cb
281    * This sets the result variable and destroys the dialog
282    */
283   if (yes_button)
284     {
285       button = gtk_button_new_with_label (yes_button);
286       gtk_widget_set_can_default (button, TRUE);
287       gtk_container_add (GTK_CONTAINER (button_box), button);
288
289       if (default_index == 0)
290         gtk_widget_grab_default (button);
291       
292       g_signal_connect (button, "clicked",
293                         G_CALLBACK (msgbox_yes_cb), &result);
294     }
295
296   /* When No is clicked, call the msgbox_no_cb
297    * This sets the result variable and destroys the dialog
298    */
299   if (no_button)
300     {
301       button = gtk_button_new_with_label (no_button);
302       gtk_widget_set_can_default (button, TRUE);
303       gtk_container_add (GTK_CONTAINER (button_box), button);
304
305       if (default_index == 0)
306         gtk_widget_grab_default (button);
307       
308       g_signal_connect (button, "clicked",
309                         G_CALLBACK (msgbox_no_cb), &result);
310     }
311
312   /* When Cancel is clicked, destroy the dialog
313    */
314   if (cancel_button)
315     {
316       button = gtk_button_new_with_label (cancel_button);
317       gtk_widget_set_can_default (button, TRUE);
318       gtk_container_add (GTK_CONTAINER (button_box), button);
319       
320       if (default_index == 1)
321         gtk_widget_grab_default (button);
322       
323       g_signal_connect_swapped (button, "clicked",
324                                 G_CALLBACK (gtk_object_destroy), dialog);
325     }
326
327   gtk_widget_show_all (dialog);
328
329   /* Run a recursive main loop until a button is clicked
330    * or the user destroys the dialog through the window mananger */
331   gtk_main ();
332
333   return result;
334 }
335
336 #ifdef DO_BLINK
337 /*
338  * Example buffer filling code
339  */
340 static gint
341 blink_timeout (gpointer data)
342 {
343   GtkTextTag *tag;
344   static gboolean flip = FALSE;
345   
346   tag = GTK_TEXT_TAG (data);
347
348   g_object_set (tag,
349                  "foreground", flip ? "blue" : "purple",
350                  NULL);
351
352   flip = !flip;
353
354   return TRUE;
355 }
356 #endif
357
358 static gint
359 tag_event_handler (GtkTextTag *tag, GtkWidget *widget, GdkEvent *event,
360                   const GtkTextIter *iter, gpointer user_data)
361 {
362   gint char_index;
363
364   char_index = gtk_text_iter_get_offset (iter);
365   
366   switch (event->type)
367     {
368     case GDK_MOTION_NOTIFY:
369       printf ("Motion event at char %d tag `%s'\n",
370              char_index, tag->name);
371       break;
372         
373     case GDK_BUTTON_PRESS:
374       printf ("Button press at char %d tag `%s'\n",
375              char_index, tag->name);
376       break;
377         
378     case GDK_2BUTTON_PRESS:
379       printf ("Double click at char %d tag `%s'\n",
380              char_index, tag->name);
381       break;
382         
383     case GDK_3BUTTON_PRESS:
384       printf ("Triple click at char %d tag `%s'\n",
385              char_index, tag->name);
386       break;
387         
388     case GDK_BUTTON_RELEASE:
389       printf ("Button release at char %d tag `%s'\n",
390              char_index, tag->name);
391       break;
392         
393     case GDK_KEY_PRESS:
394     case GDK_KEY_RELEASE:
395       printf ("Key event at char %d tag `%s'\n",
396               char_index, tag->name);
397       break;
398       
399     case GDK_ENTER_NOTIFY:
400     case GDK_LEAVE_NOTIFY:
401     case GDK_PROPERTY_NOTIFY:
402     case GDK_SELECTION_CLEAR:
403     case GDK_SELECTION_REQUEST:
404     case GDK_SELECTION_NOTIFY:
405     case GDK_PROXIMITY_IN:
406     case GDK_PROXIMITY_OUT:
407     case GDK_DRAG_ENTER:
408     case GDK_DRAG_LEAVE:
409     case GDK_DRAG_MOTION:
410     case GDK_DRAG_STATUS:
411     case GDK_DROP_START:
412     case GDK_DROP_FINISHED:
413     default:
414       break;
415     }
416
417   return FALSE;
418 }
419
420 static void
421 setup_tag (GtkTextTag *tag)
422 {
423   g_signal_connect (tag,
424                     "event",
425                     G_CALLBACK (tag_event_handler),
426                     NULL);
427 }
428
429 static const char  *book_closed_xpm[] = {
430 "16 16 6 1",
431 "       c None s None",
432 ".      c black",
433 "X      c red",
434 "o      c yellow",
435 "O      c #808080",
436 "#      c white",
437 "                ",
438 "       ..       ",
439 "     ..XX.      ",
440 "   ..XXXXX.     ",
441 " ..XXXXXXXX.    ",
442 ".ooXXXXXXXXX.   ",
443 "..ooXXXXXXXXX.  ",
444 ".X.ooXXXXXXXXX. ",
445 ".XX.ooXXXXXX..  ",
446 " .XX.ooXXX..#O  ",
447 "  .XX.oo..##OO. ",
448 "   .XX..##OO..  ",
449 "    .X.#OO..    ",
450 "     ..O..      ",
451 "      ..        ",
452 "                "};
453
454 void
455 fill_example_buffer (GtkTextBuffer *buffer)
456 {
457   GtkTextIter iter, iter2;
458   GtkTextTag *tag;
459   GtkTextChildAnchor *anchor;
460   GdkColor color;
461   GdkColor color2;
462   GdkPixbuf *pixbuf;
463   int i;
464   char *str;
465   
466   /* FIXME this is broken if called twice on a buffer, since
467    * we try to create tags a second time.
468    */
469   
470   tag = gtk_text_buffer_create_tag (buffer, "fg_blue", NULL);
471
472 #ifdef DO_BLINK
473   g_timeout_add (1000, (GSourceFunc)blink_timeout, tag);
474 #endif     
475  
476   setup_tag (tag);
477   
478   color.red = color.green = 0;
479   color.blue = 0xffff;
480   color2.red = 0xfff;
481   color2.blue = 0x0;
482   color2.green = 0;
483   g_object_set (tag,
484                 "foreground_gdk", &color,
485                 "background_gdk", &color2,
486                 "size_points", 24.0,
487                 NULL);
488
489   tag = gtk_text_buffer_create_tag (buffer, "fg_red", NULL);
490
491   setup_tag (tag);
492       
493   color.blue = color.green = 0;
494   color.red = 0xffff;
495   g_object_set (tag,
496                 "rise", -4 * PANGO_SCALE,
497                 "foreground_gdk", &color,
498                 NULL);
499
500   tag = gtk_text_buffer_create_tag (buffer, "bg_green", NULL);
501
502   setup_tag (tag);
503       
504   color.blue = color.red = 0;
505   color.green = 0xffff;
506   g_object_set (tag,
507                 "background_gdk", &color,
508                 "size_points", 10.0,
509                 NULL);
510
511   tag = gtk_text_buffer_create_tag (buffer, "strikethrough", NULL);
512
513   setup_tag (tag);
514       
515   g_object_set (tag,
516                 "strikethrough", TRUE,
517                 NULL);
518
519
520   tag = gtk_text_buffer_create_tag (buffer, "underline", NULL);
521
522   setup_tag (tag);
523       
524   g_object_set (tag,
525                 "underline", PANGO_UNDERLINE_SINGLE,
526                 NULL);
527
528   tag = gtk_text_buffer_create_tag (buffer, "underline_error", NULL);
529
530   setup_tag (tag);
531       
532   g_object_set (tag,
533                 "underline", PANGO_UNDERLINE_ERROR,
534                 NULL);
535
536   tag = gtk_text_buffer_create_tag (buffer, "centered", NULL);
537       
538   g_object_set (tag,
539                 "justification", GTK_JUSTIFY_CENTER,
540                 NULL);
541
542   tag = gtk_text_buffer_create_tag (buffer, "rtl_quote", NULL);
543       
544   g_object_set (tag,
545                 "wrap_mode", GTK_WRAP_WORD,
546                 "direction", GTK_TEXT_DIR_RTL,
547                 "indent", 30,
548                 "left_margin", 20,
549                 "right_margin", 20,
550                 NULL);
551
552
553   tag = gtk_text_buffer_create_tag (buffer, "negative_indent", NULL);
554       
555   g_object_set (tag,
556                 "indent", -25,
557                 NULL);
558   
559   gtk_text_buffer_get_iter_at_offset (buffer, &iter, 0);
560
561   anchor = gtk_text_buffer_create_child_anchor (buffer, &iter);
562
563   g_object_ref (anchor);
564   
565   g_object_set_data_full (G_OBJECT (buffer), "anchor", anchor,
566                           (GDestroyNotify) g_object_unref);
567   
568   pixbuf = gdk_pixbuf_new_from_xpm_data (book_closed_xpm);
569   
570   i = 0;
571   while (i < 100)
572     {
573       GtkTextMark * temp_mark;
574       
575       gtk_text_buffer_get_iter_at_offset (buffer, &iter, 0);
576           
577       gtk_text_buffer_insert_pixbuf (buffer, &iter, pixbuf);
578           
579       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",
580                             i);
581       
582       gtk_text_buffer_insert (buffer, &iter, str, -1);
583
584       g_free (str);
585       
586       gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, 0, 5);
587           
588       gtk_text_buffer_insert (buffer, &iter,
589                              "(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"
590                              /* This is UTF8 stuff, Emacs doesn't
591                                 really know how to display it */
592                              "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);
593
594       temp_mark =
595         gtk_text_buffer_create_mark (buffer, "tmp_mark", &iter, TRUE);
596
597 #if 1
598       gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, 0, 6);
599       gtk_text_buffer_get_iter_at_line_offset (buffer, &iter2, 0, 13);
600
601       gtk_text_buffer_apply_tag_by_name (buffer, "fg_blue", &iter, &iter2);
602
603       gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, 1, 10);
604       gtk_text_buffer_get_iter_at_line_offset (buffer, &iter2, 1, 16);
605
606       gtk_text_buffer_apply_tag_by_name (buffer, "underline", &iter, &iter2);
607
608       gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, 1, 4);
609       gtk_text_buffer_get_iter_at_line_offset (buffer, &iter2, 1, 7);
610
611       gtk_text_buffer_apply_tag_by_name (buffer, "underline_error", &iter, &iter2);
612
613       gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, 1, 14);
614       gtk_text_buffer_get_iter_at_line_offset (buffer, &iter2, 1, 24);
615
616       gtk_text_buffer_apply_tag_by_name (buffer, "strikethrough", &iter, &iter2);
617           
618       gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, 0, 9);
619       gtk_text_buffer_get_iter_at_line_offset (buffer, &iter2, 0, 16);
620
621       gtk_text_buffer_apply_tag_by_name (buffer, "bg_green", &iter, &iter2);
622   
623       gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, 4, 2);
624       gtk_text_buffer_get_iter_at_line_offset (buffer, &iter2, 4, 10);
625
626       gtk_text_buffer_apply_tag_by_name (buffer, "bg_green", &iter, &iter2);
627
628       gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, 4, 8);
629       gtk_text_buffer_get_iter_at_line_offset (buffer, &iter2, 4, 15);
630
631       gtk_text_buffer_apply_tag_by_name (buffer, "fg_red", &iter, &iter2);
632 #endif
633
634       gtk_text_buffer_get_iter_at_mark (buffer, &iter, temp_mark);
635       gtk_text_buffer_insert (buffer, &iter, "Centered text!\n", -1);
636           
637       gtk_text_buffer_get_iter_at_mark (buffer, &iter2, temp_mark);
638       gtk_text_buffer_apply_tag_by_name (buffer, "centered", &iter2, &iter);
639
640       gtk_text_buffer_move_mark (buffer, temp_mark, &iter);
641       gtk_text_buffer_insert (buffer, &iter, "Word wrapped, Right-to-left Quote\n", -1);
642       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);
643       gtk_text_buffer_get_iter_at_mark (buffer, &iter2, temp_mark);
644       gtk_text_buffer_apply_tag_by_name (buffer, "rtl_quote", &iter2, &iter);
645
646       gtk_text_buffer_insert_with_tags (buffer, &iter,
647                                         "Paragraph with negative indentation. blah blah blah blah blah. The quick brown fox jumped over the lazy dog.\n",
648                                         -1,
649                                         gtk_text_tag_table_lookup (gtk_text_buffer_get_tag_table (buffer),
650                                                                    "negative_indent"),
651                                         NULL);
652       
653       ++i;
654     }
655
656   g_object_unref (pixbuf);
657   
658   printf ("%d lines %d chars\n",
659           gtk_text_buffer_get_line_count (buffer),
660           gtk_text_buffer_get_char_count (buffer));
661
662   /* Move cursor to start */
663   gtk_text_buffer_get_iter_at_offset (buffer, &iter, 0);
664   gtk_text_buffer_place_cursor (buffer, &iter);
665   
666   gtk_text_buffer_set_modified (buffer, FALSE);
667 }
668
669 gboolean
670 fill_file_buffer (GtkTextBuffer *buffer, const char *filename)
671 {
672   FILE* f;
673   gchar buf[2048];
674   gint remaining = 0;
675   GtkTextIter iter, end;
676
677   f = fopen (filename, "r");
678   
679   if (f == NULL)
680     {
681       gchar *err = g_strdup_printf ("Cannot open file '%s': %s",
682                                     filename, g_strerror (errno));
683       msgbox_run (NULL, err, "OK", NULL, NULL, 0);
684       g_free (err);
685       return FALSE;
686     }
687   
688   gtk_text_buffer_get_iter_at_offset (buffer, &iter, 0);
689   while (!feof (f))
690     {
691       gint count;
692       const char *leftover;
693       int to_read = 2047  - remaining;
694
695       count = fread (buf + remaining, 1, to_read, f);
696       buf[count + remaining] = '\0';
697
698       g_utf8_validate (buf, count + remaining, &leftover);
699       
700       g_assert (g_utf8_validate (buf, leftover - buf, NULL));
701       gtk_text_buffer_insert (buffer, &iter, buf, leftover - buf);
702
703       remaining = (buf + remaining + count) - leftover;
704       g_memmove (buf, leftover, remaining);
705
706       if (remaining > 6 || count < to_read)
707           break;
708     }
709
710   if (remaining)
711     {
712       gchar *err = g_strdup_printf ("Invalid UTF-8 data encountered reading file '%s'", filename);
713       msgbox_run (NULL, err, "OK", NULL, NULL, 0);
714       g_free (err);
715     }
716   
717   /* We had a newline in the buffer to begin with. (The buffer always contains
718    * a newline, so we delete to the end of the buffer to clean up.
719    */
720   gtk_text_buffer_get_end_iter (buffer, &end);
721   gtk_text_buffer_delete (buffer, &iter, &end);
722   
723   gtk_text_buffer_set_modified (buffer, FALSE);
724
725   return TRUE;
726 }
727
728 static gint
729 delete_event_cb (GtkWidget *window, GdkEventAny *event, gpointer data)
730 {
731   View *view = view_from_widget (window);
732
733   push_active_window (GTK_WINDOW (window));
734   check_close_view (view);
735   pop_active_window ();
736
737   return TRUE;
738 }
739
740 /*
741  * Menu callbacks
742  */
743
744 static View *
745 get_empty_view (View *view)
746 {
747   if (!view->buffer->filename &&
748       !gtk_text_buffer_get_modified (view->buffer->buffer))
749     return view;
750   else
751     return create_view (create_buffer ());
752 }
753
754 static View *
755 view_from_widget (GtkWidget *widget)
756 {
757   if (GTK_IS_MENU_ITEM (widget))
758     {
759       GtkItemFactory *item_factory = gtk_item_factory_from_widget (widget);
760       return g_object_get_data (G_OBJECT (item_factory), "view");      
761     }
762   else
763     {
764       GtkWidget *app = gtk_widget_get_toplevel (widget);
765       return g_object_get_data (G_OBJECT (app), "view");
766     }
767 }
768
769 static void
770 do_new (gpointer             callback_data,
771         guint                callback_action,
772         GtkWidget           *widget)
773 {
774   create_view (create_buffer ());
775 }
776
777 static void
778 do_new_view (gpointer             callback_data,
779              guint                callback_action,
780              GtkWidget           *widget)
781 {
782   View *view = view_from_widget (widget);
783   
784   create_view (view->buffer);
785 }
786
787 gboolean
788 open_ok_func (const char *filename, gpointer data)
789 {
790   View *view = data;
791   View *new_view = get_empty_view (view);
792
793   if (!fill_file_buffer (new_view->buffer->buffer, filename))
794     {
795       if (new_view != view)
796         close_view (new_view);
797       return FALSE;
798     }
799   else
800     {
801       g_free (new_view->buffer->filename);
802       new_view->buffer->filename = g_strdup (filename);
803       buffer_filename_set (new_view->buffer);
804       
805       return TRUE;
806     }
807 }
808
809 static void
810 do_open (gpointer             callback_data,
811          guint                callback_action,
812          GtkWidget           *widget)
813 {
814   View *view = view_from_widget (widget);
815
816   push_active_window (GTK_WINDOW (view->window));
817   filesel_run (NULL, "Open File", NULL, open_ok_func, view);
818   pop_active_window ();
819 }
820
821 static void
822 do_save_as (gpointer             callback_data,
823             guint                callback_action,
824             GtkWidget           *widget)
825 {
826   View *view = view_from_widget (widget);  
827
828   push_active_window (GTK_WINDOW (view->window));
829   save_as_buffer (view->buffer);
830   pop_active_window ();
831 }
832
833 static void
834 do_save (gpointer             callback_data,
835          guint                callback_action,
836          GtkWidget           *widget)
837 {
838   View *view = view_from_widget (widget);
839
840   push_active_window (GTK_WINDOW (view->window));
841   if (!view->buffer->filename)
842     do_save_as (callback_data, callback_action, widget);
843   else
844     save_buffer (view->buffer);
845   pop_active_window ();
846 }
847
848 static void
849 do_close   (gpointer             callback_data,
850             guint                callback_action,
851             GtkWidget           *widget)
852 {
853   View *view = view_from_widget (widget);
854
855   push_active_window (GTK_WINDOW (view->window));
856   check_close_view (view);
857   pop_active_window ();
858 }
859
860 static void
861 do_exit    (gpointer             callback_data,
862             guint                callback_action,
863             GtkWidget           *widget)
864 {
865   View *view = view_from_widget (widget);
866
867   GSList *tmp_list = buffers;
868
869   push_active_window (GTK_WINDOW (view->window));
870   while (tmp_list)
871     {
872       if (!check_buffer_saved (tmp_list->data))
873         return;
874
875       tmp_list = tmp_list->next;
876     }
877
878   gtk_main_quit ();
879   pop_active_window ();
880 }
881
882 static void
883 do_example (gpointer             callback_data,
884             guint                callback_action,
885             GtkWidget           *widget)
886 {
887   View *view = view_from_widget (widget);
888   View *new_view;
889
890   new_view = get_empty_view (view);
891   
892   fill_example_buffer (new_view->buffer->buffer);
893
894   view_add_example_widgets (new_view);
895 }
896
897
898 static void
899 do_insert_and_scroll (gpointer             callback_data,
900                       guint                callback_action,
901                       GtkWidget           *widget)
902 {
903   View *view = view_from_widget (widget);
904   GtkTextBuffer *buffer;
905   GtkTextIter start, end;
906   GtkTextMark *mark;
907   
908   buffer = view->buffer->buffer;
909
910   gtk_text_buffer_get_bounds (buffer, &start, &end);
911   mark = gtk_text_buffer_create_mark (buffer, NULL, &end, /* right grav */ FALSE);
912
913   gtk_text_buffer_insert (buffer, &end,
914                           "Hello this is multiple lines of text\n"
915                           "Line 1\n"  "Line 2\n"
916                           "Line 3\n"  "Line 4\n"
917                           "Line 5\n",
918                           -1);
919
920   gtk_text_view_scroll_to_mark (GTK_TEXT_VIEW (view->text_view), mark,
921                                 0, TRUE, 0.0, 1.0);
922   gtk_text_buffer_delete_mark (buffer, mark);
923 }
924
925 static void
926 do_wrap_changed (gpointer             callback_data,
927                  guint                callback_action,
928                  GtkWidget           *widget)
929 {
930   View *view = view_from_widget (widget);
931
932   gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (view->text_view), callback_action);
933 }
934
935 static void
936 do_direction_changed (gpointer             callback_data,
937                       guint                callback_action,
938                       GtkWidget           *widget)
939 {
940   View *view = view_from_widget (widget);
941   
942   gtk_widget_set_direction (view->text_view, callback_action);
943   gtk_widget_queue_resize (view->text_view);
944 }
945
946
947 static void
948 do_spacing_changed (gpointer             callback_data,
949                     guint                callback_action,
950                     GtkWidget           *widget)
951 {
952   View *view = view_from_widget (widget);
953
954   if (callback_action)
955     {
956       gtk_text_view_set_pixels_above_lines (GTK_TEXT_VIEW (view->text_view),
957                                             23);
958       gtk_text_view_set_pixels_below_lines (GTK_TEXT_VIEW (view->text_view),
959                                             21);
960       gtk_text_view_set_pixels_inside_wrap (GTK_TEXT_VIEW (view->text_view),
961                                             9);
962     }
963   else
964     {
965       gtk_text_view_set_pixels_above_lines (GTK_TEXT_VIEW (view->text_view),
966                                             0);
967       gtk_text_view_set_pixels_below_lines (GTK_TEXT_VIEW (view->text_view),
968                                             0);
969       gtk_text_view_set_pixels_inside_wrap (GTK_TEXT_VIEW (view->text_view),
970                                             0);
971     }
972 }
973
974 static void
975 do_editable_changed (gpointer callback_data,
976                      guint callback_action,
977                      GtkWidget *widget)
978 {
979   View *view = view_from_widget (widget);
980
981   gtk_text_view_set_editable (GTK_TEXT_VIEW (view->text_view), callback_action);
982 }
983
984 static void
985 change_cursor_color (GtkWidget *widget,
986                      gboolean   set)
987 {
988   if (set)
989     {
990       GdkColor red = {0, 65535, 0, 0};
991       gtk_widget_modify_cursor (widget, &red, &red);
992     }
993   else
994     gtk_widget_modify_cursor (widget, NULL, NULL);
995 }
996
997 static void
998 do_cursor_visible_changed (gpointer callback_data,
999                            guint callback_action,
1000                            GtkWidget *widget)
1001 {
1002   View *view = view_from_widget (widget);
1003
1004   switch (callback_action)
1005     {
1006     case 0:
1007       gtk_text_view_set_cursor_visible (GTK_TEXT_VIEW (view->text_view), FALSE);
1008       break;
1009     case 1:
1010       gtk_text_view_set_cursor_visible (GTK_TEXT_VIEW (view->text_view), TRUE);
1011       change_cursor_color (view->text_view, FALSE);
1012       break;
1013     case 2:
1014       gtk_text_view_set_cursor_visible (GTK_TEXT_VIEW (view->text_view), TRUE);
1015       change_cursor_color (view->text_view, TRUE);
1016       break;
1017     }
1018 }
1019
1020 static void
1021 do_color_cycle_changed (gpointer callback_data,
1022                         guint callback_action,
1023                         GtkWidget *widget)
1024 {
1025   View *view = view_from_widget (widget);
1026
1027   buffer_set_colors (view->buffer, callback_action);
1028 }
1029
1030 static void
1031 do_apply_editable (gpointer callback_data,
1032                    guint callback_action,
1033                    GtkWidget *widget)
1034 {
1035   View *view = view_from_widget (widget);
1036   GtkTextIter start;
1037   GtkTextIter end;
1038   
1039   if (gtk_text_buffer_get_selection_bounds (view->buffer->buffer,
1040                                             &start, &end))
1041     {
1042       if (callback_action)
1043         {
1044           gtk_text_buffer_remove_tag (view->buffer->buffer,
1045                                       view->buffer->not_editable_tag,
1046                                       &start, &end);
1047         }
1048       else
1049         {
1050           gtk_text_buffer_apply_tag (view->buffer->buffer,
1051                                      view->buffer->not_editable_tag,
1052                                      &start, &end);
1053         }
1054     }
1055 }
1056
1057 static void
1058 do_apply_invisible (gpointer callback_data,
1059                     guint callback_action,
1060                     GtkWidget *widget)
1061 {
1062   View *view = view_from_widget (widget);
1063   GtkTextIter start;
1064   GtkTextIter end;
1065   
1066   if (gtk_text_buffer_get_selection_bounds (view->buffer->buffer,
1067                                             &start, &end))
1068     {
1069       if (callback_action)
1070         {
1071           gtk_text_buffer_remove_tag (view->buffer->buffer,
1072                                       view->buffer->invisible_tag,
1073                                       &start, &end);
1074         }
1075       else
1076         {
1077           gtk_text_buffer_apply_tag (view->buffer->buffer,
1078                                      view->buffer->invisible_tag,
1079                                      &start, &end);
1080         }
1081     }
1082 }
1083
1084 static void
1085 do_apply_rise (gpointer callback_data,
1086                guint callback_action,
1087                GtkWidget *widget)
1088 {
1089   View *view = view_from_widget (widget);
1090   GtkTextIter start;
1091   GtkTextIter end;
1092   
1093   if (gtk_text_buffer_get_selection_bounds (view->buffer->buffer,
1094                                             &start, &end))
1095     {
1096       if (callback_action)
1097         {
1098           gtk_text_buffer_remove_tag (view->buffer->buffer,
1099                                       view->buffer->rise_tag,
1100                                       &start, &end);
1101         }
1102       else
1103         {
1104           gtk_text_buffer_apply_tag (view->buffer->buffer,
1105                                      view->buffer->rise_tag,
1106                                      &start, &end);
1107         }
1108     }
1109 }
1110
1111 static void
1112 do_apply_large (gpointer callback_data,
1113                 guint callback_action,
1114                 GtkWidget *widget)
1115 {
1116   View *view = view_from_widget (widget);
1117   GtkTextIter start;
1118   GtkTextIter end;
1119   
1120   if (gtk_text_buffer_get_selection_bounds (view->buffer->buffer,
1121                                             &start, &end))
1122     {
1123       if (callback_action)
1124         {
1125           gtk_text_buffer_remove_tag (view->buffer->buffer,
1126                                       view->buffer->large_tag,
1127                                       &start, &end);
1128         }
1129       else
1130         {
1131           gtk_text_buffer_apply_tag (view->buffer->buffer,
1132                                      view->buffer->large_tag,
1133                                      &start, &end);
1134         }
1135     }
1136 }
1137
1138 static void
1139 do_apply_indent (gpointer callback_data,
1140                  guint callback_action,
1141                  GtkWidget *widget)
1142 {
1143   View *view = view_from_widget (widget);
1144   GtkTextIter start;
1145   GtkTextIter end;
1146   
1147   if (gtk_text_buffer_get_selection_bounds (view->buffer->buffer,
1148                                             &start, &end))
1149     {
1150       if (callback_action)
1151         {
1152           gtk_text_buffer_remove_tag (view->buffer->buffer,
1153                                       view->buffer->indent_tag,
1154                                       &start, &end);
1155         }
1156       else
1157         {
1158           gtk_text_buffer_apply_tag (view->buffer->buffer,
1159                                      view->buffer->indent_tag,
1160                                      &start, &end);
1161         }
1162     }
1163 }
1164
1165 static void
1166 do_apply_margin (gpointer callback_data,
1167                  guint callback_action,
1168                  GtkWidget *widget)
1169 {
1170   View *view = view_from_widget (widget);
1171   GtkTextIter start;
1172   GtkTextIter end;
1173   
1174   if (gtk_text_buffer_get_selection_bounds (view->buffer->buffer,
1175                                             &start, &end))
1176     {
1177       if (callback_action)
1178         {
1179           gtk_text_buffer_remove_tag (view->buffer->buffer,
1180                                       view->buffer->margin_tag,
1181                                       &start, &end);
1182         }
1183       else
1184         {
1185           gtk_text_buffer_apply_tag (view->buffer->buffer,
1186                                      view->buffer->margin_tag,
1187                                      &start, &end);
1188         }
1189     }
1190 }
1191
1192 static void
1193 do_apply_tabs (gpointer callback_data,
1194                guint callback_action,
1195                GtkWidget *widget)
1196 {
1197   View *view = view_from_widget (widget);
1198   GtkTextIter start;
1199   GtkTextIter end;
1200   
1201   if (gtk_text_buffer_get_selection_bounds (view->buffer->buffer,
1202                                             &start, &end))
1203     {
1204       if (callback_action)
1205         {
1206           gtk_text_buffer_remove_tag (view->buffer->buffer,
1207                                       view->buffer->custom_tabs_tag,
1208                                       &start, &end);
1209         }
1210       else
1211         {
1212           gtk_text_buffer_apply_tag (view->buffer->buffer,
1213                                      view->buffer->custom_tabs_tag,
1214                                      &start, &end);
1215         }
1216     }
1217 }
1218
1219 static void
1220 do_apply_colors (gpointer callback_data,
1221                  guint callback_action,
1222                  GtkWidget *widget)
1223 {
1224   View *view = view_from_widget (widget);
1225   Buffer *buffer = view->buffer;
1226   GtkTextIter start;
1227   GtkTextIter end;
1228   
1229   if (gtk_text_buffer_get_selection_bounds (view->buffer->buffer,
1230                                             &start, &end))
1231     {
1232       if (!callback_action)
1233         {
1234           GSList *tmp;
1235           
1236           tmp = buffer->color_tags;
1237           while (tmp != NULL)
1238             {
1239               gtk_text_buffer_remove_tag (view->buffer->buffer,
1240                                           tmp->data,
1241                                           &start, &end);              
1242               tmp = g_slist_next (tmp);
1243             }
1244         }
1245       else
1246         {
1247           GSList *tmp;
1248           
1249           tmp = buffer->color_tags;
1250           while (TRUE)
1251             {
1252               GtkTextIter next;
1253               gboolean done = FALSE;
1254               
1255               next = start;
1256               gtk_text_iter_forward_char (&next);
1257               gtk_text_iter_forward_char (&next);
1258
1259               if (gtk_text_iter_compare (&next, &end) >= 0)
1260                 {
1261                   next = end;
1262                   done = TRUE;
1263                 }
1264               
1265               gtk_text_buffer_apply_tag (view->buffer->buffer,
1266                                          tmp->data,
1267                                          &start, &next);
1268
1269               start = next;
1270
1271               if (done)
1272                 return;
1273               
1274               tmp = g_slist_next (tmp);
1275               if (tmp == NULL)
1276                 tmp = buffer->color_tags;
1277             } 
1278         }
1279     }
1280 }
1281
1282 static void
1283 do_remove_tags (gpointer callback_data,
1284                 guint callback_action,
1285                 GtkWidget *widget)
1286 {
1287   View *view = view_from_widget (widget);
1288   GtkTextIter start;
1289   GtkTextIter end;
1290   
1291   if (gtk_text_buffer_get_selection_bounds (view->buffer->buffer,
1292                                             &start, &end))
1293     {
1294       gtk_text_buffer_remove_all_tags (view->buffer->buffer,
1295                                        &start, &end);
1296     }
1297 }
1298
1299 static void
1300 do_properties (gpointer callback_data,
1301                 guint callback_action,
1302                 GtkWidget *widget)
1303 {
1304   View *view = view_from_widget (widget);
1305
1306   create_prop_editor (G_OBJECT (view->text_view), 0);
1307 }
1308
1309 static void
1310 rich_text_store_populate (GtkListStore  *store,
1311                           GtkTextBuffer *buffer,
1312                           gboolean       deserialize)
1313 {
1314   GdkAtom *formats;
1315   gint     n_formats;
1316   gint     i;
1317
1318   gtk_list_store_clear (store);
1319
1320   if (deserialize)
1321     formats = gtk_text_buffer_get_deserialize_formats (buffer, &n_formats);
1322   else
1323     formats = gtk_text_buffer_get_serialize_formats (buffer, &n_formats);
1324
1325   for (i = 0; i < n_formats; i++)
1326     {
1327       GtkTreeIter  iter;
1328       gchar       *mime_type;
1329       gboolean     can_create_tags = FALSE;
1330
1331       mime_type = gdk_atom_name (formats[i]);
1332
1333       if (deserialize)
1334         can_create_tags =
1335           gtk_text_buffer_deserialize_get_can_create_tags (buffer, formats[i]);
1336
1337       gtk_list_store_append (store, &iter);
1338       gtk_list_store_set (store, &iter,
1339                           0, formats[i],
1340                           1, mime_type,
1341                           2, can_create_tags,
1342                           -1);
1343
1344       g_free (mime_type);
1345     }
1346
1347   g_free (formats);
1348 }
1349
1350 static void
1351 rich_text_paste_target_list_notify (GtkTextBuffer    *buffer,
1352                                     const GParamSpec *pspec,
1353                                     GtkListStore     *store)
1354 {
1355   rich_text_store_populate (store, buffer, TRUE);
1356 }
1357
1358 static void
1359 rich_text_copy_target_list_notify (GtkTextBuffer    *buffer,
1360                                    const GParamSpec *pspec,
1361                                    GtkListStore     *store)
1362 {
1363   rich_text_store_populate (store, buffer, FALSE);
1364 }
1365
1366 static void
1367 rich_text_can_create_tags_toggled (GtkCellRendererToggle *toggle,
1368                                    const gchar           *path,
1369                                    GtkTreeModel          *model)
1370 {
1371   GtkTreeIter iter;
1372
1373   if (gtk_tree_model_get_iter_from_string (model, &iter, path))
1374     {
1375       GtkTextBuffer *buffer;
1376       GdkAtom        format;
1377       gboolean       can_create_tags;
1378
1379       buffer = g_object_get_data (G_OBJECT (model), "buffer");
1380
1381       gtk_tree_model_get (model, &iter,
1382                           0, &format,
1383                           2, &can_create_tags,
1384                           -1);
1385
1386       gtk_text_buffer_deserialize_set_can_create_tags (buffer, format,
1387                                                        !can_create_tags);
1388
1389       gtk_list_store_set (GTK_LIST_STORE (model), &iter,
1390                           2, !can_create_tags,
1391                           -1);
1392     }
1393 }
1394
1395 static void
1396 rich_text_unregister_clicked (GtkWidget   *button,
1397                               GtkTreeView *tv)
1398 {
1399   GtkTreeSelection *sel = gtk_tree_view_get_selection (tv);
1400   GtkTreeModel     *model;
1401   GtkTreeIter       iter;
1402
1403   if (gtk_tree_selection_get_selected (sel, &model, &iter))
1404     {
1405       GtkTextBuffer *buffer;
1406       gboolean       deserialize;
1407       GdkAtom        format;
1408
1409       buffer = g_object_get_data (G_OBJECT (model), "buffer");
1410       deserialize = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (model),
1411                                                         "deserialize"));
1412
1413       gtk_tree_model_get (model, &iter,
1414                           0, &format,
1415                           -1);
1416
1417       if (deserialize)
1418         gtk_text_buffer_unregister_deserialize_format (buffer, format);
1419       else
1420         gtk_text_buffer_unregister_serialize_format (buffer, format);
1421     }
1422 }
1423
1424 static void
1425 rich_text_register_clicked (GtkWidget   *button,
1426                             GtkTreeView *tv)
1427 {
1428   GtkWidget *dialog;
1429   GtkWidget *label;
1430   GtkWidget *entry;
1431
1432   dialog = gtk_dialog_new_with_buttons ("Register new Tagset",
1433                                         GTK_WINDOW (gtk_widget_get_toplevel (button)),
1434                                         GTK_DIALOG_DESTROY_WITH_PARENT,
1435                                         GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1436                                         GTK_STOCK_OK,     GTK_RESPONSE_OK,
1437                                         NULL);
1438   label = gtk_label_new ("Enter tagset name or leave blank for "
1439                          "unrestricted internal format:");
1440   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), label,
1441                       FALSE, FALSE, 0);
1442
1443   entry = gtk_entry_new ();
1444   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), entry,
1445                       FALSE, FALSE, 0);
1446
1447   gtk_widget_show_all (dialog);
1448
1449   if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK)
1450     {
1451       GtkTreeModel  *model  = gtk_tree_view_get_model (tv);
1452       GtkTextBuffer *buffer = g_object_get_data (G_OBJECT (model), "buffer");
1453       const gchar   *tagset = gtk_entry_get_text (GTK_ENTRY (entry));
1454       gboolean       deserialize;
1455
1456       deserialize = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (model),
1457                                                         "deserialize"));
1458
1459       if (tagset && ! strlen (tagset))
1460         tagset = NULL;
1461
1462       if (deserialize)
1463         gtk_text_buffer_register_deserialize_tagset (buffer, tagset);
1464       else
1465         gtk_text_buffer_register_serialize_tagset (buffer, tagset);
1466     }
1467
1468   gtk_widget_destroy (dialog);
1469 }
1470
1471 static void
1472 do_rich_text (gpointer callback_data,
1473               guint deserialize,
1474               GtkWidget *widget)
1475 {
1476   View *view = view_from_widget (widget);
1477   GtkTextBuffer *buffer;
1478   GtkWidget *dialog;
1479   GtkWidget *tv;
1480   GtkWidget *sw;
1481   GtkWidget *hbox;
1482   GtkWidget *button;
1483   GtkListStore *store;
1484
1485   buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view->text_view));
1486
1487   dialog = gtk_dialog_new_with_buttons (deserialize ?
1488                                         "Rich Text Paste & Drop" :
1489                                         "Rich Text Copy & Drag",
1490                                         GTK_WINDOW (view->window),
1491                                         GTK_DIALOG_DESTROY_WITH_PARENT,
1492                                         GTK_STOCK_CLOSE, 0,
1493                                         NULL);
1494   g_signal_connect (dialog, "response",
1495                     G_CALLBACK (gtk_widget_destroy),
1496                     NULL);
1497
1498   store = gtk_list_store_new (3,
1499                               G_TYPE_POINTER,
1500                               G_TYPE_STRING,
1501                               G_TYPE_BOOLEAN);
1502
1503   g_object_set_data (G_OBJECT (store), "buffer", buffer);
1504   g_object_set_data (G_OBJECT (store), "deserialize",
1505                      GUINT_TO_POINTER (deserialize));
1506
1507   tv = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store));
1508   g_object_unref (store);
1509
1510   gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (tv),
1511                                                0, "Rich Text Format",
1512                                                gtk_cell_renderer_text_new (),
1513                                                "text", 1,
1514                                                NULL);
1515
1516   if (deserialize)
1517     {
1518       GtkCellRenderer *renderer = gtk_cell_renderer_toggle_new ();
1519
1520       gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (tv),
1521                                                    1, "Can Create Tags",
1522                                                    renderer,
1523                                                    "active", 2,
1524                                                    NULL);
1525
1526       g_signal_connect (renderer, "toggled",
1527                         G_CALLBACK (rich_text_can_create_tags_toggled),
1528                         store);
1529     }
1530
1531   sw = gtk_scrolled_window_new (NULL, NULL);
1532   gtk_widget_set_size_request (sw, 300, 100);
1533   gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), sw);
1534
1535   gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (sw), tv);
1536
1537   hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, FALSE, 6);
1538   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), hbox,
1539                       FALSE, FALSE, 0);
1540
1541   button = gtk_button_new_with_label ("Unregister Selected Format");
1542   gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
1543
1544   g_signal_connect (button, "clicked",
1545                     G_CALLBACK (rich_text_unregister_clicked),
1546                     tv);
1547
1548   button = gtk_button_new_with_label ("Register New Tagset\n"
1549                                       "for the Internal Format");
1550   gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
1551
1552   g_signal_connect (button, "clicked",
1553                     G_CALLBACK (rich_text_register_clicked),
1554                     tv);
1555
1556   if (deserialize)
1557     g_signal_connect_object (buffer, "notify::paste-target-list",
1558                              G_CALLBACK (rich_text_paste_target_list_notify),
1559                              G_OBJECT (store), 0);
1560   else
1561     g_signal_connect_object (buffer, "notify::copy-target-list",
1562                              G_CALLBACK (rich_text_copy_target_list_notify),
1563                              G_OBJECT (store), 0);
1564
1565   rich_text_store_populate (store, buffer, deserialize);
1566
1567   gtk_widget_show_all (dialog);
1568 }
1569
1570 enum
1571 {
1572   RESPONSE_FORWARD,
1573   RESPONSE_BACKWARD
1574 };
1575
1576 static void
1577 dialog_response_callback (GtkWidget *dialog, gint response_id, gpointer data)
1578 {
1579   GtkTextBuffer *buffer;
1580   View *view = data;
1581   GtkTextIter start, end;
1582   gchar *search_string;
1583
1584   if (response_id != RESPONSE_FORWARD &&
1585       response_id != RESPONSE_BACKWARD)
1586     {
1587       gtk_widget_destroy (dialog);
1588       return;
1589     }
1590   
1591   buffer = g_object_get_data (G_OBJECT (dialog), "buffer");
1592
1593   gtk_text_buffer_get_bounds (buffer, &start, &end);
1594   
1595   search_string = gtk_text_iter_get_text (&start, &end);
1596
1597   g_print ("Searching for `%s'\n", search_string);
1598
1599   if (response_id == RESPONSE_FORWARD)
1600     buffer_search_forward (view->buffer, search_string, view);
1601   else if (response_id == RESPONSE_BACKWARD)
1602     buffer_search_backward (view->buffer, search_string, view);
1603     
1604   g_free (search_string);
1605   
1606   gtk_widget_destroy (dialog);
1607 }
1608
1609 static void
1610 do_copy  (gpointer callback_data,
1611           guint callback_action,
1612           GtkWidget *widget)
1613 {
1614   View *view = view_from_widget (widget);
1615   GtkTextBuffer *buffer;
1616
1617   buffer = view->buffer->buffer;
1618
1619   gtk_text_buffer_copy_clipboard (buffer,
1620                                   gtk_clipboard_get (GDK_NONE));
1621 }
1622
1623 static void
1624 do_search (gpointer callback_data,
1625            guint callback_action,
1626            GtkWidget *widget)
1627 {
1628   View *view = view_from_widget (widget);
1629   GtkWidget *dialog;
1630   GtkWidget *search_text;
1631   GtkTextBuffer *buffer;
1632
1633   dialog = gtk_dialog_new_with_buttons ("Search",
1634                                         GTK_WINDOW (view->window),
1635                                         GTK_DIALOG_DESTROY_WITH_PARENT,
1636                                         "Forward", RESPONSE_FORWARD,
1637                                         "Backward", RESPONSE_BACKWARD,
1638                                         GTK_STOCK_CANCEL,
1639                                         GTK_RESPONSE_NONE, NULL);
1640
1641
1642   buffer = gtk_text_buffer_new (NULL);
1643
1644   search_text = gtk_text_view_new_with_buffer (buffer);
1645
1646   g_object_unref (buffer);
1647   
1648   gtk_box_pack_end (GTK_BOX (GTK_DIALOG (dialog)->vbox),
1649                     search_text,
1650                     TRUE, TRUE, 0);
1651
1652   g_object_set_data (G_OBJECT (dialog), "buffer", buffer);
1653   
1654   g_signal_connect (dialog,
1655                     "response",
1656                     G_CALLBACK (dialog_response_callback),
1657                     view);
1658
1659   gtk_widget_show (search_text);
1660
1661   gtk_widget_grab_focus (search_text);
1662   
1663   gtk_widget_show_all (dialog);
1664 }
1665
1666 static void
1667 do_select_all (gpointer callback_data,
1668                guint callback_action,
1669                GtkWidget *widget)
1670 {
1671   View *view = view_from_widget (widget);
1672   GtkTextBuffer *buffer;
1673   GtkTextIter start, end;
1674
1675   buffer = view->buffer->buffer;
1676
1677   gtk_text_buffer_get_bounds (buffer, &start, &end);
1678   gtk_text_buffer_select_range (buffer, &start, &end);
1679 }
1680
1681 typedef struct
1682 {
1683   /* position is in coordinate system of text_view_move_child */
1684   int click_x;
1685   int click_y;
1686   int start_x;
1687   int start_y;
1688   int button;
1689 } ChildMoveInfo;
1690
1691 static gboolean
1692 movable_child_callback (GtkWidget *child,
1693                         GdkEvent  *event,
1694                         gpointer   data)
1695 {
1696   ChildMoveInfo *info;
1697   GtkTextView *text_view;
1698
1699   text_view = GTK_TEXT_VIEW (data);
1700   
1701   g_return_val_if_fail (GTK_IS_EVENT_BOX (child), FALSE);
1702   g_return_val_if_fail (gtk_widget_get_parent (child) == GTK_WIDGET (text_view), FALSE);  
1703   
1704   info = g_object_get_data (G_OBJECT (child),
1705                             "testtext-move-info");
1706
1707   if (info == NULL)
1708     {
1709       info = g_new (ChildMoveInfo, 1);      
1710       info->start_x = -1;
1711       info->start_y = -1;
1712       info->button = -1;
1713       g_object_set_data_full (G_OBJECT (child),
1714                               "testtext-move-info",
1715                               info,
1716                               g_free);
1717     }
1718   
1719   switch (event->type)
1720     {
1721     case GDK_BUTTON_PRESS:
1722       if (info->button < 0)
1723         {
1724           if (gdk_pointer_grab (event->button.window,
1725                                 FALSE,
1726                                 GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK |
1727                                 GDK_BUTTON_RELEASE_MASK,
1728                                 NULL,
1729                                 NULL,
1730                                 event->button.time) != GDK_GRAB_SUCCESS)
1731             return FALSE;
1732           
1733           info->button = event->button.button;
1734           
1735           info->start_x = child->allocation.x;
1736           info->start_y = child->allocation.y;
1737           info->click_x = child->allocation.x + event->button.x;
1738           info->click_y = child->allocation.y + event->button.y;
1739         }
1740       break;
1741
1742     case GDK_BUTTON_RELEASE:
1743       if (info->button < 0)
1744         return FALSE;
1745
1746       if (info->button == event->button.button)
1747         {
1748           int x, y;
1749           
1750           gdk_pointer_ungrab (event->button.time);
1751           info->button = -1;
1752
1753           /* convert to window coords from event box coords */
1754           x = info->start_x + (event->button.x + child->allocation.x - info->click_x);
1755           y = info->start_y + (event->button.y + child->allocation.y - info->click_y);
1756
1757           gtk_text_view_move_child (text_view,
1758                                     child,
1759                                     x, y);
1760         }
1761       break;
1762
1763     case GDK_MOTION_NOTIFY:
1764       {
1765         int x, y;
1766         
1767         if (info->button < 0)
1768           return FALSE;
1769         
1770         gdk_window_get_pointer (child->window, &x, &y, NULL); /* ensure more events */
1771
1772         /* to window coords from event box coords */
1773         x += child->allocation.x;
1774         y += child->allocation.y;
1775         
1776         x = info->start_x + (x - info->click_x);
1777         y = info->start_y + (y - info->click_y);
1778         
1779         gtk_text_view_move_child (text_view,
1780                                   child,
1781                                   x, y);
1782       }
1783       break;
1784
1785     default:
1786       break;
1787     }
1788
1789   return FALSE;
1790 }
1791
1792 static void
1793 add_movable_child (GtkTextView      *text_view,
1794                    GtkTextWindowType window)
1795 {
1796   GtkWidget *event_box;
1797   GtkWidget *label;
1798   GdkColor color;
1799   
1800   label = gtk_label_new ("Drag me around");  
1801   
1802   event_box = gtk_event_box_new ();
1803   gtk_widget_add_events (event_box,
1804                          GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
1805                          GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK);
1806
1807   color.red = 0xffff;
1808   color.green = color.blue = 0;
1809   gtk_widget_modify_bg (event_box, GTK_STATE_NORMAL, &color);
1810   
1811   gtk_container_add (GTK_CONTAINER (event_box), label);
1812
1813   gtk_widget_show_all (event_box);
1814
1815   g_signal_connect (event_box, "event",
1816                     G_CALLBACK (movable_child_callback),
1817                     text_view);
1818
1819   gtk_text_view_add_child_in_window (text_view,
1820                                      event_box,
1821                                      window,
1822                                      0, 0);
1823 }
1824
1825 static void
1826 do_add_children (gpointer callback_data,
1827                  guint callback_action,
1828                  GtkWidget *widget)
1829 {
1830   View *view = view_from_widget (widget);
1831
1832   add_movable_child (GTK_TEXT_VIEW (view->text_view),
1833                      GTK_TEXT_WINDOW_WIDGET);
1834   add_movable_child (GTK_TEXT_VIEW (view->text_view),
1835                      GTK_TEXT_WINDOW_LEFT);
1836   add_movable_child (GTK_TEXT_VIEW (view->text_view),
1837                      GTK_TEXT_WINDOW_RIGHT);
1838 }
1839
1840 static void
1841 do_add_focus_children (gpointer callback_data,
1842                        guint callback_action,
1843                        GtkWidget *widget)
1844 {
1845   View *view = view_from_widget (widget);
1846   GtkWidget *child;
1847   GtkTextChildAnchor *anchor;
1848   GtkTextIter iter;
1849   GtkTextView *text_view;
1850
1851   text_view = GTK_TEXT_VIEW (view->text_view);
1852   
1853   child = gtk_button_new_with_mnemonic ("Button _A in widget->window");
1854
1855   gtk_text_view_add_child_in_window (text_view,
1856                                      child,
1857                                      GTK_TEXT_WINDOW_WIDGET,
1858                                      200, 200);
1859
1860   child = gtk_button_new_with_mnemonic ("Button _B in widget->window");
1861
1862   gtk_text_view_add_child_in_window (text_view,
1863                                      child,
1864                                      GTK_TEXT_WINDOW_WIDGET,
1865                                      350, 300);
1866
1867   child = gtk_button_new_with_mnemonic ("Button _C in left window");
1868
1869   gtk_text_view_add_child_in_window (text_view,
1870                                      child,
1871                                      GTK_TEXT_WINDOW_LEFT,
1872                                      0, 0);
1873
1874   child = gtk_button_new_with_mnemonic ("Button _D in right window");
1875   
1876   gtk_text_view_add_child_in_window (text_view,
1877                                      child,
1878                                      GTK_TEXT_WINDOW_RIGHT,
1879                                      0, 0);
1880
1881   gtk_text_buffer_get_start_iter (view->buffer->buffer, &iter);
1882   
1883   anchor = gtk_text_buffer_create_child_anchor (view->buffer->buffer, &iter);
1884
1885   child = gtk_button_new_with_mnemonic ("Button _E in buffer");
1886   
1887   gtk_text_view_add_child_at_anchor (text_view, child, anchor);
1888
1889   anchor = gtk_text_buffer_create_child_anchor (view->buffer->buffer, &iter);
1890
1891   child = gtk_button_new_with_mnemonic ("Button _F in buffer");
1892   
1893   gtk_text_view_add_child_at_anchor (text_view, child, anchor);
1894
1895   anchor = gtk_text_buffer_create_child_anchor (view->buffer->buffer, &iter);
1896
1897   child = gtk_button_new_with_mnemonic ("Button _G in buffer");
1898   
1899   gtk_text_view_add_child_at_anchor (text_view, child, anchor);
1900
1901   /* show all the buttons */
1902   gtk_widget_show_all (view->text_view);
1903 }
1904
1905 static void
1906 view_init_menus (View *view)
1907 {
1908   GtkTextDirection direction = gtk_widget_get_direction (view->text_view);
1909   GtkWrapMode wrap_mode = gtk_text_view_get_wrap_mode (GTK_TEXT_VIEW (view->text_view));
1910   GtkWidget *menu_item = NULL;
1911
1912   switch (direction)
1913     {
1914     case GTK_TEXT_DIR_LTR:
1915       menu_item = gtk_item_factory_get_widget (view->item_factory, "/Settings/Left-to-Right");
1916       break;
1917     case GTK_TEXT_DIR_RTL:
1918       menu_item = gtk_item_factory_get_widget (view->item_factory, "/Settings/Right-to-Left");
1919       break;
1920     default:
1921       break;
1922     }
1923
1924   if (menu_item)
1925     gtk_menu_item_activate (GTK_MENU_ITEM (menu_item));
1926
1927   switch (wrap_mode)
1928     {
1929     case GTK_WRAP_NONE:
1930       menu_item = gtk_item_factory_get_widget (view->item_factory, "/Settings/Wrap Off");
1931       break;
1932     case GTK_WRAP_WORD:
1933       menu_item = gtk_item_factory_get_widget (view->item_factory, "/Settings/Wrap Words");
1934       break;
1935     case GTK_WRAP_CHAR:
1936       menu_item = gtk_item_factory_get_widget (view->item_factory, "/Settings/Wrap Chars");
1937       break;
1938     default:
1939       break;
1940     }
1941   
1942   if (menu_item)
1943     gtk_menu_item_activate (GTK_MENU_ITEM (menu_item));
1944 }
1945
1946 static GtkItemFactoryEntry menu_items[] =
1947 {
1948   { "/_File",            NULL,         NULL,        0, "<Branch>" },
1949   { "/File/_New",        "<control>N", do_new,      0, NULL },
1950   { "/File/New _View",   NULL,         do_new_view, 0, NULL },
1951   { "/File/_Open",       "<control>O", do_open,     0, NULL },
1952   { "/File/_Save",       "<control>S", do_save,     0, NULL },
1953   { "/File/Save _As...", NULL,         do_save_as,  0, NULL },
1954   { "/File/sep1",        NULL,         NULL,        0, "<Separator>" },
1955   { "/File/_Close",     "<control>W" , do_close,    0, NULL },
1956   { "/File/E_xit",      "<control>Q" , do_exit,     0, NULL },
1957
1958   { "/_Edit", NULL, 0, 0, "<Branch>" },
1959   { "/Edit/Copy", NULL, do_copy, 0, NULL },
1960   { "/Edit/sep1", NULL, NULL, 0, "<Separator>" },
1961   { "/Edit/Find...", NULL, do_search, 0, NULL },
1962   { "/Edit/Select All", "<control>A", do_select_all, 0, NULL }, 
1963
1964   { "/_Settings",         NULL,         NULL,             0, "<Branch>" },
1965   { "/Settings/Wrap _Off",   NULL,      do_wrap_changed,  GTK_WRAP_NONE, "<RadioItem>" },
1966   { "/Settings/Wrap _Words", NULL,      do_wrap_changed,  GTK_WRAP_WORD, "/Settings/Wrap Off" },
1967   { "/Settings/Wrap _Chars", NULL,      do_wrap_changed,  GTK_WRAP_CHAR, "/Settings/Wrap Off" },
1968   { "/Settings/sep1",        NULL,      NULL,             0, "<Separator>" },
1969   { "/Settings/Editable", NULL,      do_editable_changed,  TRUE, "<RadioItem>" },
1970   { "/Settings/Not editable",    NULL,      do_editable_changed,  FALSE, "/Settings/Editable" },
1971   { "/Settings/sep1",        NULL,      NULL,             0, "<Separator>" },
1972
1973   { "/Settings/Cursor normal",    NULL,      do_cursor_visible_changed,  1, "<RadioItem>" },
1974   { "/Settings/Cursor not visible", NULL,      do_cursor_visible_changed,  0, "/Settings/Cursor normal" },
1975   { "/Settings/Cursor colored", NULL,      do_cursor_visible_changed,  2, "/Settings/Cursor normal" },
1976   { "/Settings/sep1",        NULL,      NULL,          0, "<Separator>" },
1977   
1978   { "/Settings/Left-to-Right", NULL,    do_direction_changed,  GTK_TEXT_DIR_LTR, "<RadioItem>" },
1979   { "/Settings/Right-to-Left", NULL,    do_direction_changed,  GTK_TEXT_DIR_RTL, "/Settings/Left-to-Right" },
1980
1981   { "/Settings/sep1",        NULL,      NULL,                0, "<Separator>" },
1982   { "/Settings/Sane spacing", NULL,    do_spacing_changed,  FALSE, "<RadioItem>" },
1983   { "/Settings/Funky spacing", NULL,    do_spacing_changed,  TRUE, "/Settings/Sane spacing" },
1984   { "/Settings/sep1",        NULL,      NULL,                0, "<Separator>" },
1985   { "/Settings/Don't cycle color tags", NULL,    do_color_cycle_changed,  FALSE, "<RadioItem>" },
1986   { "/Settings/Cycle colors", NULL,    do_color_cycle_changed,  TRUE, "/Settings/Don't cycle color tags" },
1987   { "/_Attributes",       NULL,         NULL,                0, "<Branch>" },
1988   { "/Attributes/Editable",       NULL,         do_apply_editable, TRUE, NULL },
1989   { "/Attributes/Not editable",           NULL,         do_apply_editable, FALSE, NULL },
1990   { "/Attributes/Invisible",      NULL,         do_apply_invisible, FALSE, NULL },
1991   { "/Attributes/Visible",        NULL,         do_apply_invisible, TRUE, NULL },
1992   { "/Attributes/Rise",           NULL,         do_apply_rise, FALSE, NULL },
1993   { "/Attributes/Large",          NULL,         do_apply_large, FALSE, NULL },
1994   { "/Attributes/Indent",         NULL,         do_apply_indent, FALSE, NULL },
1995   { "/Attributes/Margins",        NULL,         do_apply_margin, FALSE, NULL },
1996   { "/Attributes/Custom tabs",            NULL,         do_apply_tabs, FALSE, NULL },
1997   { "/Attributes/Default tabs",           NULL,         do_apply_tabs, TRUE, NULL },
1998   { "/Attributes/Color cycles",           NULL,         do_apply_colors, TRUE, NULL },
1999   { "/Attributes/No colors",              NULL,         do_apply_colors, FALSE, NULL },
2000   { "/Attributes/Remove all tags",        NULL, do_remove_tags, 0, NULL },
2001   { "/Attributes/Properties",             NULL, do_properties, 0, NULL },
2002   { "/Attributes/Rich Text copy & drag",  NULL, do_rich_text, 0, NULL },
2003   { "/Attributes/Rich Text paste & drop", NULL, do_rich_text, 1, NULL },
2004   { "/_Test",            NULL,         NULL,           0, "<Branch>" },
2005   { "/Test/_Example",    NULL,         do_example,  0, NULL },
2006   { "/Test/_Insert and scroll", NULL,         do_insert_and_scroll,  0, NULL },
2007   { "/Test/_Add fixed children", NULL,         do_add_children,  0, NULL },
2008   { "/Test/A_dd focusable children", NULL,    do_add_focus_children,  0, NULL },
2009 };
2010
2011 static gboolean
2012 save_buffer (Buffer *buffer)
2013 {
2014   GtkTextIter start, end;
2015   gchar *chars;
2016   gboolean result = FALSE;
2017   gboolean have_backup = FALSE;
2018   gchar *bak_filename;
2019   FILE *file;
2020
2021   g_return_val_if_fail (buffer->filename != NULL, FALSE);
2022
2023   bak_filename = g_strconcat (buffer->filename, "~", NULL);
2024   
2025   if (rename (buffer->filename, bak_filename) != 0)
2026     {
2027       if (errno != ENOENT)
2028         {
2029           gchar *err = g_strdup_printf ("Cannot back up '%s' to '%s': %s",
2030                                         buffer->filename, bak_filename, g_strerror (errno));
2031           msgbox_run (NULL, err, "OK", NULL, NULL, 0);
2032           g_free (err);
2033           return FALSE;
2034         }
2035     }
2036   else
2037     have_backup = TRUE;
2038   
2039   file = fopen (buffer->filename, "w");
2040   if (!file)
2041     {
2042       gchar *err = g_strdup_printf ("Cannot back up '%s' to '%s': %s",
2043                                     buffer->filename, bak_filename, g_strerror (errno));
2044       msgbox_run (NULL, err, "OK", NULL, NULL, 0);
2045     }
2046   else
2047     {
2048       gtk_text_buffer_get_iter_at_offset (buffer->buffer, &start, 0);
2049       gtk_text_buffer_get_end_iter (buffer->buffer, &end);
2050   
2051       chars = gtk_text_buffer_get_slice (buffer->buffer, &start, &end, FALSE);
2052
2053       if (fputs (chars, file) == EOF ||
2054           fclose (file) == EOF)
2055         {
2056           gchar *err = g_strdup_printf ("Error writing to '%s': %s",
2057                                         buffer->filename, g_strerror (errno));
2058           msgbox_run (NULL, err, "OK", NULL, NULL, 0);
2059           g_free (err);
2060         }
2061       else
2062         {
2063           /* Success
2064            */
2065           result = TRUE;
2066           gtk_text_buffer_set_modified (buffer->buffer, FALSE);   
2067         }
2068         
2069       g_free (chars);
2070     }
2071
2072   if (!result && have_backup)
2073     {
2074       if (rename (bak_filename, buffer->filename) != 0)
2075         {
2076           gchar *err = g_strdup_printf ("Error restoring backup file '%s' to '%s': %s\nBackup left as '%s'",
2077                                         buffer->filename, bak_filename, g_strerror (errno), bak_filename);
2078           msgbox_run (NULL, err, "OK", NULL, NULL, 0);
2079           g_free (err);
2080         }
2081     }
2082
2083   g_free (bak_filename);
2084   
2085   return result;
2086 }
2087
2088 static gboolean
2089 save_as_ok_func (const char *filename, gpointer data)
2090 {
2091   Buffer *buffer = data;
2092   char *old_filename = buffer->filename;
2093
2094   if (!buffer->filename || strcmp (filename, buffer->filename) != 0)
2095     {
2096       struct stat statbuf;
2097
2098       if (stat (filename, &statbuf) == 0)
2099         {
2100           gchar *err = g_strdup_printf ("Ovewrite existing file '%s'?", filename);
2101           gint result = msgbox_run (NULL, err, "Yes", "No", NULL, 1);
2102           g_free (err);
2103
2104           if (result != 0)
2105             return FALSE;
2106         }
2107     }
2108   
2109   buffer->filename = g_strdup (filename);
2110
2111   if (save_buffer (buffer))
2112     {
2113       g_free (old_filename);
2114       buffer_filename_set (buffer);
2115       return TRUE;
2116     }
2117   else
2118     {
2119       g_free (buffer->filename);
2120       buffer->filename = old_filename;
2121       return FALSE;
2122     }
2123 }
2124
2125 static gboolean
2126 save_as_buffer (Buffer *buffer)
2127 {
2128   return filesel_run (NULL, "Save File", NULL, save_as_ok_func, buffer);
2129 }
2130
2131 static gboolean
2132 check_buffer_saved (Buffer *buffer)
2133 {
2134   if (gtk_text_buffer_get_modified (buffer->buffer))
2135     {
2136       char *pretty_name = buffer_pretty_name (buffer);
2137       char *msg = g_strdup_printf ("Save changes to '%s'?", pretty_name);
2138       gint result;
2139       
2140       g_free (pretty_name);
2141       
2142       result = msgbox_run (NULL, msg, "Yes", "No", "Cancel", 0);
2143       g_free (msg);
2144   
2145       if (result == 0)
2146         return save_as_buffer (buffer);
2147       else if (result == 1)
2148         return TRUE;
2149       else
2150         return FALSE;
2151     }
2152   else
2153     return TRUE;
2154 }
2155
2156 #define N_COLORS 16
2157
2158 static Buffer *
2159 create_buffer (void)
2160 {
2161   Buffer *buffer;
2162   PangoTabArray *tabs;
2163   gint i;
2164   
2165   buffer = g_new (Buffer, 1);
2166
2167   buffer->buffer = gtk_text_buffer_new (NULL);
2168   
2169   buffer->refcount = 1;
2170   buffer->filename = NULL;
2171   buffer->untitled_serial = -1;
2172
2173   buffer->color_tags = NULL;
2174   buffer->color_cycle_timeout = 0;
2175   buffer->start_hue = 0.0;
2176   
2177   i = 0;
2178   while (i < N_COLORS)
2179     {
2180       GtkTextTag *tag;
2181
2182       tag = gtk_text_buffer_create_tag (buffer->buffer, NULL, NULL);
2183       
2184       buffer->color_tags = g_slist_prepend (buffer->color_tags, tag);
2185       
2186       ++i;
2187     }
2188
2189 #if 1  
2190   buffer->invisible_tag = gtk_text_buffer_create_tag (buffer->buffer, NULL,
2191                                                       "invisible", TRUE, NULL);
2192 #endif  
2193   
2194   buffer->not_editable_tag =
2195     gtk_text_buffer_create_tag (buffer->buffer, NULL,
2196                                 "editable", FALSE,
2197                                 "foreground", "purple", NULL);
2198
2199   buffer->found_text_tag = gtk_text_buffer_create_tag (buffer->buffer, NULL,
2200                                                        "foreground", "red", NULL);
2201
2202   buffer->rise_tag = gtk_text_buffer_create_tag (buffer->buffer, NULL,
2203                                                  "rise", 10 * PANGO_SCALE, NULL);
2204
2205   buffer->large_tag = gtk_text_buffer_create_tag (buffer->buffer, NULL,
2206                                                  "scale", PANGO_SCALE_X_LARGE, NULL);
2207
2208   buffer->indent_tag = gtk_text_buffer_create_tag (buffer->buffer, NULL,
2209                                                    "indent", 20, NULL);
2210
2211   buffer->margin_tag = gtk_text_buffer_create_tag (buffer->buffer, NULL,
2212                                                    "left_margin", 20, "right_margin", 20, NULL);
2213
2214   tabs = pango_tab_array_new_with_positions (4,
2215                                              TRUE,
2216                                              PANGO_TAB_LEFT, 10,
2217                                              PANGO_TAB_LEFT, 30,
2218                                              PANGO_TAB_LEFT, 60,
2219                                              PANGO_TAB_LEFT, 120);
2220   
2221   buffer->custom_tabs_tag = gtk_text_buffer_create_tag (buffer->buffer, NULL,
2222                                                         "tabs", tabs,
2223                                                         "foreground", "green", NULL);
2224
2225   pango_tab_array_free (tabs);
2226   
2227   buffers = g_slist_prepend (buffers, buffer);
2228   
2229   return buffer;
2230 }
2231
2232 static char *
2233 buffer_pretty_name (Buffer *buffer)
2234 {
2235   if (buffer->filename)
2236     {
2237       char *p;
2238       char *result = g_path_get_basename (buffer->filename);
2239       p = strchr (result, '/');
2240       if (p)
2241         *p = '\0';
2242
2243       return result;
2244     }
2245   else
2246     {
2247       if (buffer->untitled_serial == -1)
2248         buffer->untitled_serial = untitled_serial++;
2249
2250       if (buffer->untitled_serial == 1)
2251         return g_strdup ("Untitled");
2252       else
2253         return g_strdup_printf ("Untitled #%d", buffer->untitled_serial);
2254     }
2255 }
2256
2257 static void
2258 buffer_filename_set (Buffer *buffer)
2259 {
2260   GSList *tmp_list = views;
2261
2262   while (tmp_list)
2263     {
2264       View *view = tmp_list->data;
2265
2266       if (view->buffer == buffer)
2267         view_set_title (view);
2268
2269       tmp_list = tmp_list->next;
2270     }
2271 }
2272
2273 static void
2274 buffer_search (Buffer     *buffer,
2275                const char *str,
2276                View       *view,
2277                gboolean forward)
2278 {
2279   GtkTextIter iter;
2280   GtkTextIter start, end;
2281   GtkWidget *dialog;
2282   int i;
2283   
2284   /* remove tag from whole buffer */
2285   gtk_text_buffer_get_bounds (buffer->buffer, &start, &end);
2286   gtk_text_buffer_remove_tag (buffer->buffer,  buffer->found_text_tag,
2287                               &start, &end );
2288   
2289   gtk_text_buffer_get_iter_at_mark (buffer->buffer, &iter,
2290                                     gtk_text_buffer_get_mark (buffer->buffer,
2291                                                               "insert"));
2292
2293   i = 0;
2294   if (*str != '\0')
2295     {
2296       GtkTextIter match_start, match_end;
2297
2298       if (forward)
2299         {
2300           while (gtk_text_iter_forward_search (&iter, str,
2301                                                GTK_TEXT_SEARCH_VISIBLE_ONLY |
2302                                                GTK_TEXT_SEARCH_TEXT_ONLY,
2303                                                &match_start, &match_end,
2304                                                NULL))
2305             {
2306               ++i;
2307               gtk_text_buffer_apply_tag (buffer->buffer, buffer->found_text_tag,
2308                                          &match_start, &match_end);
2309               
2310               iter = match_end;
2311             }
2312         }
2313       else
2314         {
2315           while (gtk_text_iter_backward_search (&iter, str,
2316                                                 GTK_TEXT_SEARCH_VISIBLE_ONLY |
2317                                                 GTK_TEXT_SEARCH_TEXT_ONLY,
2318                                                 &match_start, &match_end,
2319                                                 NULL))
2320             {
2321               ++i;
2322               gtk_text_buffer_apply_tag (buffer->buffer, buffer->found_text_tag,
2323                                          &match_start, &match_end);
2324               
2325               iter = match_start;
2326             }
2327         }
2328     }
2329
2330   dialog = gtk_message_dialog_new (GTK_WINDOW (view->window),
2331                                    GTK_DIALOG_DESTROY_WITH_PARENT,
2332                                    GTK_MESSAGE_INFO,
2333                                    GTK_BUTTONS_OK,
2334                                    "%d strings found and marked in red",
2335                                    i);
2336
2337   g_signal_connect_swapped (dialog,
2338                             "response",
2339                             G_CALLBACK (gtk_widget_destroy), dialog);
2340   
2341   gtk_widget_show (dialog);
2342 }
2343
2344 static void
2345 buffer_search_forward (Buffer *buffer, const char *str,
2346                        View *view)
2347 {
2348   buffer_search (buffer, str, view, TRUE);
2349 }
2350
2351 static void
2352 buffer_search_backward (Buffer *buffer, const char *str,
2353                         View *view)
2354 {
2355   buffer_search (buffer, str, view, FALSE);
2356 }
2357
2358 static void
2359 buffer_ref (Buffer *buffer)
2360 {
2361   buffer->refcount++;
2362 }
2363
2364 static void
2365 buffer_unref (Buffer *buffer)
2366 {
2367   buffer->refcount--;
2368   if (buffer->refcount == 0)
2369     {
2370       buffer_set_colors (buffer, FALSE);
2371       buffers = g_slist_remove (buffers, buffer);
2372       g_object_unref (buffer->buffer);
2373       g_free (buffer->filename);
2374       g_free (buffer);
2375     }
2376 }
2377
2378 static void
2379 hsv_to_rgb (gdouble *h,
2380             gdouble *s,
2381             gdouble *v)
2382 {
2383   gdouble hue, saturation, value;
2384   gdouble f, p, q, t;
2385
2386   if (*s == 0.0)
2387     {
2388       *h = *v;
2389       *s = *v;
2390       *v = *v; /* heh */
2391     }
2392   else
2393     {
2394       hue = *h * 6.0;
2395       saturation = *s;
2396       value = *v;
2397       
2398       if (hue >= 6.0)
2399         hue = 0.0;
2400       
2401       f = hue - (int) hue;
2402       p = value * (1.0 - saturation);
2403       q = value * (1.0 - saturation * f);
2404       t = value * (1.0 - saturation * (1.0 - f));
2405       
2406       switch ((int) hue)
2407         {
2408         case 0:
2409           *h = value;
2410           *s = t;
2411           *v = p;
2412           break;
2413           
2414         case 1:
2415           *h = q;
2416           *s = value;
2417           *v = p;
2418           break;
2419           
2420         case 2:
2421           *h = p;
2422           *s = value;
2423           *v = t;
2424           break;
2425           
2426         case 3:
2427           *h = p;
2428           *s = q;
2429           *v = value;
2430           break;
2431           
2432         case 4:
2433           *h = t;
2434           *s = p;
2435           *v = value;
2436           break;
2437           
2438         case 5:
2439           *h = value;
2440           *s = p;
2441           *v = q;
2442           break;
2443           
2444         default:
2445           g_assert_not_reached ();
2446         }
2447     }
2448 }
2449
2450 static void
2451 hue_to_color (gdouble   hue,
2452               GdkColor *color)
2453 {
2454   gdouble h, s, v;
2455
2456   h = hue;
2457   s = 1.0;
2458   v = 1.0;
2459
2460   g_return_if_fail (hue <= 1.0);
2461   
2462   hsv_to_rgb (&h, &s, &v);
2463
2464   color->red = h * 65535;
2465   color->green = s * 65535;
2466   color->blue = v * 65535;
2467 }
2468
2469
2470 static gint
2471 color_cycle_timeout (gpointer data)
2472 {
2473   Buffer *buffer = data;
2474
2475   buffer_cycle_colors (buffer);
2476
2477   return TRUE;
2478 }
2479
2480 static void
2481 buffer_set_colors (Buffer  *buffer,
2482                    gboolean enabled)
2483 {
2484   GSList *tmp;
2485   gdouble hue = 0.0;
2486
2487   if (enabled && buffer->color_cycle_timeout == 0)
2488     buffer->color_cycle_timeout = gdk_threads_add_timeout (200, color_cycle_timeout, buffer);
2489   else if (!enabled && buffer->color_cycle_timeout != 0)
2490     {
2491       g_source_remove (buffer->color_cycle_timeout);
2492       buffer->color_cycle_timeout = 0;
2493     }
2494     
2495   tmp = buffer->color_tags;
2496   while (tmp != NULL)
2497     {
2498       if (enabled)
2499         {
2500           GdkColor color;
2501           
2502           hue_to_color (hue, &color);
2503
2504           g_object_set (tmp->data,
2505                         "foreground_gdk", &color,
2506                         NULL);
2507         }
2508       else
2509         g_object_set (tmp->data,
2510                       "foreground_set", FALSE,
2511                       NULL);
2512
2513       hue += 1.0 / N_COLORS;
2514       
2515       tmp = g_slist_next (tmp);
2516     }
2517 }
2518
2519 static void
2520 buffer_cycle_colors (Buffer *buffer)
2521 {
2522   GSList *tmp;
2523   gdouble hue = buffer->start_hue;
2524   
2525   tmp = buffer->color_tags;
2526   while (tmp != NULL)
2527     {
2528       GdkColor color;
2529       
2530       hue_to_color (hue, &color);
2531       
2532       g_object_set (tmp->data,
2533                     "foreground_gdk", &color,
2534                     NULL);
2535
2536       hue += 1.0 / N_COLORS;
2537       if (hue > 1.0)
2538         hue = 0.0;
2539       
2540       tmp = g_slist_next (tmp);
2541     }
2542
2543   buffer->start_hue += 1.0 / N_COLORS;
2544   if (buffer->start_hue > 1.0)
2545     buffer->start_hue = 0.0;
2546 }
2547
2548 static void
2549 close_view (View *view)
2550 {
2551   views = g_slist_remove (views, view);
2552   buffer_unref (view->buffer);
2553   gtk_widget_destroy (view->window);
2554   g_object_unref (view->item_factory);
2555   
2556   g_free (view);
2557   
2558   if (!views)
2559     gtk_main_quit ();
2560 }
2561
2562 static void
2563 check_close_view (View *view)
2564 {
2565   if (view->buffer->refcount > 1 ||
2566       check_buffer_saved (view->buffer))
2567     close_view (view);
2568 }
2569
2570 static void
2571 view_set_title (View *view)
2572 {
2573   char *pretty_name = buffer_pretty_name (view->buffer);
2574   char *title = g_strconcat ("testtext - ", pretty_name, NULL);
2575
2576   gtk_window_set_title (GTK_WINDOW (view->window), title);
2577
2578   g_free (pretty_name);
2579   g_free (title);
2580 }
2581
2582 static void
2583 cursor_set_callback (GtkTextBuffer     *buffer,
2584                      const GtkTextIter *location,
2585                      GtkTextMark       *mark,
2586                      gpointer           user_data)
2587 {
2588   GtkTextView *text_view;
2589
2590   /* Redraw tab windows if the cursor moves
2591    * on the mapped widget (windows may not exist before realization...
2592    */
2593   
2594   text_view = GTK_TEXT_VIEW (user_data);
2595   
2596   if (gtk_widget_get_mapped (GTK_WIDGET (text_view)) &&
2597       mark == gtk_text_buffer_get_insert (buffer))
2598     {
2599       GdkWindow *tab_window;
2600
2601       tab_window = gtk_text_view_get_window (text_view,
2602                                              GTK_TEXT_WINDOW_TOP);
2603
2604       gdk_window_invalidate_rect (tab_window, NULL, FALSE);
2605       
2606       tab_window = gtk_text_view_get_window (text_view,
2607                                              GTK_TEXT_WINDOW_BOTTOM);
2608
2609       gdk_window_invalidate_rect (tab_window, NULL, FALSE);
2610     }
2611 }
2612
2613 static gint
2614 tab_stops_expose (GtkWidget      *widget,
2615                   GdkEventExpose *event,
2616                   gpointer        user_data)
2617 {
2618   gint first_x;
2619   gint last_x;
2620   gint i;
2621   GdkWindow *top_win;
2622   GdkWindow *bottom_win;
2623   GtkTextView *text_view;
2624   GtkTextWindowType type;
2625   GdkDrawable *target;
2626   gint *positions = NULL;
2627   gint size;
2628   GtkTextAttributes *attrs;
2629   GtkTextIter insert;
2630   GtkTextBuffer *buffer;
2631   gboolean in_pixels;
2632   
2633   text_view = GTK_TEXT_VIEW (widget);
2634   
2635   /* See if this expose is on the tab stop window */
2636   top_win = gtk_text_view_get_window (text_view,
2637                                       GTK_TEXT_WINDOW_TOP);
2638
2639   bottom_win = gtk_text_view_get_window (text_view,
2640                                          GTK_TEXT_WINDOW_BOTTOM);
2641
2642   if (event->window == top_win)
2643     {
2644       type = GTK_TEXT_WINDOW_TOP;
2645       target = top_win;
2646     }
2647   else if (event->window == bottom_win)
2648     {
2649       type = GTK_TEXT_WINDOW_BOTTOM;
2650       target = bottom_win;
2651     }
2652   else
2653     return FALSE;
2654   
2655   first_x = event->area.x;
2656   last_x = first_x + event->area.width;
2657
2658   gtk_text_view_window_to_buffer_coords (text_view,
2659                                          type,
2660                                          first_x,
2661                                          0,
2662                                          &first_x,
2663                                          NULL);
2664
2665   gtk_text_view_window_to_buffer_coords (text_view,
2666                                          type,
2667                                          last_x,
2668                                          0,
2669                                          &last_x,
2670                                          NULL);
2671
2672   buffer = gtk_text_view_get_buffer (text_view);
2673
2674   gtk_text_buffer_get_iter_at_mark (buffer,
2675                                     &insert,
2676                                     gtk_text_buffer_get_mark (buffer,
2677                                                               "insert"));
2678   
2679   attrs = gtk_text_attributes_new ();
2680
2681   gtk_text_iter_get_attributes (&insert, attrs);
2682
2683   if (attrs->tabs)
2684     {
2685       size = pango_tab_array_get_size (attrs->tabs);
2686       
2687       pango_tab_array_get_tabs (attrs->tabs,
2688                                 NULL,
2689                                 &positions);
2690
2691       in_pixels = pango_tab_array_get_positions_in_pixels (attrs->tabs);
2692     }
2693   else
2694     {
2695       size = 0;
2696       in_pixels = FALSE;
2697     }
2698       
2699   gtk_text_attributes_unref (attrs);
2700   
2701   i = 0;
2702   while (i < size)
2703     {
2704       gint pos;
2705
2706       if (!in_pixels)
2707         positions[i] = PANGO_PIXELS (positions[i]);
2708       
2709       gtk_text_view_buffer_to_window_coords (text_view,
2710                                              type,
2711                                              positions[i],
2712                                              0,
2713                                              &pos,
2714                                              NULL);
2715       
2716       gdk_draw_line (target, 
2717                      widget->style->fg_gc [widget->state],
2718                      pos, 0,
2719                      pos, 15); 
2720       
2721       ++i;
2722     }
2723
2724   g_free (positions);
2725
2726   return TRUE;
2727 }
2728
2729 static void
2730 get_lines (GtkTextView  *text_view,
2731            gint          first_y,
2732            gint          last_y,
2733            GArray       *buffer_coords,
2734            GArray       *numbers,
2735            gint         *countp)
2736 {
2737   GtkTextIter iter;
2738   gint count;
2739   gint size;  
2740
2741   g_array_set_size (buffer_coords, 0);
2742   g_array_set_size (numbers, 0);
2743   
2744   /* Get iter at first y */
2745   gtk_text_view_get_line_at_y (text_view, &iter, first_y, NULL);
2746
2747   /* For each iter, get its location and add it to the arrays.
2748    * Stop when we pass last_y
2749    */
2750   count = 0;
2751   size = 0;
2752
2753   while (!gtk_text_iter_is_end (&iter))
2754     {
2755       gint y, height;
2756       gint line_num;
2757       
2758       gtk_text_view_get_line_yrange (text_view, &iter, &y, &height);
2759
2760       g_array_append_val (buffer_coords, y);
2761       line_num = gtk_text_iter_get_line (&iter);
2762       g_array_append_val (numbers, line_num);
2763       
2764       ++count;
2765
2766       if ((y + height) >= last_y)
2767         break;
2768       
2769       gtk_text_iter_forward_line (&iter);
2770     }
2771
2772   *countp = count;
2773 }
2774
2775 static gint
2776 line_numbers_expose (GtkWidget      *widget,
2777                      GdkEventExpose *event,
2778                      gpointer        user_data)
2779 {
2780   gint count;
2781   GArray *numbers;
2782   GArray *pixels;
2783   gint first_y;
2784   gint last_y;
2785   gint i;
2786   GdkWindow *left_win;
2787   GdkWindow *right_win;
2788   PangoLayout *layout;
2789   GtkTextView *text_view;
2790   GtkTextWindowType type;
2791   GdkDrawable *target;
2792   
2793   text_view = GTK_TEXT_VIEW (widget);
2794   
2795   /* See if this expose is on the line numbers window */
2796   left_win = gtk_text_view_get_window (text_view,
2797                                        GTK_TEXT_WINDOW_LEFT);
2798
2799   right_win = gtk_text_view_get_window (text_view,
2800                                         GTK_TEXT_WINDOW_RIGHT);
2801
2802   if (event->window == left_win)
2803     {
2804       type = GTK_TEXT_WINDOW_LEFT;
2805       target = left_win;
2806     }
2807   else if (event->window == right_win)
2808     {
2809       type = GTK_TEXT_WINDOW_RIGHT;
2810       target = right_win;
2811     }
2812   else
2813     return FALSE;
2814   
2815   first_y = event->area.y;
2816   last_y = first_y + event->area.height;
2817
2818   gtk_text_view_window_to_buffer_coords (text_view,
2819                                          type,
2820                                          0,
2821                                          first_y,
2822                                          NULL,
2823                                          &first_y);
2824
2825   gtk_text_view_window_to_buffer_coords (text_view,
2826                                          type,
2827                                          0,
2828                                          last_y,
2829                                          NULL,
2830                                          &last_y);
2831
2832   numbers = g_array_new (FALSE, FALSE, sizeof (gint));
2833   pixels = g_array_new (FALSE, FALSE, sizeof (gint));
2834   
2835   get_lines (text_view,
2836              first_y,
2837              last_y,
2838              pixels,
2839              numbers,
2840              &count);
2841   
2842   /* Draw fully internationalized numbers! */
2843   
2844   layout = gtk_widget_create_pango_layout (widget, "");
2845   
2846   i = 0;
2847   while (i < count)
2848     {
2849       gint pos;
2850       gchar *str;
2851       
2852       gtk_text_view_buffer_to_window_coords (text_view,
2853                                              type,
2854                                              0,
2855                                              g_array_index (pixels, gint, i),
2856                                              NULL,
2857                                              &pos);
2858
2859       str = g_strdup_printf ("%d", g_array_index (numbers, gint, i));
2860
2861       pango_layout_set_text (layout, str, -1);
2862
2863       gtk_paint_layout (widget->style,
2864                         target,
2865                         gtk_widget_get_state (widget),
2866                         FALSE,
2867                         NULL,
2868                         widget,
2869                         NULL,
2870                         2, pos + 2,
2871                         layout);
2872
2873       g_free (str);
2874       
2875       ++i;
2876     }
2877
2878   g_array_free (pixels, TRUE);
2879   g_array_free (numbers, TRUE);
2880   
2881   g_object_unref (layout);
2882
2883   /* don't stop emission, need to draw children */
2884   return FALSE;
2885 }
2886
2887 static void
2888 selection_changed (GtkTextBuffer *buffer,
2889                    GParamSpec    *pspec,
2890                    GtkWidget     *copy_menu)
2891 {
2892   gtk_widget_set_sensitive (copy_menu, gtk_text_buffer_get_has_selection (buffer));
2893 }
2894
2895 static View *
2896 create_view (Buffer *buffer)
2897 {
2898   View *view;
2899   GtkWidget *copy_menu;
2900   GtkWidget *sw;
2901   GtkWidget *vbox;
2902   
2903   view = g_new0 (View, 1);
2904   views = g_slist_prepend (views, view);
2905
2906   view->buffer = buffer;
2907   buffer_ref (buffer);
2908   
2909   view->window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2910   g_object_set_data (G_OBJECT (view->window), "view", view);
2911   
2912   g_signal_connect (view->window, "delete_event",
2913                     G_CALLBACK (delete_event_cb), NULL);
2914
2915   view->accel_group = gtk_accel_group_new ();
2916   view->item_factory = gtk_item_factory_new (GTK_TYPE_MENU_BAR, "<main>", view->accel_group);
2917   g_object_set_data (G_OBJECT (view->item_factory), "view", view);
2918   
2919   gtk_item_factory_create_items (view->item_factory, G_N_ELEMENTS (menu_items), menu_items, view);
2920
2921   /* make the Copy menu item sensitivity update according to the selection */
2922   copy_menu = gtk_item_factory_get_item (view->item_factory, "<main>/Edit/Copy");
2923   gtk_widget_set_sensitive (copy_menu, gtk_text_buffer_get_has_selection (view->buffer->buffer));
2924   g_signal_connect (view->buffer->buffer,
2925                     "notify::has-selection",
2926                     G_CALLBACK (selection_changed),
2927                     copy_menu);
2928
2929   gtk_window_add_accel_group (GTK_WINDOW (view->window), view->accel_group);
2930
2931   vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, FALSE, 0);
2932   gtk_container_add (GTK_CONTAINER (view->window), vbox);
2933
2934   gtk_box_pack_start (GTK_BOX (vbox),
2935                       gtk_item_factory_get_widget (view->item_factory, "<main>"),
2936                       FALSE, FALSE, 0);
2937   
2938   sw = gtk_scrolled_window_new (NULL, NULL);
2939   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
2940                                  GTK_POLICY_AUTOMATIC,
2941                                  GTK_POLICY_AUTOMATIC);
2942
2943   view->text_view = gtk_text_view_new_with_buffer (buffer->buffer);
2944   gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (view->text_view),
2945                                GTK_WRAP_WORD);
2946
2947   /* Make sure border width works, no real reason to do this other than testing */
2948   gtk_container_set_border_width (GTK_CONTAINER (view->text_view),
2949                                   10);
2950   
2951   /* Draw tab stops in the top and bottom windows. */
2952   
2953   gtk_text_view_set_border_window_size (GTK_TEXT_VIEW (view->text_view),
2954                                         GTK_TEXT_WINDOW_TOP,
2955                                         15);
2956
2957   gtk_text_view_set_border_window_size (GTK_TEXT_VIEW (view->text_view),
2958                                         GTK_TEXT_WINDOW_BOTTOM,
2959                                         15);
2960
2961   g_signal_connect (view->text_view,
2962                     "expose_event",
2963                     G_CALLBACK (tab_stops_expose),
2964                     NULL);  
2965
2966   g_signal_connect (view->buffer->buffer,
2967                     "mark_set",
2968                     G_CALLBACK (cursor_set_callback),
2969                     view->text_view);
2970   
2971   /* Draw line numbers in the side windows; we should really be
2972    * more scientific about what width we set them to.
2973    */
2974   gtk_text_view_set_border_window_size (GTK_TEXT_VIEW (view->text_view),
2975                                         GTK_TEXT_WINDOW_RIGHT,
2976                                         30);
2977   
2978   gtk_text_view_set_border_window_size (GTK_TEXT_VIEW (view->text_view),
2979                                         GTK_TEXT_WINDOW_LEFT,
2980                                         30);
2981   
2982   g_signal_connect (view->text_view,
2983                     "expose_event",
2984                     G_CALLBACK (line_numbers_expose),
2985                     NULL);
2986   
2987   gtk_box_pack_start (GTK_BOX (vbox), sw, TRUE, TRUE, 0);
2988   gtk_container_add (GTK_CONTAINER (sw), view->text_view);
2989
2990   gtk_window_set_default_size (GTK_WINDOW (view->window), 500, 500);
2991
2992   gtk_widget_grab_focus (view->text_view);
2993
2994   view_set_title (view);
2995   view_init_menus (view);
2996
2997   view_add_example_widgets (view);
2998   
2999   gtk_widget_show_all (view->window);
3000   return view;
3001 }
3002
3003 static void
3004 view_add_example_widgets (View *view)
3005 {
3006   GtkTextChildAnchor *anchor;
3007   Buffer *buffer;
3008
3009   buffer = view->buffer;
3010   
3011   anchor = g_object_get_data (G_OBJECT (buffer->buffer),
3012                               "anchor");
3013
3014   if (anchor && !gtk_text_child_anchor_get_deleted (anchor))
3015     {
3016       GtkWidget *widget;
3017
3018       widget = gtk_button_new_with_label ("Foo");
3019       
3020       gtk_text_view_add_child_at_anchor (GTK_TEXT_VIEW (view->text_view),
3021                                          widget,
3022                                          anchor);
3023
3024       gtk_widget_show (widget);
3025     }
3026 }
3027
3028 void
3029 test_init (void)
3030 {
3031   if (g_file_test ("../gdk-pixbuf/libpixbufloader-pnm.la",
3032                    G_FILE_TEST_EXISTS))
3033     {
3034       g_setenv ("GDK_PIXBUF_MODULE_FILE", "../gdk-pixbuf/loaders.cache", TRUE);
3035       g_setenv ("GTK_IM_MODULE_FILE", "../modules/input/immodules.cache", TRUE);
3036     }
3037 }
3038
3039 int
3040 main (int argc, char** argv)
3041 {
3042   Buffer *buffer;
3043   View *view;
3044   int i;
3045
3046   test_init ();
3047   gtk_init (&argc, &argv);
3048   
3049   buffer = create_buffer ();
3050   view = create_view (buffer);
3051   buffer_unref (buffer);
3052   
3053   push_active_window (GTK_WINDOW (view->window));
3054   for (i=1; i < argc; i++)
3055     {
3056       char *filename;
3057
3058       /* Quick and dirty canonicalization - better should be in GLib
3059        */
3060
3061       if (!g_path_is_absolute (argv[i]))
3062         {
3063           char *cwd = g_get_current_dir ();
3064           filename = g_strconcat (cwd, "/", argv[i], NULL);
3065           g_free (cwd);
3066         }
3067       else
3068         filename = argv[i];
3069
3070       open_ok_func (filename, view);
3071
3072       if (filename != argv[i])
3073         g_free (filename);
3074     }
3075   pop_active_window ();
3076   
3077   gtk_main ();
3078
3079   return 0;
3080 }
3081
3082