]> Pileus Git - ~andy/gtk/blob - tests/testtext.c
5ad9304bcac6f79acfc6274432b28394a595d168
[~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_FLAGS (button, GTK_CAN_DEFAULT);
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_FLAGS (button, GTK_CAN_DEFAULT);
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_FLAGS (button, GTK_CAN_DEFAULT);
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 do_cursor_visible_changed (gpointer callback_data,
988                            guint callback_action,
989                            GtkWidget *widget)
990 {
991   View *view = view_from_widget (widget);
992
993   gtk_text_view_set_cursor_visible (GTK_TEXT_VIEW (view->text_view), callback_action);
994 }
995
996 static void
997 do_color_cycle_changed (gpointer callback_data,
998                         guint callback_action,
999                         GtkWidget *widget)
1000 {
1001   View *view = view_from_widget (widget);
1002
1003   buffer_set_colors (view->buffer, callback_action);
1004 }
1005
1006 static void
1007 do_apply_editable (gpointer callback_data,
1008                    guint callback_action,
1009                    GtkWidget *widget)
1010 {
1011   View *view = view_from_widget (widget);
1012   GtkTextIter start;
1013   GtkTextIter end;
1014   
1015   if (gtk_text_buffer_get_selection_bounds (view->buffer->buffer,
1016                                             &start, &end))
1017     {
1018       if (callback_action)
1019         {
1020           gtk_text_buffer_remove_tag (view->buffer->buffer,
1021                                       view->buffer->not_editable_tag,
1022                                       &start, &end);
1023         }
1024       else
1025         {
1026           gtk_text_buffer_apply_tag (view->buffer->buffer,
1027                                      view->buffer->not_editable_tag,
1028                                      &start, &end);
1029         }
1030     }
1031 }
1032
1033 static void
1034 do_apply_invisible (gpointer callback_data,
1035                     guint callback_action,
1036                     GtkWidget *widget)
1037 {
1038   View *view = view_from_widget (widget);
1039   GtkTextIter start;
1040   GtkTextIter end;
1041   
1042   if (gtk_text_buffer_get_selection_bounds (view->buffer->buffer,
1043                                             &start, &end))
1044     {
1045       if (callback_action)
1046         {
1047           gtk_text_buffer_remove_tag (view->buffer->buffer,
1048                                       view->buffer->invisible_tag,
1049                                       &start, &end);
1050         }
1051       else
1052         {
1053           gtk_text_buffer_apply_tag (view->buffer->buffer,
1054                                      view->buffer->invisible_tag,
1055                                      &start, &end);
1056         }
1057     }
1058 }
1059
1060 static void
1061 do_apply_rise (gpointer callback_data,
1062                guint callback_action,
1063                GtkWidget *widget)
1064 {
1065   View *view = view_from_widget (widget);
1066   GtkTextIter start;
1067   GtkTextIter end;
1068   
1069   if (gtk_text_buffer_get_selection_bounds (view->buffer->buffer,
1070                                             &start, &end))
1071     {
1072       if (callback_action)
1073         {
1074           gtk_text_buffer_remove_tag (view->buffer->buffer,
1075                                       view->buffer->rise_tag,
1076                                       &start, &end);
1077         }
1078       else
1079         {
1080           gtk_text_buffer_apply_tag (view->buffer->buffer,
1081                                      view->buffer->rise_tag,
1082                                      &start, &end);
1083         }
1084     }
1085 }
1086
1087 static void
1088 do_apply_large (gpointer callback_data,
1089                 guint callback_action,
1090                 GtkWidget *widget)
1091 {
1092   View *view = view_from_widget (widget);
1093   GtkTextIter start;
1094   GtkTextIter end;
1095   
1096   if (gtk_text_buffer_get_selection_bounds (view->buffer->buffer,
1097                                             &start, &end))
1098     {
1099       if (callback_action)
1100         {
1101           gtk_text_buffer_remove_tag (view->buffer->buffer,
1102                                       view->buffer->large_tag,
1103                                       &start, &end);
1104         }
1105       else
1106         {
1107           gtk_text_buffer_apply_tag (view->buffer->buffer,
1108                                      view->buffer->large_tag,
1109                                      &start, &end);
1110         }
1111     }
1112 }
1113
1114 static void
1115 do_apply_indent (gpointer callback_data,
1116                  guint callback_action,
1117                  GtkWidget *widget)
1118 {
1119   View *view = view_from_widget (widget);
1120   GtkTextIter start;
1121   GtkTextIter end;
1122   
1123   if (gtk_text_buffer_get_selection_bounds (view->buffer->buffer,
1124                                             &start, &end))
1125     {
1126       if (callback_action)
1127         {
1128           gtk_text_buffer_remove_tag (view->buffer->buffer,
1129                                       view->buffer->indent_tag,
1130                                       &start, &end);
1131         }
1132       else
1133         {
1134           gtk_text_buffer_apply_tag (view->buffer->buffer,
1135                                      view->buffer->indent_tag,
1136                                      &start, &end);
1137         }
1138     }
1139 }
1140
1141 static void
1142 do_apply_margin (gpointer callback_data,
1143                  guint callback_action,
1144                  GtkWidget *widget)
1145 {
1146   View *view = view_from_widget (widget);
1147   GtkTextIter start;
1148   GtkTextIter end;
1149   
1150   if (gtk_text_buffer_get_selection_bounds (view->buffer->buffer,
1151                                             &start, &end))
1152     {
1153       if (callback_action)
1154         {
1155           gtk_text_buffer_remove_tag (view->buffer->buffer,
1156                                       view->buffer->margin_tag,
1157                                       &start, &end);
1158         }
1159       else
1160         {
1161           gtk_text_buffer_apply_tag (view->buffer->buffer,
1162                                      view->buffer->margin_tag,
1163                                      &start, &end);
1164         }
1165     }
1166 }
1167
1168 static void
1169 do_apply_tabs (gpointer callback_data,
1170                guint callback_action,
1171                GtkWidget *widget)
1172 {
1173   View *view = view_from_widget (widget);
1174   GtkTextIter start;
1175   GtkTextIter end;
1176   
1177   if (gtk_text_buffer_get_selection_bounds (view->buffer->buffer,
1178                                             &start, &end))
1179     {
1180       if (callback_action)
1181         {
1182           gtk_text_buffer_remove_tag (view->buffer->buffer,
1183                                       view->buffer->custom_tabs_tag,
1184                                       &start, &end);
1185         }
1186       else
1187         {
1188           gtk_text_buffer_apply_tag (view->buffer->buffer,
1189                                      view->buffer->custom_tabs_tag,
1190                                      &start, &end);
1191         }
1192     }
1193 }
1194
1195 static void
1196 do_apply_colors (gpointer callback_data,
1197                  guint callback_action,
1198                  GtkWidget *widget)
1199 {
1200   View *view = view_from_widget (widget);
1201   Buffer *buffer = view->buffer;
1202   GtkTextIter start;
1203   GtkTextIter end;
1204   
1205   if (gtk_text_buffer_get_selection_bounds (view->buffer->buffer,
1206                                             &start, &end))
1207     {
1208       if (!callback_action)
1209         {
1210           GSList *tmp;
1211           
1212           tmp = buffer->color_tags;
1213           while (tmp != NULL)
1214             {
1215               gtk_text_buffer_remove_tag (view->buffer->buffer,
1216                                           tmp->data,
1217                                           &start, &end);              
1218               tmp = g_slist_next (tmp);
1219             }
1220         }
1221       else
1222         {
1223           GSList *tmp;
1224           
1225           tmp = buffer->color_tags;
1226           while (TRUE)
1227             {
1228               GtkTextIter next;
1229               gboolean done = FALSE;
1230               
1231               next = start;
1232               gtk_text_iter_forward_char (&next);
1233               gtk_text_iter_forward_char (&next);
1234
1235               if (gtk_text_iter_compare (&next, &end) >= 0)
1236                 {
1237                   next = end;
1238                   done = TRUE;
1239                 }
1240               
1241               gtk_text_buffer_apply_tag (view->buffer->buffer,
1242                                          tmp->data,
1243                                          &start, &next);
1244
1245               start = next;
1246
1247               if (done)
1248                 return;
1249               
1250               tmp = g_slist_next (tmp);
1251               if (tmp == NULL)
1252                 tmp = buffer->color_tags;
1253             } 
1254         }
1255     }
1256 }
1257
1258 static void
1259 do_remove_tags (gpointer callback_data,
1260                 guint callback_action,
1261                 GtkWidget *widget)
1262 {
1263   View *view = view_from_widget (widget);
1264   GtkTextIter start;
1265   GtkTextIter end;
1266   
1267   if (gtk_text_buffer_get_selection_bounds (view->buffer->buffer,
1268                                             &start, &end))
1269     {
1270       gtk_text_buffer_remove_all_tags (view->buffer->buffer,
1271                                        &start, &end);
1272     }
1273 }
1274
1275 static void
1276 do_properties (gpointer callback_data,
1277                 guint callback_action,
1278                 GtkWidget *widget)
1279 {
1280   View *view = view_from_widget (widget);
1281
1282   create_prop_editor (G_OBJECT (view->text_view), 0);
1283 }
1284
1285 static void
1286 rich_text_store_populate (GtkListStore  *store,
1287                           GtkTextBuffer *buffer,
1288                           gboolean       deserialize)
1289 {
1290   GdkAtom *formats;
1291   gint     n_formats;
1292   gint     i;
1293
1294   gtk_list_store_clear (store);
1295
1296   if (deserialize)
1297     formats = gtk_text_buffer_get_deserialize_formats (buffer, &n_formats);
1298   else
1299     formats = gtk_text_buffer_get_serialize_formats (buffer, &n_formats);
1300
1301   for (i = 0; i < n_formats; i++)
1302     {
1303       GtkTreeIter  iter;
1304       gchar       *mime_type;
1305       gboolean     can_create_tags = FALSE;
1306
1307       mime_type = gdk_atom_name (formats[i]);
1308
1309       if (deserialize)
1310         can_create_tags =
1311           gtk_text_buffer_deserialize_get_can_create_tags (buffer, formats[i]);
1312
1313       gtk_list_store_append (store, &iter);
1314       gtk_list_store_set (store, &iter,
1315                           0, formats[i],
1316                           1, mime_type,
1317                           2, can_create_tags,
1318                           -1);
1319
1320       g_free (mime_type);
1321     }
1322
1323   g_free (formats);
1324 }
1325
1326 static void
1327 rich_text_paste_target_list_notify (GtkTextBuffer    *buffer,
1328                                     const GParamSpec *pspec,
1329                                     GtkListStore     *store)
1330 {
1331   rich_text_store_populate (store, buffer, TRUE);
1332 }
1333
1334 static void
1335 rich_text_copy_target_list_notify (GtkTextBuffer    *buffer,
1336                                    const GParamSpec *pspec,
1337                                    GtkListStore     *store)
1338 {
1339   rich_text_store_populate (store, buffer, FALSE);
1340 }
1341
1342 static void
1343 rich_text_can_create_tags_toggled (GtkCellRendererToggle *toggle,
1344                                    const gchar           *path,
1345                                    GtkTreeModel          *model)
1346 {
1347   GtkTreeIter iter;
1348
1349   if (gtk_tree_model_get_iter_from_string (model, &iter, path))
1350     {
1351       GtkTextBuffer *buffer;
1352       GdkAtom        format;
1353       gboolean       can_create_tags;
1354
1355       buffer = g_object_get_data (G_OBJECT (model), "buffer");
1356
1357       gtk_tree_model_get (model, &iter,
1358                           0, &format,
1359                           2, &can_create_tags,
1360                           -1);
1361
1362       gtk_text_buffer_deserialize_set_can_create_tags (buffer, format,
1363                                                        !can_create_tags);
1364
1365       gtk_list_store_set (GTK_LIST_STORE (model), &iter,
1366                           2, !can_create_tags,
1367                           -1);
1368     }
1369 }
1370
1371 static void
1372 rich_text_unregister_clicked (GtkWidget   *button,
1373                               GtkTreeView *tv)
1374 {
1375   GtkTreeSelection *sel = gtk_tree_view_get_selection (tv);
1376   GtkTreeModel     *model;
1377   GtkTreeIter       iter;
1378
1379   if (gtk_tree_selection_get_selected (sel, &model, &iter))
1380     {
1381       GtkTextBuffer *buffer;
1382       gboolean       deserialize;
1383       GdkAtom        format;
1384
1385       buffer = g_object_get_data (G_OBJECT (model), "buffer");
1386       deserialize = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (model),
1387                                                         "deserialize"));
1388
1389       gtk_tree_model_get (model, &iter,
1390                           0, &format,
1391                           -1);
1392
1393       if (deserialize)
1394         gtk_text_buffer_unregister_deserialize_format (buffer, format);
1395       else
1396         gtk_text_buffer_unregister_serialize_format (buffer, format);
1397     }
1398 }
1399
1400 static void
1401 rich_text_register_clicked (GtkWidget   *button,
1402                             GtkTreeView *tv)
1403 {
1404   GtkWidget *dialog;
1405   GtkWidget *label;
1406   GtkWidget *entry;
1407
1408   dialog = gtk_dialog_new_with_buttons ("Register new Tagset",
1409                                         GTK_WINDOW (gtk_widget_get_toplevel (button)),
1410                                         GTK_DIALOG_DESTROY_WITH_PARENT,
1411                                         GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1412                                         GTK_STOCK_OK,     GTK_RESPONSE_OK,
1413                                         NULL);
1414   label = gtk_label_new ("Enter tagset name or leave blank for "
1415                          "unrestricted internal format:");
1416   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), label,
1417                       FALSE, FALSE, 0);
1418
1419   entry = gtk_entry_new ();
1420   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), entry,
1421                       FALSE, FALSE, 0);
1422
1423   gtk_widget_show_all (dialog);
1424
1425   if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK)
1426     {
1427       GtkTreeModel  *model  = gtk_tree_view_get_model (tv);
1428       GtkTextBuffer *buffer = g_object_get_data (G_OBJECT (model), "buffer");
1429       const gchar   *tagset = gtk_entry_get_text (GTK_ENTRY (entry));
1430       gboolean       deserialize;
1431
1432       deserialize = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (model),
1433                                                         "deserialize"));
1434
1435       if (tagset && ! strlen (tagset))
1436         tagset = NULL;
1437
1438       if (deserialize)
1439         gtk_text_buffer_register_deserialize_tagset (buffer, tagset);
1440       else
1441         gtk_text_buffer_register_serialize_tagset (buffer, tagset);
1442     }
1443
1444   gtk_widget_destroy (dialog);
1445 }
1446
1447 static void
1448 do_rich_text (gpointer callback_data,
1449               guint deserialize,
1450               GtkWidget *widget)
1451 {
1452   View *view = view_from_widget (widget);
1453   GtkTextBuffer *buffer;
1454   GtkWidget *dialog;
1455   GtkWidget *tv;
1456   GtkWidget *sw;
1457   GtkWidget *hbox;
1458   GtkWidget *button;
1459   GtkListStore *store;
1460
1461   buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (view->text_view));
1462
1463   dialog = gtk_dialog_new_with_buttons (deserialize ?
1464                                         "Rich Text Paste & Drop" :
1465                                         "Rich Text Copy & Drag",
1466                                         GTK_WINDOW (view->window),
1467                                         GTK_DIALOG_DESTROY_WITH_PARENT,
1468                                         GTK_STOCK_CLOSE, 0,
1469                                         NULL);
1470   g_signal_connect (dialog, "response",
1471                     G_CALLBACK (gtk_widget_destroy),
1472                     NULL);
1473
1474   store = gtk_list_store_new (3,
1475                               G_TYPE_POINTER,
1476                               G_TYPE_STRING,
1477                               G_TYPE_BOOLEAN);
1478
1479   g_object_set_data (G_OBJECT (store), "buffer", buffer);
1480   g_object_set_data (G_OBJECT (store), "deserialize",
1481                      GUINT_TO_POINTER (deserialize));
1482
1483   tv = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store));
1484   g_object_unref (store);
1485
1486   gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (tv),
1487                                                0, "Rich Text Format",
1488                                                gtk_cell_renderer_text_new (),
1489                                                "text", 1,
1490                                                NULL);
1491
1492   if (deserialize)
1493     {
1494       GtkCellRenderer *renderer = gtk_cell_renderer_toggle_new ();
1495
1496       gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (tv),
1497                                                    1, "Can Create Tags",
1498                                                    renderer,
1499                                                    "active", 2,
1500                                                    NULL);
1501
1502       g_signal_connect (renderer, "toggled",
1503                         G_CALLBACK (rich_text_can_create_tags_toggled),
1504                         store);
1505     }
1506
1507   sw = gtk_scrolled_window_new (NULL, NULL);
1508   gtk_widget_set_size_request (sw, 300, 100);
1509   gtk_container_add (GTK_CONTAINER (GTK_DIALOG (dialog)->vbox), sw);
1510
1511   gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (sw), tv);
1512
1513   hbox = gtk_hbox_new (FALSE, 6);
1514   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), hbox,
1515                       FALSE, FALSE, 0);
1516
1517   button = gtk_button_new_with_label ("Unregister Selected Format");
1518   gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
1519
1520   g_signal_connect (button, "clicked",
1521                     G_CALLBACK (rich_text_unregister_clicked),
1522                     tv);
1523
1524   button = gtk_button_new_with_label ("Register New Tagset\n"
1525                                       "for the Internal Format");
1526   gtk_box_pack_start (GTK_BOX (hbox), button, FALSE, FALSE, 0);
1527
1528   g_signal_connect (button, "clicked",
1529                     G_CALLBACK (rich_text_register_clicked),
1530                     tv);
1531
1532   if (deserialize)
1533     g_signal_connect_object (buffer, "notify::paste-target-list",
1534                              G_CALLBACK (rich_text_paste_target_list_notify),
1535                              G_OBJECT (store), 0);
1536   else
1537     g_signal_connect_object (buffer, "notify::copy-target-list",
1538                              G_CALLBACK (rich_text_copy_target_list_notify),
1539                              G_OBJECT (store), 0);
1540
1541   rich_text_store_populate (store, buffer, deserialize);
1542
1543   gtk_widget_show_all (dialog);
1544 }
1545
1546 enum
1547 {
1548   RESPONSE_FORWARD,
1549   RESPONSE_BACKWARD
1550 };
1551
1552 static void
1553 dialog_response_callback (GtkWidget *dialog, gint response_id, gpointer data)
1554 {
1555   GtkTextBuffer *buffer;
1556   View *view = data;
1557   GtkTextIter start, end;
1558   gchar *search_string;
1559
1560   if (response_id != RESPONSE_FORWARD &&
1561       response_id != RESPONSE_BACKWARD)
1562     {
1563       gtk_widget_destroy (dialog);
1564       return;
1565     }
1566   
1567   buffer = g_object_get_data (G_OBJECT (dialog), "buffer");
1568
1569   gtk_text_buffer_get_bounds (buffer, &start, &end);
1570   
1571   search_string = gtk_text_iter_get_text (&start, &end);
1572
1573   g_print ("Searching for `%s'\n", search_string);
1574
1575   if (response_id == RESPONSE_FORWARD)
1576     buffer_search_forward (view->buffer, search_string, view);
1577   else if (response_id == RESPONSE_BACKWARD)
1578     buffer_search_backward (view->buffer, search_string, view);
1579     
1580   g_free (search_string);
1581   
1582   gtk_widget_destroy (dialog);
1583 }
1584
1585 static void
1586 do_copy  (gpointer callback_data,
1587           guint callback_action,
1588           GtkWidget *widget)
1589 {
1590   View *view = view_from_widget (widget);
1591   GtkTextBuffer *buffer;
1592
1593   buffer = view->buffer->buffer;
1594
1595   gtk_text_buffer_copy_clipboard (buffer,
1596                                   gtk_clipboard_get (GDK_NONE));
1597 }
1598
1599 static void
1600 do_search (gpointer callback_data,
1601            guint callback_action,
1602            GtkWidget *widget)
1603 {
1604   View *view = view_from_widget (widget);
1605   GtkWidget *dialog;
1606   GtkWidget *search_text;
1607   GtkTextBuffer *buffer;
1608
1609   dialog = gtk_dialog_new_with_buttons ("Search",
1610                                         GTK_WINDOW (view->window),
1611                                         GTK_DIALOG_DESTROY_WITH_PARENT,
1612                                         "Forward", RESPONSE_FORWARD,
1613                                         "Backward", RESPONSE_BACKWARD,
1614                                         GTK_STOCK_CANCEL,
1615                                         GTK_RESPONSE_NONE, NULL);
1616
1617
1618   buffer = gtk_text_buffer_new (NULL);
1619
1620   search_text = gtk_text_view_new_with_buffer (buffer);
1621
1622   g_object_unref (buffer);
1623   
1624   gtk_box_pack_end (GTK_BOX (GTK_DIALOG (dialog)->vbox),
1625                     search_text,
1626                     TRUE, TRUE, 0);
1627
1628   g_object_set_data (G_OBJECT (dialog), "buffer", buffer);
1629   
1630   g_signal_connect (dialog,
1631                     "response",
1632                     G_CALLBACK (dialog_response_callback),
1633                     view);
1634
1635   gtk_widget_show (search_text);
1636
1637   gtk_widget_grab_focus (search_text);
1638   
1639   gtk_widget_show_all (dialog);
1640 }
1641
1642 static void
1643 do_select_all (gpointer callback_data,
1644                guint callback_action,
1645                GtkWidget *widget)
1646 {
1647   View *view = view_from_widget (widget);
1648   GtkTextBuffer *buffer;
1649   GtkTextIter start, end;
1650
1651   buffer = view->buffer->buffer;
1652
1653   gtk_text_buffer_get_bounds (buffer, &start, &end);
1654   gtk_text_buffer_select_range (buffer, &start, &end);
1655 }
1656
1657 typedef struct
1658 {
1659   /* position is in coordinate system of text_view_move_child */
1660   int click_x;
1661   int click_y;
1662   int start_x;
1663   int start_y;
1664   int button;
1665 } ChildMoveInfo;
1666
1667 static gboolean
1668 movable_child_callback (GtkWidget *child,
1669                         GdkEvent  *event,
1670                         gpointer   data)
1671 {
1672   ChildMoveInfo *info;
1673   GtkTextView *text_view;
1674
1675   text_view = GTK_TEXT_VIEW (data);
1676   
1677   g_return_val_if_fail (GTK_IS_EVENT_BOX (child), FALSE);
1678   g_return_val_if_fail (gtk_widget_get_parent (child) == GTK_WIDGET (text_view), FALSE);  
1679   
1680   info = g_object_get_data (G_OBJECT (child),
1681                             "testtext-move-info");
1682
1683   if (info == NULL)
1684     {
1685       info = g_new (ChildMoveInfo, 1);      
1686       info->start_x = -1;
1687       info->start_y = -1;
1688       info->button = -1;
1689       g_object_set_data_full (G_OBJECT (child),
1690                               "testtext-move-info",
1691                               info,
1692                               g_free);
1693     }
1694   
1695   switch (event->type)
1696     {
1697     case GDK_BUTTON_PRESS:
1698       if (info->button < 0)
1699         {
1700           if (gdk_pointer_grab (event->button.window,
1701                                 FALSE,
1702                                 GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK |
1703                                 GDK_BUTTON_RELEASE_MASK,
1704                                 NULL,
1705                                 NULL,
1706                                 event->button.time) != GDK_GRAB_SUCCESS)
1707             return FALSE;
1708           
1709           info->button = event->button.button;
1710           
1711           info->start_x = child->allocation.x;
1712           info->start_y = child->allocation.y;
1713           info->click_x = child->allocation.x + event->button.x;
1714           info->click_y = child->allocation.y + event->button.y;
1715         }
1716       break;
1717
1718     case GDK_BUTTON_RELEASE:
1719       if (info->button < 0)
1720         return FALSE;
1721
1722       if (info->button == event->button.button)
1723         {
1724           int x, y;
1725           
1726           gdk_pointer_ungrab (event->button.time);
1727           info->button = -1;
1728
1729           /* convert to window coords from event box coords */
1730           x = info->start_x + (event->button.x + child->allocation.x - info->click_x);
1731           y = info->start_y + (event->button.y + child->allocation.y - info->click_y);
1732
1733           gtk_text_view_move_child (text_view,
1734                                     child,
1735                                     x, y);
1736         }
1737       break;
1738
1739     case GDK_MOTION_NOTIFY:
1740       {
1741         int x, y;
1742         
1743         if (info->button < 0)
1744           return FALSE;
1745         
1746         gdk_window_get_pointer (child->window, &x, &y, NULL); /* ensure more events */
1747
1748         /* to window coords from event box coords */
1749         x += child->allocation.x;
1750         y += child->allocation.y;
1751         
1752         x = info->start_x + (x - info->click_x);
1753         y = info->start_y + (y - info->click_y);
1754         
1755         gtk_text_view_move_child (text_view,
1756                                   child,
1757                                   x, y);
1758       }
1759       break;
1760
1761     default:
1762       break;
1763     }
1764
1765   return FALSE;
1766 }
1767
1768 static void
1769 add_movable_child (GtkTextView      *text_view,
1770                    GtkTextWindowType window)
1771 {
1772   GtkWidget *event_box;
1773   GtkWidget *label;
1774   GdkColor color;
1775   
1776   label = gtk_label_new ("Drag me around");  
1777   
1778   event_box = gtk_event_box_new ();
1779   gtk_widget_add_events (event_box,
1780                          GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
1781                          GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK);
1782
1783   color.red = 0xffff;
1784   color.green = color.blue = 0;
1785   gtk_widget_modify_bg (event_box, GTK_STATE_NORMAL, &color);
1786   
1787   gtk_container_add (GTK_CONTAINER (event_box), label);
1788
1789   gtk_widget_show_all (event_box);
1790
1791   g_signal_connect (event_box, "event",
1792                     G_CALLBACK (movable_child_callback),
1793                     text_view);
1794
1795   gtk_text_view_add_child_in_window (text_view,
1796                                      event_box,
1797                                      window,
1798                                      0, 0);
1799 }
1800
1801 static void
1802 do_add_children (gpointer callback_data,
1803                  guint callback_action,
1804                  GtkWidget *widget)
1805 {
1806   View *view = view_from_widget (widget);
1807
1808   add_movable_child (GTK_TEXT_VIEW (view->text_view),
1809                      GTK_TEXT_WINDOW_WIDGET);
1810   add_movable_child (GTK_TEXT_VIEW (view->text_view),
1811                      GTK_TEXT_WINDOW_LEFT);
1812   add_movable_child (GTK_TEXT_VIEW (view->text_view),
1813                      GTK_TEXT_WINDOW_RIGHT);
1814 }
1815
1816 static void
1817 do_add_focus_children (gpointer callback_data,
1818                        guint callback_action,
1819                        GtkWidget *widget)
1820 {
1821   View *view = view_from_widget (widget);
1822   GtkWidget *child;
1823   GtkTextChildAnchor *anchor;
1824   GtkTextIter iter;
1825   GtkTextView *text_view;
1826
1827   text_view = GTK_TEXT_VIEW (view->text_view);
1828   
1829   child = gtk_button_new_with_mnemonic ("Button _A in widget->window");
1830
1831   gtk_text_view_add_child_in_window (text_view,
1832                                      child,
1833                                      GTK_TEXT_WINDOW_WIDGET,
1834                                      200, 200);
1835
1836   child = gtk_button_new_with_mnemonic ("Button _B in widget->window");
1837
1838   gtk_text_view_add_child_in_window (text_view,
1839                                      child,
1840                                      GTK_TEXT_WINDOW_WIDGET,
1841                                      350, 300);
1842
1843   child = gtk_button_new_with_mnemonic ("Button _C in left window");
1844
1845   gtk_text_view_add_child_in_window (text_view,
1846                                      child,
1847                                      GTK_TEXT_WINDOW_LEFT,
1848                                      0, 0);
1849
1850   child = gtk_button_new_with_mnemonic ("Button _D in right window");
1851   
1852   gtk_text_view_add_child_in_window (text_view,
1853                                      child,
1854                                      GTK_TEXT_WINDOW_RIGHT,
1855                                      0, 0);
1856
1857   gtk_text_buffer_get_start_iter (view->buffer->buffer, &iter);
1858   
1859   anchor = gtk_text_buffer_create_child_anchor (view->buffer->buffer, &iter);
1860
1861   child = gtk_button_new_with_mnemonic ("Button _E in buffer");
1862   
1863   gtk_text_view_add_child_at_anchor (text_view, child, anchor);
1864
1865   anchor = gtk_text_buffer_create_child_anchor (view->buffer->buffer, &iter);
1866
1867   child = gtk_button_new_with_mnemonic ("Button _F in buffer");
1868   
1869   gtk_text_view_add_child_at_anchor (text_view, child, anchor);
1870
1871   anchor = gtk_text_buffer_create_child_anchor (view->buffer->buffer, &iter);
1872
1873   child = gtk_button_new_with_mnemonic ("Button _G in buffer");
1874   
1875   gtk_text_view_add_child_at_anchor (text_view, child, anchor);
1876
1877   /* show all the buttons */
1878   gtk_widget_show_all (view->text_view);
1879 }
1880
1881 static void
1882 view_init_menus (View *view)
1883 {
1884   GtkTextDirection direction = gtk_widget_get_direction (view->text_view);
1885   GtkWrapMode wrap_mode = gtk_text_view_get_wrap_mode (GTK_TEXT_VIEW (view->text_view));
1886   GtkWidget *menu_item = NULL;
1887
1888   switch (direction)
1889     {
1890     case GTK_TEXT_DIR_LTR:
1891       menu_item = gtk_item_factory_get_widget (view->item_factory, "/Settings/Left-to-Right");
1892       break;
1893     case GTK_TEXT_DIR_RTL:
1894       menu_item = gtk_item_factory_get_widget (view->item_factory, "/Settings/Right-to-Left");
1895       break;
1896     default:
1897       break;
1898     }
1899
1900   if (menu_item)
1901     gtk_menu_item_activate (GTK_MENU_ITEM (menu_item));
1902
1903   switch (wrap_mode)
1904     {
1905     case GTK_WRAP_NONE:
1906       menu_item = gtk_item_factory_get_widget (view->item_factory, "/Settings/Wrap Off");
1907       break;
1908     case GTK_WRAP_WORD:
1909       menu_item = gtk_item_factory_get_widget (view->item_factory, "/Settings/Wrap Words");
1910       break;
1911     case GTK_WRAP_CHAR:
1912       menu_item = gtk_item_factory_get_widget (view->item_factory, "/Settings/Wrap Chars");
1913       break;
1914     default:
1915       break;
1916     }
1917   
1918   if (menu_item)
1919     gtk_menu_item_activate (GTK_MENU_ITEM (menu_item));
1920 }
1921
1922 static GtkItemFactoryEntry menu_items[] =
1923 {
1924   { "/_File",            NULL,         NULL,        0, "<Branch>" },
1925   { "/File/_New",        "<control>N", do_new,      0, NULL },
1926   { "/File/New _View",   NULL,         do_new_view, 0, NULL },
1927   { "/File/_Open",       "<control>O", do_open,     0, NULL },
1928   { "/File/_Save",       "<control>S", do_save,     0, NULL },
1929   { "/File/Save _As...", NULL,         do_save_as,  0, NULL },
1930   { "/File/sep1",        NULL,         NULL,        0, "<Separator>" },
1931   { "/File/_Close",     "<control>W" , do_close,    0, NULL },
1932   { "/File/E_xit",      "<control>Q" , do_exit,     0, NULL },
1933
1934   { "/_Edit", NULL, 0, 0, "<Branch>" },
1935   { "/Edit/Copy", NULL, do_copy, 0, NULL },
1936   { "/Edit/sep1", NULL, NULL, 0, "<Separator>" },
1937   { "/Edit/Find...", NULL, do_search, 0, NULL },
1938   { "/Edit/Select All", "<control>A", do_select_all, 0, NULL }, 
1939
1940   { "/_Settings",         NULL,         NULL,             0, "<Branch>" },
1941   { "/Settings/Wrap _Off",   NULL,      do_wrap_changed,  GTK_WRAP_NONE, "<RadioItem>" },
1942   { "/Settings/Wrap _Words", NULL,      do_wrap_changed,  GTK_WRAP_WORD, "/Settings/Wrap Off" },
1943   { "/Settings/Wrap _Chars", NULL,      do_wrap_changed,  GTK_WRAP_CHAR, "/Settings/Wrap Off" },
1944   { "/Settings/sep1",        NULL,      NULL,             0, "<Separator>" },
1945   { "/Settings/Editable", NULL,      do_editable_changed,  TRUE, "<RadioItem>" },
1946   { "/Settings/Not editable",    NULL,      do_editable_changed,  FALSE, "/Settings/Editable" },
1947   { "/Settings/sep1",        NULL,      NULL,             0, "<Separator>" },
1948
1949   { "/Settings/Cursor visible",    NULL,      do_cursor_visible_changed,  TRUE, "<RadioItem>" },
1950   { "/Settings/Cursor not visible", NULL,      do_cursor_visible_changed,  FALSE, "/Settings/Cursor visible" },
1951   { "/Settings/sep1",        NULL,      NULL,          0, "<Separator>" },
1952   
1953   { "/Settings/Left-to-Right", NULL,    do_direction_changed,  GTK_TEXT_DIR_LTR, "<RadioItem>" },
1954   { "/Settings/Right-to-Left", NULL,    do_direction_changed,  GTK_TEXT_DIR_RTL, "/Settings/Left-to-Right" },
1955
1956   { "/Settings/sep1",        NULL,      NULL,                0, "<Separator>" },
1957   { "/Settings/Sane spacing", NULL,    do_spacing_changed,  FALSE, "<RadioItem>" },
1958   { "/Settings/Funky spacing", NULL,    do_spacing_changed,  TRUE, "/Settings/Sane spacing" },
1959   { "/Settings/sep1",        NULL,      NULL,                0, "<Separator>" },
1960   { "/Settings/Don't cycle color tags", NULL,    do_color_cycle_changed,  FALSE, "<RadioItem>" },
1961   { "/Settings/Cycle colors", NULL,    do_color_cycle_changed,  TRUE, "/Settings/Don't cycle color tags" },
1962   { "/_Attributes",       NULL,         NULL,                0, "<Branch>" },
1963   { "/Attributes/Editable",       NULL,         do_apply_editable, TRUE, NULL },
1964   { "/Attributes/Not editable",           NULL,         do_apply_editable, FALSE, NULL },
1965   { "/Attributes/Invisible",      NULL,         do_apply_invisible, FALSE, NULL },
1966   { "/Attributes/Visible",        NULL,         do_apply_invisible, TRUE, NULL },
1967   { "/Attributes/Rise",           NULL,         do_apply_rise, FALSE, NULL },
1968   { "/Attributes/Large",          NULL,         do_apply_large, FALSE, NULL },
1969   { "/Attributes/Indent",         NULL,         do_apply_indent, FALSE, NULL },
1970   { "/Attributes/Margins",        NULL,         do_apply_margin, FALSE, NULL },
1971   { "/Attributes/Custom tabs",            NULL,         do_apply_tabs, FALSE, NULL },
1972   { "/Attributes/Default tabs",           NULL,         do_apply_tabs, TRUE, NULL },
1973   { "/Attributes/Color cycles",           NULL,         do_apply_colors, TRUE, NULL },
1974   { "/Attributes/No colors",              NULL,         do_apply_colors, FALSE, NULL },
1975   { "/Attributes/Remove all tags",        NULL, do_remove_tags, 0, NULL },
1976   { "/Attributes/Properties",             NULL, do_properties, 0, NULL },
1977   { "/Attributes/Rich Text copy & drag",  NULL, do_rich_text, 0, NULL },
1978   { "/Attributes/Rich Text paste & drop", NULL, do_rich_text, 1, NULL },
1979   { "/_Test",            NULL,         NULL,           0, "<Branch>" },
1980   { "/Test/_Example",    NULL,         do_example,  0, NULL },
1981   { "/Test/_Insert and scroll", NULL,         do_insert_and_scroll,  0, NULL },
1982   { "/Test/_Add fixed children", NULL,         do_add_children,  0, NULL },
1983   { "/Test/A_dd focusable children", NULL,    do_add_focus_children,  0, NULL },
1984 };
1985
1986 static gboolean
1987 save_buffer (Buffer *buffer)
1988 {
1989   GtkTextIter start, end;
1990   gchar *chars;
1991   gboolean result = FALSE;
1992   gboolean have_backup = FALSE;
1993   gchar *bak_filename;
1994   FILE *file;
1995
1996   g_return_val_if_fail (buffer->filename != NULL, FALSE);
1997
1998   bak_filename = g_strconcat (buffer->filename, "~", NULL);
1999   
2000   if (rename (buffer->filename, bak_filename) != 0)
2001     {
2002       if (errno != ENOENT)
2003         {
2004           gchar *err = g_strdup_printf ("Cannot back up '%s' to '%s': %s",
2005                                         buffer->filename, bak_filename, g_strerror (errno));
2006           msgbox_run (NULL, err, "OK", NULL, NULL, 0);
2007           g_free (err);
2008           return FALSE;
2009         }
2010     }
2011   else
2012     have_backup = TRUE;
2013   
2014   file = fopen (buffer->filename, "w");
2015   if (!file)
2016     {
2017       gchar *err = g_strdup_printf ("Cannot back up '%s' to '%s': %s",
2018                                     buffer->filename, bak_filename, g_strerror (errno));
2019       msgbox_run (NULL, err, "OK", NULL, NULL, 0);
2020     }
2021   else
2022     {
2023       gtk_text_buffer_get_iter_at_offset (buffer->buffer, &start, 0);
2024       gtk_text_buffer_get_end_iter (buffer->buffer, &end);
2025   
2026       chars = gtk_text_buffer_get_slice (buffer->buffer, &start, &end, FALSE);
2027
2028       if (fputs (chars, file) == EOF ||
2029           fclose (file) == EOF)
2030         {
2031           gchar *err = g_strdup_printf ("Error writing to '%s': %s",
2032                                         buffer->filename, g_strerror (errno));
2033           msgbox_run (NULL, err, "OK", NULL, NULL, 0);
2034           g_free (err);
2035         }
2036       else
2037         {
2038           /* Success
2039            */
2040           result = TRUE;
2041           gtk_text_buffer_set_modified (buffer->buffer, FALSE);   
2042         }
2043         
2044       g_free (chars);
2045     }
2046
2047   if (!result && have_backup)
2048     {
2049       if (rename (bak_filename, buffer->filename) != 0)
2050         {
2051           gchar *err = g_strdup_printf ("Error restoring backup file '%s' to '%s': %s\nBackup left as '%s'",
2052                                         buffer->filename, bak_filename, g_strerror (errno), bak_filename);
2053           msgbox_run (NULL, err, "OK", NULL, NULL, 0);
2054           g_free (err);
2055         }
2056     }
2057
2058   g_free (bak_filename);
2059   
2060   return result;
2061 }
2062
2063 static gboolean
2064 save_as_ok_func (const char *filename, gpointer data)
2065 {
2066   Buffer *buffer = data;
2067   char *old_filename = buffer->filename;
2068
2069   if (!buffer->filename || strcmp (filename, buffer->filename) != 0)
2070     {
2071       struct stat statbuf;
2072
2073       if (stat (filename, &statbuf) == 0)
2074         {
2075           gchar *err = g_strdup_printf ("Ovewrite existing file '%s'?", filename);
2076           gint result = msgbox_run (NULL, err, "Yes", "No", NULL, 1);
2077           g_free (err);
2078
2079           if (result != 0)
2080             return FALSE;
2081         }
2082     }
2083   
2084   buffer->filename = g_strdup (filename);
2085
2086   if (save_buffer (buffer))
2087     {
2088       g_free (old_filename);
2089       buffer_filename_set (buffer);
2090       return TRUE;
2091     }
2092   else
2093     {
2094       g_free (buffer->filename);
2095       buffer->filename = old_filename;
2096       return FALSE;
2097     }
2098 }
2099
2100 static gboolean
2101 save_as_buffer (Buffer *buffer)
2102 {
2103   return filesel_run (NULL, "Save File", NULL, save_as_ok_func, buffer);
2104 }
2105
2106 static gboolean
2107 check_buffer_saved (Buffer *buffer)
2108 {
2109   if (gtk_text_buffer_get_modified (buffer->buffer))
2110     {
2111       char *pretty_name = buffer_pretty_name (buffer);
2112       char *msg = g_strdup_printf ("Save changes to '%s'?", pretty_name);
2113       gint result;
2114       
2115       g_free (pretty_name);
2116       
2117       result = msgbox_run (NULL, msg, "Yes", "No", "Cancel", 0);
2118       g_free (msg);
2119   
2120       if (result == 0)
2121         return save_as_buffer (buffer);
2122       else if (result == 1)
2123         return TRUE;
2124       else
2125         return FALSE;
2126     }
2127   else
2128     return TRUE;
2129 }
2130
2131 #define N_COLORS 16
2132
2133 static Buffer *
2134 create_buffer (void)
2135 {
2136   Buffer *buffer;
2137   PangoTabArray *tabs;
2138   gint i;
2139   
2140   buffer = g_new (Buffer, 1);
2141
2142   buffer->buffer = gtk_text_buffer_new (NULL);
2143   
2144   buffer->refcount = 1;
2145   buffer->filename = NULL;
2146   buffer->untitled_serial = -1;
2147
2148   buffer->color_tags = NULL;
2149   buffer->color_cycle_timeout = 0;
2150   buffer->start_hue = 0.0;
2151   
2152   i = 0;
2153   while (i < N_COLORS)
2154     {
2155       GtkTextTag *tag;
2156
2157       tag = gtk_text_buffer_create_tag (buffer->buffer, NULL, NULL);
2158       
2159       buffer->color_tags = g_slist_prepend (buffer->color_tags, tag);
2160       
2161       ++i;
2162     }
2163
2164 #if 1  
2165   buffer->invisible_tag = gtk_text_buffer_create_tag (buffer->buffer, NULL,
2166                                                       "invisible", TRUE, NULL);
2167 #endif  
2168   
2169   buffer->not_editable_tag =
2170     gtk_text_buffer_create_tag (buffer->buffer, NULL,
2171                                 "editable", FALSE,
2172                                 "foreground", "purple", NULL);
2173
2174   buffer->found_text_tag = gtk_text_buffer_create_tag (buffer->buffer, NULL,
2175                                                        "foreground", "red", NULL);
2176
2177   buffer->rise_tag = gtk_text_buffer_create_tag (buffer->buffer, NULL,
2178                                                  "rise", 10 * PANGO_SCALE, NULL);
2179
2180   buffer->large_tag = gtk_text_buffer_create_tag (buffer->buffer, NULL,
2181                                                  "scale", PANGO_SCALE_X_LARGE, NULL);
2182
2183   buffer->indent_tag = gtk_text_buffer_create_tag (buffer->buffer, NULL,
2184                                                    "indent", 20, NULL);
2185
2186   buffer->margin_tag = gtk_text_buffer_create_tag (buffer->buffer, NULL,
2187                                                    "left_margin", 20, "right_margin", 20, NULL);
2188
2189   tabs = pango_tab_array_new_with_positions (4,
2190                                              TRUE,
2191                                              PANGO_TAB_LEFT, 10,
2192                                              PANGO_TAB_LEFT, 30,
2193                                              PANGO_TAB_LEFT, 60,
2194                                              PANGO_TAB_LEFT, 120);
2195   
2196   buffer->custom_tabs_tag = gtk_text_buffer_create_tag (buffer->buffer, NULL,
2197                                                         "tabs", tabs,
2198                                                         "foreground", "green", NULL);
2199
2200   pango_tab_array_free (tabs);
2201   
2202   buffers = g_slist_prepend (buffers, buffer);
2203   
2204   return buffer;
2205 }
2206
2207 static char *
2208 buffer_pretty_name (Buffer *buffer)
2209 {
2210   if (buffer->filename)
2211     {
2212       char *p;
2213       char *result = g_path_get_basename (buffer->filename);
2214       p = strchr (result, '/');
2215       if (p)
2216         *p = '\0';
2217
2218       return result;
2219     }
2220   else
2221     {
2222       if (buffer->untitled_serial == -1)
2223         buffer->untitled_serial = untitled_serial++;
2224
2225       if (buffer->untitled_serial == 1)
2226         return g_strdup ("Untitled");
2227       else
2228         return g_strdup_printf ("Untitled #%d", buffer->untitled_serial);
2229     }
2230 }
2231
2232 static void
2233 buffer_filename_set (Buffer *buffer)
2234 {
2235   GSList *tmp_list = views;
2236
2237   while (tmp_list)
2238     {
2239       View *view = tmp_list->data;
2240
2241       if (view->buffer == buffer)
2242         view_set_title (view);
2243
2244       tmp_list = tmp_list->next;
2245     }
2246 }
2247
2248 static void
2249 buffer_search (Buffer     *buffer,
2250                const char *str,
2251                View       *view,
2252                gboolean forward)
2253 {
2254   GtkTextIter iter;
2255   GtkTextIter start, end;
2256   GtkWidget *dialog;
2257   int i;
2258   
2259   /* remove tag from whole buffer */
2260   gtk_text_buffer_get_bounds (buffer->buffer, &start, &end);
2261   gtk_text_buffer_remove_tag (buffer->buffer,  buffer->found_text_tag,
2262                               &start, &end );
2263   
2264   gtk_text_buffer_get_iter_at_mark (buffer->buffer, &iter,
2265                                     gtk_text_buffer_get_mark (buffer->buffer,
2266                                                               "insert"));
2267
2268   i = 0;
2269   if (*str != '\0')
2270     {
2271       GtkTextIter match_start, match_end;
2272
2273       if (forward)
2274         {
2275           while (gtk_text_iter_forward_search (&iter, str,
2276                                                GTK_TEXT_SEARCH_VISIBLE_ONLY |
2277                                                GTK_TEXT_SEARCH_TEXT_ONLY,
2278                                                &match_start, &match_end,
2279                                                NULL))
2280             {
2281               ++i;
2282               gtk_text_buffer_apply_tag (buffer->buffer, buffer->found_text_tag,
2283                                          &match_start, &match_end);
2284               
2285               iter = match_end;
2286             }
2287         }
2288       else
2289         {
2290           while (gtk_text_iter_backward_search (&iter, str,
2291                                                 GTK_TEXT_SEARCH_VISIBLE_ONLY |
2292                                                 GTK_TEXT_SEARCH_TEXT_ONLY,
2293                                                 &match_start, &match_end,
2294                                                 NULL))
2295             {
2296               ++i;
2297               gtk_text_buffer_apply_tag (buffer->buffer, buffer->found_text_tag,
2298                                          &match_start, &match_end);
2299               
2300               iter = match_start;
2301             }
2302         }
2303     }
2304
2305   dialog = gtk_message_dialog_new (GTK_WINDOW (view->window),
2306                                    GTK_DIALOG_DESTROY_WITH_PARENT,
2307                                    GTK_MESSAGE_INFO,
2308                                    GTK_BUTTONS_OK,
2309                                    "%d strings found and marked in red",
2310                                    i);
2311
2312   g_signal_connect_swapped (dialog,
2313                             "response",
2314                             G_CALLBACK (gtk_widget_destroy), dialog);
2315   
2316   gtk_widget_show (dialog);
2317 }
2318
2319 static void
2320 buffer_search_forward (Buffer *buffer, const char *str,
2321                        View *view)
2322 {
2323   buffer_search (buffer, str, view, TRUE);
2324 }
2325
2326 static void
2327 buffer_search_backward (Buffer *buffer, const char *str,
2328                         View *view)
2329 {
2330   buffer_search (buffer, str, view, FALSE);
2331 }
2332
2333 static void
2334 buffer_ref (Buffer *buffer)
2335 {
2336   buffer->refcount++;
2337 }
2338
2339 static void
2340 buffer_unref (Buffer *buffer)
2341 {
2342   buffer->refcount--;
2343   if (buffer->refcount == 0)
2344     {
2345       buffer_set_colors (buffer, FALSE);
2346       buffers = g_slist_remove (buffers, buffer);
2347       g_object_unref (buffer->buffer);
2348       g_free (buffer->filename);
2349       g_free (buffer);
2350     }
2351 }
2352
2353 static void
2354 hsv_to_rgb (gdouble *h,
2355             gdouble *s,
2356             gdouble *v)
2357 {
2358   gdouble hue, saturation, value;
2359   gdouble f, p, q, t;
2360
2361   if (*s == 0.0)
2362     {
2363       *h = *v;
2364       *s = *v;
2365       *v = *v; /* heh */
2366     }
2367   else
2368     {
2369       hue = *h * 6.0;
2370       saturation = *s;
2371       value = *v;
2372       
2373       if (hue >= 6.0)
2374         hue = 0.0;
2375       
2376       f = hue - (int) hue;
2377       p = value * (1.0 - saturation);
2378       q = value * (1.0 - saturation * f);
2379       t = value * (1.0 - saturation * (1.0 - f));
2380       
2381       switch ((int) hue)
2382         {
2383         case 0:
2384           *h = value;
2385           *s = t;
2386           *v = p;
2387           break;
2388           
2389         case 1:
2390           *h = q;
2391           *s = value;
2392           *v = p;
2393           break;
2394           
2395         case 2:
2396           *h = p;
2397           *s = value;
2398           *v = t;
2399           break;
2400           
2401         case 3:
2402           *h = p;
2403           *s = q;
2404           *v = value;
2405           break;
2406           
2407         case 4:
2408           *h = t;
2409           *s = p;
2410           *v = value;
2411           break;
2412           
2413         case 5:
2414           *h = value;
2415           *s = p;
2416           *v = q;
2417           break;
2418           
2419         default:
2420           g_assert_not_reached ();
2421         }
2422     }
2423 }
2424
2425 static void
2426 hue_to_color (gdouble   hue,
2427               GdkColor *color)
2428 {
2429   gdouble h, s, v;
2430
2431   h = hue;
2432   s = 1.0;
2433   v = 1.0;
2434
2435   g_return_if_fail (hue <= 1.0);
2436   
2437   hsv_to_rgb (&h, &s, &v);
2438
2439   color->red = h * 65535;
2440   color->green = s * 65535;
2441   color->blue = v * 65535;
2442 }
2443
2444
2445 static gint
2446 color_cycle_timeout (gpointer data)
2447 {
2448   Buffer *buffer = data;
2449
2450   buffer_cycle_colors (buffer);
2451
2452   return TRUE;
2453 }
2454
2455 static void
2456 buffer_set_colors (Buffer  *buffer,
2457                    gboolean enabled)
2458 {
2459   GSList *tmp;
2460   gdouble hue = 0.0;
2461
2462   if (enabled && buffer->color_cycle_timeout == 0)
2463     buffer->color_cycle_timeout = g_timeout_add (200, color_cycle_timeout, buffer);
2464   else if (!enabled && buffer->color_cycle_timeout != 0)
2465     {
2466       g_source_remove (buffer->color_cycle_timeout);
2467       buffer->color_cycle_timeout = 0;
2468     }
2469     
2470   tmp = buffer->color_tags;
2471   while (tmp != NULL)
2472     {
2473       if (enabled)
2474         {
2475           GdkColor color;
2476           
2477           hue_to_color (hue, &color);
2478
2479           g_object_set (tmp->data,
2480                         "foreground_gdk", &color,
2481                         NULL);
2482         }
2483       else
2484         g_object_set (tmp->data,
2485                       "foreground_set", FALSE,
2486                       NULL);
2487
2488       hue += 1.0 / N_COLORS;
2489       
2490       tmp = g_slist_next (tmp);
2491     }
2492 }
2493
2494 static void
2495 buffer_cycle_colors (Buffer *buffer)
2496 {
2497   GSList *tmp;
2498   gdouble hue = buffer->start_hue;
2499   
2500   tmp = buffer->color_tags;
2501   while (tmp != NULL)
2502     {
2503       GdkColor color;
2504       
2505       hue_to_color (hue, &color);
2506       
2507       g_object_set (tmp->data,
2508                     "foreground_gdk", &color,
2509                     NULL);
2510
2511       hue += 1.0 / N_COLORS;
2512       if (hue > 1.0)
2513         hue = 0.0;
2514       
2515       tmp = g_slist_next (tmp);
2516     }
2517
2518   buffer->start_hue += 1.0 / N_COLORS;
2519   if (buffer->start_hue > 1.0)
2520     buffer->start_hue = 0.0;
2521 }
2522
2523 static void
2524 close_view (View *view)
2525 {
2526   views = g_slist_remove (views, view);
2527   buffer_unref (view->buffer);
2528   gtk_widget_destroy (view->window);
2529   g_object_unref (view->item_factory);
2530   
2531   g_free (view);
2532   
2533   if (!views)
2534     gtk_main_quit ();
2535 }
2536
2537 static void
2538 check_close_view (View *view)
2539 {
2540   if (view->buffer->refcount > 1 ||
2541       check_buffer_saved (view->buffer))
2542     close_view (view);
2543 }
2544
2545 static void
2546 view_set_title (View *view)
2547 {
2548   char *pretty_name = buffer_pretty_name (view->buffer);
2549   char *title = g_strconcat ("testtext - ", pretty_name, NULL);
2550
2551   gtk_window_set_title (GTK_WINDOW (view->window), title);
2552
2553   g_free (pretty_name);
2554   g_free (title);
2555 }
2556
2557 static void
2558 cursor_set_callback (GtkTextBuffer     *buffer,
2559                      const GtkTextIter *location,
2560                      GtkTextMark       *mark,
2561                      gpointer           user_data)
2562 {
2563   GtkTextView *text_view;
2564
2565   /* Redraw tab windows if the cursor moves
2566    * on the mapped widget (windows may not exist before realization...
2567    */
2568   
2569   text_view = GTK_TEXT_VIEW (user_data);
2570   
2571   if (GTK_WIDGET_MAPPED (text_view) &&
2572       mark == gtk_text_buffer_get_insert (buffer))
2573     {
2574       GdkWindow *tab_window;
2575
2576       tab_window = gtk_text_view_get_window (text_view,
2577                                              GTK_TEXT_WINDOW_TOP);
2578
2579       gdk_window_invalidate_rect (tab_window, NULL, FALSE);
2580       
2581       tab_window = gtk_text_view_get_window (text_view,
2582                                              GTK_TEXT_WINDOW_BOTTOM);
2583
2584       gdk_window_invalidate_rect (tab_window, NULL, FALSE);
2585     }
2586 }
2587
2588 static gint
2589 tab_stops_expose (GtkWidget      *widget,
2590                   GdkEventExpose *event,
2591                   gpointer        user_data)
2592 {
2593   gint first_x;
2594   gint last_x;
2595   gint i;
2596   GdkWindow *top_win;
2597   GdkWindow *bottom_win;
2598   GtkTextView *text_view;
2599   GtkTextWindowType type;
2600   GdkDrawable *target;
2601   gint *positions = NULL;
2602   gint size;
2603   GtkTextAttributes *attrs;
2604   GtkTextIter insert;
2605   GtkTextBuffer *buffer;
2606   gboolean in_pixels;
2607   
2608   text_view = GTK_TEXT_VIEW (widget);
2609   
2610   /* See if this expose is on the tab stop window */
2611   top_win = gtk_text_view_get_window (text_view,
2612                                       GTK_TEXT_WINDOW_TOP);
2613
2614   bottom_win = gtk_text_view_get_window (text_view,
2615                                          GTK_TEXT_WINDOW_BOTTOM);
2616
2617   if (event->window == top_win)
2618     {
2619       type = GTK_TEXT_WINDOW_TOP;
2620       target = top_win;
2621     }
2622   else if (event->window == bottom_win)
2623     {
2624       type = GTK_TEXT_WINDOW_BOTTOM;
2625       target = bottom_win;
2626     }
2627   else
2628     return FALSE;
2629   
2630   first_x = event->area.x;
2631   last_x = first_x + event->area.width;
2632
2633   gtk_text_view_window_to_buffer_coords (text_view,
2634                                          type,
2635                                          first_x,
2636                                          0,
2637                                          &first_x,
2638                                          NULL);
2639
2640   gtk_text_view_window_to_buffer_coords (text_view,
2641                                          type,
2642                                          last_x,
2643                                          0,
2644                                          &last_x,
2645                                          NULL);
2646
2647   buffer = gtk_text_view_get_buffer (text_view);
2648
2649   gtk_text_buffer_get_iter_at_mark (buffer,
2650                                     &insert,
2651                                     gtk_text_buffer_get_mark (buffer,
2652                                                               "insert"));
2653   
2654   attrs = gtk_text_attributes_new ();
2655
2656   gtk_text_iter_get_attributes (&insert, attrs);
2657
2658   if (attrs->tabs)
2659     {
2660       size = pango_tab_array_get_size (attrs->tabs);
2661       
2662       pango_tab_array_get_tabs (attrs->tabs,
2663                                 NULL,
2664                                 &positions);
2665
2666       in_pixels = pango_tab_array_get_positions_in_pixels (attrs->tabs);
2667     }
2668   else
2669     {
2670       size = 0;
2671       in_pixels = FALSE;
2672     }
2673       
2674   gtk_text_attributes_unref (attrs);
2675   
2676   i = 0;
2677   while (i < size)
2678     {
2679       gint pos;
2680
2681       if (!in_pixels)
2682         positions[i] = PANGO_PIXELS (positions[i]);
2683       
2684       gtk_text_view_buffer_to_window_coords (text_view,
2685                                              type,
2686                                              positions[i],
2687                                              0,
2688                                              &pos,
2689                                              NULL);
2690       
2691       gdk_draw_line (target, 
2692                      widget->style->fg_gc [widget->state],
2693                      pos, 0,
2694                      pos, 15); 
2695       
2696       ++i;
2697     }
2698
2699   g_free (positions);
2700
2701   return TRUE;
2702 }
2703
2704 static void
2705 get_lines (GtkTextView  *text_view,
2706            gint          first_y,
2707            gint          last_y,
2708            GArray       *buffer_coords,
2709            GArray       *numbers,
2710            gint         *countp)
2711 {
2712   GtkTextIter iter;
2713   gint count;
2714   gint size;  
2715
2716   g_array_set_size (buffer_coords, 0);
2717   g_array_set_size (numbers, 0);
2718   
2719   /* Get iter at first y */
2720   gtk_text_view_get_line_at_y (text_view, &iter, first_y, NULL);
2721
2722   /* For each iter, get its location and add it to the arrays.
2723    * Stop when we pass last_y
2724    */
2725   count = 0;
2726   size = 0;
2727
2728   while (!gtk_text_iter_is_end (&iter))
2729     {
2730       gint y, height;
2731       gint line_num;
2732       
2733       gtk_text_view_get_line_yrange (text_view, &iter, &y, &height);
2734
2735       g_array_append_val (buffer_coords, y);
2736       line_num = gtk_text_iter_get_line (&iter);
2737       g_array_append_val (numbers, line_num);
2738       
2739       ++count;
2740
2741       if ((y + height) >= last_y)
2742         break;
2743       
2744       gtk_text_iter_forward_line (&iter);
2745     }
2746
2747   *countp = count;
2748 }
2749
2750 static gint
2751 line_numbers_expose (GtkWidget      *widget,
2752                      GdkEventExpose *event,
2753                      gpointer        user_data)
2754 {
2755   gint count;
2756   GArray *numbers;
2757   GArray *pixels;
2758   gint first_y;
2759   gint last_y;
2760   gint i;
2761   GdkWindow *left_win;
2762   GdkWindow *right_win;
2763   PangoLayout *layout;
2764   GtkTextView *text_view;
2765   GtkTextWindowType type;
2766   GdkDrawable *target;
2767   
2768   text_view = GTK_TEXT_VIEW (widget);
2769   
2770   /* See if this expose is on the line numbers window */
2771   left_win = gtk_text_view_get_window (text_view,
2772                                        GTK_TEXT_WINDOW_LEFT);
2773
2774   right_win = gtk_text_view_get_window (text_view,
2775                                         GTK_TEXT_WINDOW_RIGHT);
2776
2777   if (event->window == left_win)
2778     {
2779       type = GTK_TEXT_WINDOW_LEFT;
2780       target = left_win;
2781     }
2782   else if (event->window == right_win)
2783     {
2784       type = GTK_TEXT_WINDOW_RIGHT;
2785       target = right_win;
2786     }
2787   else
2788     return FALSE;
2789   
2790   first_y = event->area.y;
2791   last_y = first_y + event->area.height;
2792
2793   gtk_text_view_window_to_buffer_coords (text_view,
2794                                          type,
2795                                          0,
2796                                          first_y,
2797                                          NULL,
2798                                          &first_y);
2799
2800   gtk_text_view_window_to_buffer_coords (text_view,
2801                                          type,
2802                                          0,
2803                                          last_y,
2804                                          NULL,
2805                                          &last_y);
2806
2807   numbers = g_array_new (FALSE, FALSE, sizeof (gint));
2808   pixels = g_array_new (FALSE, FALSE, sizeof (gint));
2809   
2810   get_lines (text_view,
2811              first_y,
2812              last_y,
2813              pixels,
2814              numbers,
2815              &count);
2816   
2817   /* Draw fully internationalized numbers! */
2818   
2819   layout = gtk_widget_create_pango_layout (widget, "");
2820   
2821   i = 0;
2822   while (i < count)
2823     {
2824       gint pos;
2825       gchar *str;
2826       
2827       gtk_text_view_buffer_to_window_coords (text_view,
2828                                              type,
2829                                              0,
2830                                              g_array_index (pixels, gint, i),
2831                                              NULL,
2832                                              &pos);
2833
2834       str = g_strdup_printf ("%d", g_array_index (numbers, gint, i));
2835
2836       pango_layout_set_text (layout, str, -1);
2837
2838       gtk_paint_layout (widget->style,
2839                         target,
2840                         GTK_WIDGET_STATE (widget),
2841                         FALSE,
2842                         NULL,
2843                         widget,
2844                         NULL,
2845                         2, pos + 2,
2846                         layout);
2847
2848       g_free (str);
2849       
2850       ++i;
2851     }
2852
2853   g_array_free (pixels, TRUE);
2854   g_array_free (numbers, TRUE);
2855   
2856   g_object_unref (layout);
2857
2858   /* don't stop emission, need to draw children */
2859   return FALSE;
2860 }
2861
2862 static void
2863 selection_changed (GtkTextBuffer *buffer,
2864                    GParamSpec    *pspec,
2865                    GtkWidget     *copy_menu)
2866 {
2867   gtk_widget_set_sensitive (copy_menu, gtk_text_buffer_get_has_selection (buffer));
2868 }
2869
2870 static View *
2871 create_view (Buffer *buffer)
2872 {
2873   View *view;
2874   GtkWidget *copy_menu;
2875   GtkWidget *sw;
2876   GtkWidget *vbox;
2877   
2878   view = g_new0 (View, 1);
2879   views = g_slist_prepend (views, view);
2880
2881   view->buffer = buffer;
2882   buffer_ref (buffer);
2883   
2884   view->window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2885   g_object_set_data (G_OBJECT (view->window), "view", view);
2886   
2887   g_signal_connect (view->window, "delete_event",
2888                     G_CALLBACK (delete_event_cb), NULL);
2889
2890   view->accel_group = gtk_accel_group_new ();
2891   view->item_factory = gtk_item_factory_new (GTK_TYPE_MENU_BAR, "<main>", view->accel_group);
2892   g_object_set_data (G_OBJECT (view->item_factory), "view", view);
2893   
2894   gtk_item_factory_create_items (view->item_factory, G_N_ELEMENTS (menu_items), menu_items, view);
2895
2896   /* make the Copy menu item sensitivity update according to the selection */
2897   copy_menu = gtk_item_factory_get_item (view->item_factory, "<main>/Edit/Copy");
2898   gtk_widget_set_sensitive (copy_menu, gtk_text_buffer_get_has_selection (view->buffer->buffer));
2899   g_signal_connect (view->buffer->buffer,
2900                     "notify::has-selection",
2901                     G_CALLBACK (selection_changed),
2902                     copy_menu);
2903
2904   gtk_window_add_accel_group (GTK_WINDOW (view->window), view->accel_group);
2905
2906   vbox = gtk_vbox_new (FALSE, 0);
2907   gtk_container_add (GTK_CONTAINER (view->window), vbox);
2908
2909   gtk_box_pack_start (GTK_BOX (vbox),
2910                       gtk_item_factory_get_widget (view->item_factory, "<main>"),
2911                       FALSE, FALSE, 0);
2912   
2913   sw = gtk_scrolled_window_new (NULL, NULL);
2914   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
2915                                  GTK_POLICY_AUTOMATIC,
2916                                  GTK_POLICY_AUTOMATIC);
2917
2918   view->text_view = gtk_text_view_new_with_buffer (buffer->buffer);
2919   gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (view->text_view),
2920                                GTK_WRAP_WORD);
2921
2922   /* Make sure border width works, no real reason to do this other than testing */
2923   gtk_container_set_border_width (GTK_CONTAINER (view->text_view),
2924                                   10);
2925   
2926   /* Draw tab stops in the top and bottom windows. */
2927   
2928   gtk_text_view_set_border_window_size (GTK_TEXT_VIEW (view->text_view),
2929                                         GTK_TEXT_WINDOW_TOP,
2930                                         15);
2931
2932   gtk_text_view_set_border_window_size (GTK_TEXT_VIEW (view->text_view),
2933                                         GTK_TEXT_WINDOW_BOTTOM,
2934                                         15);
2935
2936   g_signal_connect (view->text_view,
2937                     "expose_event",
2938                     G_CALLBACK (tab_stops_expose),
2939                     NULL);  
2940
2941   g_signal_connect (view->buffer->buffer,
2942                     "mark_set",
2943                     G_CALLBACK (cursor_set_callback),
2944                     view->text_view);
2945   
2946   /* Draw line numbers in the side windows; we should really be
2947    * more scientific about what width we set them to.
2948    */
2949   gtk_text_view_set_border_window_size (GTK_TEXT_VIEW (view->text_view),
2950                                         GTK_TEXT_WINDOW_RIGHT,
2951                                         30);
2952   
2953   gtk_text_view_set_border_window_size (GTK_TEXT_VIEW (view->text_view),
2954                                         GTK_TEXT_WINDOW_LEFT,
2955                                         30);
2956   
2957   g_signal_connect (view->text_view,
2958                     "expose_event",
2959                     G_CALLBACK (line_numbers_expose),
2960                     NULL);
2961   
2962   gtk_box_pack_start (GTK_BOX (vbox), sw, TRUE, TRUE, 0);
2963   gtk_container_add (GTK_CONTAINER (sw), view->text_view);
2964
2965   gtk_window_set_default_size (GTK_WINDOW (view->window), 500, 500);
2966
2967   gtk_widget_grab_focus (view->text_view);
2968
2969   view_set_title (view);
2970   view_init_menus (view);
2971
2972   view_add_example_widgets (view);
2973   
2974   gtk_widget_show_all (view->window);
2975   return view;
2976 }
2977
2978 static void
2979 view_add_example_widgets (View *view)
2980 {
2981   GtkTextChildAnchor *anchor;
2982   Buffer *buffer;
2983
2984   buffer = view->buffer;
2985   
2986   anchor = g_object_get_data (G_OBJECT (buffer->buffer),
2987                               "anchor");
2988
2989   if (anchor && !gtk_text_child_anchor_get_deleted (anchor))
2990     {
2991       GtkWidget *widget;
2992
2993       widget = gtk_button_new_with_label ("Foo");
2994       
2995       gtk_text_view_add_child_at_anchor (GTK_TEXT_VIEW (view->text_view),
2996                                          widget,
2997                                          anchor);
2998
2999       gtk_widget_show (widget);
3000     }
3001 }
3002
3003 void
3004 test_init (void)
3005 {
3006   if (g_file_test ("../gdk-pixbuf/libpixbufloader-pnm.la",
3007                    G_FILE_TEST_EXISTS))
3008     {
3009       g_setenv ("GDK_PIXBUF_MODULE_FILE", "../gdk-pixbuf/gdk-pixbuf.loaders", TRUE);
3010       g_setenv ("GTK_IM_MODULE_FILE", "../modules/input/gtk.immodules", TRUE);
3011     }
3012 }
3013
3014 int
3015 main (int argc, char** argv)
3016 {
3017   Buffer *buffer;
3018   View *view;
3019   int i;
3020
3021   test_init ();
3022   gtk_init (&argc, &argv);
3023   
3024   buffer = create_buffer ();
3025   view = create_view (buffer);
3026   buffer_unref (buffer);
3027   
3028   push_active_window (GTK_WINDOW (view->window));
3029   for (i=1; i < argc; i++)
3030     {
3031       char *filename;
3032
3033       /* Quick and dirty canonicalization - better should be in GLib
3034        */
3035
3036       if (!g_path_is_absolute (argv[i]))
3037         {
3038           char *cwd = g_get_current_dir ();
3039           filename = g_strconcat (cwd, "/", argv[i], NULL);
3040           g_free (cwd);
3041         }
3042       else
3043         filename = argv[i];
3044
3045       open_ok_func (filename, view);
3046
3047       if (filename != argv[i])
3048         g_free (filename);
3049     }
3050   pop_active_window ();
3051   
3052   gtk_main ();
3053
3054   return 0;
3055 }
3056
3057