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