]> Pileus Git - ~andy/gtk/blob - tests/testtext.c
convert the X coordinates so they're with respect to the line, rather than
[~andy/gtk] / tests / testtext.c
1 #include <stdio.h>
2 #include <sys/stat.h>
3 #include <errno.h>
4 #include <stdlib.h>
5 #include <string.h>
6
7 #include <gtk/gtk.h>
8 #include <gdk/gdkkeysyms.h>
9
10 typedef struct _Buffer Buffer;
11 typedef struct _View View;
12
13 static gint untitled_serial = 1;
14
15 GSList *active_window_stack = NULL;
16
17 struct _Buffer
18 {
19   gint refcount;
20   GtkTextBuffer *buffer;
21   char *filename;
22   gint untitled_serial;
23   GtkTextTag *invisible_tag;
24   GtkTextTag *not_editable_tag;
25   GtkTextTag *found_text_tag;
26   GtkTextTag *custom_tabs_tag;
27   GSList *color_tags;
28   guint color_cycle_timeout;
29   gdouble start_hue;
30 };
31
32 struct _View
33 {
34   GtkWidget *window;
35   GtkWidget *text_view;
36   GtkAccelGroup *accel_group;
37   GtkItemFactory *item_factory;
38   Buffer *buffer;
39 };
40
41 static void push_active_window (GtkWindow *window);
42 static void pop_active_window (void);
43 static GtkWindow *get_active_window (void);
44
45 static Buffer * create_buffer      (void);
46 static gboolean check_buffer_saved (Buffer *buffer);
47 static gboolean save_buffer        (Buffer *buffer);
48 static gboolean save_as_buffer     (Buffer *buffer);
49 static char *   buffer_pretty_name (Buffer *buffer);
50 static void     buffer_filename_set (Buffer *buffer);
51 static void     buffer_search_forward (Buffer *buffer,
52                                        const char *str,
53                                        View *view);
54 static void     buffer_search_backward (Buffer *buffer,
55                                        const char *str,
56                                        View *view);
57 static void     buffer_set_colors      (Buffer  *buffer,
58                                         gboolean enabled);
59 static void     buffer_cycle_colors    (Buffer  *buffer);
60
61 static View *view_from_widget (GtkWidget *widget);
62
63 static View *create_view      (Buffer *buffer);
64 static void  check_close_view (View   *view);
65 static void  close_view       (View   *view);
66 static void  view_set_title   (View   *view);
67 static void  view_init_menus  (View   *view);
68 static void  view_add_example_widgets (View *view);
69
70 GSList *buffers = NULL;
71 GSList *views = NULL;
72
73 static void
74 push_active_window (GtkWindow *window)
75 {
76   g_object_ref (G_OBJECT (window));
77   active_window_stack = g_slist_prepend (active_window_stack, window);
78 }
79
80 static void
81 pop_active_window (void)
82 {
83   gtk_object_unref (active_window_stack->data);
84   active_window_stack = g_slist_delete_link (active_window_stack, active_window_stack);
85 }
86
87 static GtkWindow *
88 get_active_window (void)
89 {
90   if (active_window_stack)
91     return active_window_stack->data;
92   else
93     return NULL;
94 }
95
96 /*
97  * Filesel utility function
98  */
99
100 typedef gboolean (*FileselOKFunc) (const char *filename, gpointer data);
101
102 static void
103 filesel_ok_cb (GtkWidget *button, GtkWidget *filesel)
104 {
105   FileselOKFunc ok_func = g_object_get_data (G_OBJECT (filesel), "ok-func");
106   gpointer data = g_object_get_data (G_OBJECT (filesel), "ok-data");
107   gint *result = g_object_get_data (G_OBJECT (filesel), "ok-result");
108   
109   gtk_widget_hide (filesel);
110   
111   if ((*ok_func) (gtk_file_selection_get_filename (GTK_FILE_SELECTION (filesel)), data))
112     {
113       gtk_widget_destroy (filesel);
114       *result = TRUE;
115     }
116   else
117     gtk_widget_show (filesel);
118 }
119
120 gboolean
121 filesel_run (GtkWindow    *parent, 
122              const char   *title,
123              const char   *start_file,
124              FileselOKFunc func,
125              gpointer      data)
126 {
127   GtkWidget *filesel = gtk_file_selection_new (title);
128   gboolean result = FALSE;
129
130   if (!parent)
131     parent = get_active_window ();
132   
133   if (parent)
134     gtk_window_set_transient_for (GTK_WINDOW (filesel), parent);
135
136   if (start_file)
137     gtk_file_selection_set_filename (GTK_FILE_SELECTION (filesel), start_file);
138
139   
140   g_object_set_data (G_OBJECT (filesel), "ok-func", func);
141   g_object_set_data (G_OBJECT (filesel), "ok-data", data);
142   g_object_set_data (G_OBJECT (filesel), "ok-result", &result);
143
144   gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION (filesel)->ok_button),
145                       "clicked",
146                       GTK_SIGNAL_FUNC (filesel_ok_cb), filesel);
147   gtk_signal_connect_object (GTK_OBJECT (GTK_FILE_SELECTION (filesel)->cancel_button),
148                              "clicked",
149                              GTK_SIGNAL_FUNC (gtk_widget_destroy), GTK_OBJECT (filesel));
150
151   gtk_signal_connect (GTK_OBJECT (filesel), "destroy",
152                       GTK_SIGNAL_FUNC (gtk_main_quit), NULL);
153   gtk_window_set_modal (GTK_WINDOW (filesel), TRUE);
154
155   gtk_widget_show (filesel);
156   gtk_main ();
157
158   return result;
159 }
160
161 /*
162  * MsgBox utility functions
163  */
164
165 static void
166 msgbox_yes_cb (GtkWidget *widget, gboolean *result)
167 {
168   *result = 0;
169   gtk_object_destroy (GTK_OBJECT (gtk_widget_get_toplevel (widget)));
170 }
171
172 static void
173 msgbox_no_cb (GtkWidget *widget, gboolean *result)
174 {
175   *result = 1;
176   gtk_object_destroy (GTK_OBJECT (gtk_widget_get_toplevel (widget)));
177 }
178
179 static gboolean
180 msgbox_key_press_cb (GtkWidget *widget, GdkEventKey *event, gpointer data)
181 {
182   if (event->keyval == GDK_Escape)
183     {
184       gtk_signal_emit_stop_by_name (GTK_OBJECT (widget), "key_press_event");
185       gtk_object_destroy (GTK_OBJECT (widget));
186       return TRUE;
187     }
188
189   return FALSE;
190 }
191
192 gint
193 msgbox_run (GtkWindow  *parent,
194             const char *message,
195             const char *yes_button,
196             const char *no_button,
197             const char *cancel_button,
198             gint default_index)
199 {
200   gboolean result = -1;
201   GtkWidget *dialog;
202   GtkWidget *button;
203   GtkWidget *label;
204   GtkWidget *vbox;
205   GtkWidget *button_box;
206   GtkWidget *separator;
207
208   g_return_val_if_fail (message != NULL, FALSE);
209   g_return_val_if_fail (default_index >= 0 && default_index <= 1, FALSE);
210
211   if (!parent)
212     parent = get_active_window ();
213   
214   /* Create a dialog
215    */
216   dialog = gtk_window_new (GTK_WINDOW_TOPLEVEL);
217   gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
218   if (parent)
219     gtk_window_set_transient_for (GTK_WINDOW (dialog), parent);
220   gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
221
222   /* Quit our recursive main loop when the dialog is destroyed.
223    */
224   gtk_signal_connect (GTK_OBJECT (dialog), "destroy",
225                       GTK_SIGNAL_FUNC (gtk_main_quit), NULL);
226
227   /* Catch Escape key presses and have them destroy the dialog
228    */
229   gtk_signal_connect (GTK_OBJECT (dialog), "key_press_event",
230                       GTK_SIGNAL_FUNC (msgbox_key_press_cb), NULL);
231
232   /* Fill in the contents of the widget
233    */
234   vbox = gtk_vbox_new (FALSE, 0);
235   gtk_container_add (GTK_CONTAINER (dialog), vbox);
236   
237   label = gtk_label_new (message);
238   gtk_misc_set_padding (GTK_MISC (label), 12, 12);
239   gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
240   gtk_box_pack_start (GTK_BOX (vbox), label, TRUE, TRUE, 0);
241
242   separator = gtk_hseparator_new ();
243   gtk_box_pack_start (GTK_BOX (vbox), separator, FALSE, FALSE, 0);
244
245   button_box = gtk_hbutton_box_new ();
246   gtk_box_pack_start (GTK_BOX (vbox), button_box, FALSE, FALSE, 0);
247   gtk_container_set_border_width (GTK_CONTAINER (button_box), 8);
248   
249
250   /* When Yes is clicked, call the msgbox_yes_cb
251    * This sets the result variable and destroys the dialog
252    */
253   if (yes_button)
254     {
255       button = gtk_button_new_with_label (yes_button);
256       GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
257       gtk_container_add (GTK_CONTAINER (button_box), button);
258
259       if (default_index == 0)
260         gtk_widget_grab_default (button);
261       
262       gtk_signal_connect (GTK_OBJECT (button), "clicked",
263                           GTK_SIGNAL_FUNC (msgbox_yes_cb), &result);
264     }
265
266   /* When No is clicked, call the msgbox_no_cb
267    * This sets the result variable and destroys the dialog
268    */
269   if (no_button)
270     {
271       button = gtk_button_new_with_label (no_button);
272       GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
273       gtk_container_add (GTK_CONTAINER (button_box), button);
274
275       if (default_index == 0)
276         gtk_widget_grab_default (button);
277       
278       gtk_signal_connect (GTK_OBJECT (button), "clicked",
279                           GTK_SIGNAL_FUNC (msgbox_no_cb), &result);
280     }
281
282   /* When Cancel is clicked, destroy the dialog
283    */
284   if (cancel_button)
285     {
286       button = gtk_button_new_with_label (cancel_button);
287       GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
288       gtk_container_add (GTK_CONTAINER (button_box), button);
289       
290       if (default_index == 1)
291         gtk_widget_grab_default (button);
292       
293       gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
294                                  GTK_SIGNAL_FUNC (gtk_object_destroy), GTK_OBJECT (dialog));
295     }
296
297   gtk_widget_show_all (dialog);
298
299   /* Run a recursive main loop until a button is clicked
300    * or the user destroys the dialog through the window mananger */
301   gtk_main ();
302
303   return result;
304 }
305
306 /*
307  * Example buffer filling code
308  */
309 static gint
310 blink_timeout (gpointer data)
311 {
312   GtkTextTag *tag;
313   static gboolean flip = FALSE;
314   
315   tag = GTK_TEXT_TAG (data);
316
317   g_object_set (G_OBJECT (tag),
318                  "foreground", flip ? "blue" : "purple",
319                  NULL);
320
321   flip = !flip;
322
323   return TRUE;
324 }
325
326 static gint
327 tag_event_handler (GtkTextTag *tag, GtkWidget *widget, GdkEvent *event,
328                   const GtkTextIter *iter, gpointer user_data)
329 {
330   gint char_index;
331
332   char_index = gtk_text_iter_get_offset (iter);
333   
334   switch (event->type)
335     {
336     case GDK_MOTION_NOTIFY:
337       printf ("Motion event at char %d tag `%s'\n",
338              char_index, tag->name);
339       break;
340         
341     case GDK_BUTTON_PRESS:
342       printf ("Button press at char %d tag `%s'\n",
343              char_index, tag->name);
344       break;
345         
346     case GDK_2BUTTON_PRESS:
347       printf ("Double click at char %d tag `%s'\n",
348              char_index, tag->name);
349       break;
350         
351     case GDK_3BUTTON_PRESS:
352       printf ("Triple click at char %d tag `%s'\n",
353              char_index, tag->name);
354       break;
355         
356     case GDK_BUTTON_RELEASE:
357       printf ("Button release at char %d tag `%s'\n",
358              char_index, tag->name);
359       break;
360         
361     case GDK_KEY_PRESS:
362     case GDK_KEY_RELEASE:
363       printf ("Key event at char %d tag `%s'\n",
364               char_index, tag->name);
365       break;
366       
367     case GDK_ENTER_NOTIFY:
368     case GDK_LEAVE_NOTIFY:
369     case GDK_PROPERTY_NOTIFY:
370     case GDK_SELECTION_CLEAR:
371     case GDK_SELECTION_REQUEST:
372     case GDK_SELECTION_NOTIFY:
373     case GDK_PROXIMITY_IN:
374     case GDK_PROXIMITY_OUT:
375     case GDK_DRAG_ENTER:
376     case GDK_DRAG_LEAVE:
377     case GDK_DRAG_MOTION:
378     case GDK_DRAG_STATUS:
379     case GDK_DROP_START:
380     case GDK_DROP_FINISHED:
381     default:
382       break;
383     }
384
385   return FALSE;
386 }
387
388 static void
389 setup_tag (GtkTextTag *tag)
390 {
391   g_signal_connect_data (G_OBJECT (tag),
392                          "event",
393                          tag_event_handler,
394                          NULL, NULL, FALSE, FALSE);
395 }
396
397 static char  *book_closed_xpm[] = {
398 "16 16 6 1",
399 "       c None s None",
400 ".      c black",
401 "X      c red",
402 "o      c yellow",
403 "O      c #808080",
404 "#      c white",
405 "                ",
406 "       ..       ",
407 "     ..XX.      ",
408 "   ..XXXXX.     ",
409 " ..XXXXXXXX.    ",
410 ".ooXXXXXXXXX.   ",
411 "..ooXXXXXXXXX.  ",
412 ".X.ooXXXXXXXXX. ",
413 ".XX.ooXXXXXX..  ",
414 " .XX.ooXXX..#O  ",
415 "  .XX.oo..##OO. ",
416 "   .XX..##OO..  ",
417 "    .X.#OO..    ",
418 "     ..O..      ",
419 "      ..        ",
420 "                "};
421
422 void
423 fill_example_buffer (GtkTextBuffer *buffer)
424 {
425   GtkTextIter iter, iter2;
426   GtkTextTag *tag;
427   GdkColor color;
428   GdkColor color2;
429   GdkPixbuf *pixbuf;
430   int i;
431   char *str;
432   GtkTextChildAnchor *anchor;
433   
434   /* FIXME this is broken if called twice on a buffer, since
435    * we try to create tags a second time.
436    */
437   
438   tag = gtk_text_buffer_create_tag (buffer, "fg_blue");
439
440   /*       gtk_timeout_add (1000, blink_timeout, tag); */
441       
442   setup_tag (tag);
443   
444   color.red = color.green = 0;
445   color.blue = 0xffff;
446   color2.red = 0xfff;
447   color2.blue = 0x0;
448   color2.green = 0;
449   g_object_set (G_OBJECT (tag),
450                  "foreground_gdk", &color,
451                  "background_gdk", &color2,
452                   "size_points", 24.0,
453                  NULL);
454
455   tag = gtk_text_buffer_create_tag (buffer, "fg_red");
456
457   setup_tag (tag);
458       
459   color.blue = color.green = 0;
460   color.red = 0xffff;
461   g_object_set (G_OBJECT (tag),
462                  "rise", -4 * PANGO_SCALE,
463                  "foreground_gdk", &color,
464                  NULL);
465
466   tag = gtk_text_buffer_create_tag (buffer, "bg_green");
467
468   setup_tag (tag);
469       
470   color.blue = color.red = 0;
471   color.green = 0xffff;
472   g_object_set (G_OBJECT (tag),
473                 "background_gdk", &color,
474                 "size_points", 10.0,
475                 NULL);
476
477   tag = gtk_text_buffer_create_tag (buffer, "strikethrough");
478
479   setup_tag (tag);
480       
481   g_object_set (G_OBJECT (tag),
482                 "strikethrough", TRUE,
483                 NULL);
484
485
486   tag = gtk_text_buffer_create_tag (buffer, "underline");
487
488   setup_tag (tag);
489       
490   g_object_set (G_OBJECT (tag),
491                 "underline", PANGO_UNDERLINE_SINGLE,
492                 NULL);
493
494   setup_tag (tag);
495       
496   g_object_set (G_OBJECT (tag),
497                 "underline", PANGO_UNDERLINE_SINGLE,
498                 NULL);
499
500   tag = gtk_text_buffer_create_tag (buffer, "centered");
501       
502   g_object_set (G_OBJECT (tag),
503                 "justification", GTK_JUSTIFY_CENTER,
504                 NULL);
505
506   tag = gtk_text_buffer_create_tag (buffer, "rtl_quote");
507       
508   g_object_set (G_OBJECT (tag),
509                 "wrap_mode", GTK_WRAP_WORD,
510                 "direction", GTK_TEXT_DIR_RTL,
511                 "indent", 30,
512                 "left_margin", 20,
513                   "right_margin", 20,
514                 NULL);
515
516
517 #if 0
518   gtk_text_buffer_get_iter_at_offset (buffer, &iter, 0);
519
520   anchor = gtk_text_buffer_create_child_anchor (buffer, &iter);
521
522   g_object_ref (G_OBJECT (anchor));
523   
524   g_object_set_data_full (G_OBJECT (buffer), "anchor", anchor,
525                           (GDestroyNotify) g_object_unref);
526 #endif
527   
528   pixbuf = gdk_pixbuf_new_from_xpm_data (book_closed_xpm);
529   
530   i = 0;
531   while (i < 100)
532     {
533       GtkTextMark * temp_mark;
534       
535       gtk_text_buffer_get_iter_at_offset (buffer, &iter, 0);
536           
537       gtk_text_buffer_insert_pixbuf (buffer, &iter, pixbuf);
538           
539       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",
540                             i);
541       
542       gtk_text_buffer_insert (buffer, &iter, str, -1);
543
544       g_free (str);
545       
546       gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, 0, 5);
547           
548       gtk_text_buffer_insert (buffer, &iter,
549                              "(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"
550                              /* This is UTF8 stuff, Emacs doesn't
551                                 really know how to display it */
552                              "German (Deutsch Süd) Grüß Gott Greek (Ελληνικά) Γειά σας Hebrew   שלום Japanese (日本語)\n", -1);
553
554       temp_mark =
555         gtk_text_buffer_create_mark (buffer, "tmp_mark", &iter, TRUE);
556
557 #if 1
558       gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, 0, 6);
559       gtk_text_buffer_get_iter_at_line_offset (buffer, &iter2, 0, 13);
560
561       gtk_text_buffer_apply_tag_by_name (buffer, "fg_blue", &iter, &iter2);
562
563       gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, 1, 10);
564       gtk_text_buffer_get_iter_at_line_offset (buffer, &iter2, 1, 16);
565
566       gtk_text_buffer_apply_tag_by_name (buffer, "underline", &iter, &iter2);
567
568       gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, 1, 14);
569       gtk_text_buffer_get_iter_at_line_offset (buffer, &iter2, 1, 24);
570
571       gtk_text_buffer_apply_tag_by_name (buffer, "strikethrough", &iter, &iter2);
572           
573       gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, 0, 9);
574       gtk_text_buffer_get_iter_at_line_offset (buffer, &iter2, 0, 16);
575
576       gtk_text_buffer_apply_tag_by_name (buffer, "bg_green", &iter, &iter2);
577   
578       gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, 4, 2);
579       gtk_text_buffer_get_iter_at_line_offset (buffer, &iter2, 4, 10);
580
581       gtk_text_buffer_apply_tag_by_name (buffer, "bg_green", &iter, &iter2);
582
583       gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, 4, 8);
584       gtk_text_buffer_get_iter_at_line_offset (buffer, &iter2, 4, 15);
585
586       gtk_text_buffer_apply_tag_by_name (buffer, "fg_red", &iter, &iter2);
587 #endif
588
589       gtk_text_buffer_get_iter_at_mark (buffer, &iter, temp_mark);
590       gtk_text_buffer_insert (buffer, &iter, "Centered text!\n", -1);
591           
592       gtk_text_buffer_get_iter_at_mark (buffer, &iter2, temp_mark);
593       gtk_text_buffer_apply_tag_by_name (buffer, "centered", &iter2, &iter);
594
595       gtk_text_buffer_move_mark (buffer, temp_mark, &iter);
596       gtk_text_buffer_insert (buffer, &iter, "Word wrapped, Right-to-left Quote\n", -1);
597       gtk_text_buffer_insert (buffer, &iter, "وقد بدأ ثلاث من أكثر المؤسسات تقدما في شبكة اكسيون برامجها كمنظمات لا تسعى للربح، ثم تحولت في السنوات الخمس الماضية إلى مؤسسات مالية منظمة، وباتت جزءا من النظام المالي في بلدانها، ولكنها تتخصص في خدمة قطاع المشروعات الصغيرة. وأحد أكثر هذه المؤسسات نجاحا هو »بانكوسول« في بوليفيا.\n", -1);
598       gtk_text_buffer_get_iter_at_mark (buffer, &iter2, temp_mark);
599       gtk_text_buffer_apply_tag_by_name (buffer, "rtl_quote", &iter2, &iter);
600           
601       ++i;
602     }
603
604   g_object_unref (G_OBJECT (pixbuf));
605   
606   printf ("%d lines %d chars\n",
607           gtk_text_buffer_get_line_count (buffer),
608           gtk_text_buffer_get_char_count (buffer));
609
610   /* Move cursor to start */
611   gtk_text_buffer_get_iter_at_offset (buffer, &iter, 0);
612   gtk_text_buffer_place_cursor (buffer, &iter);
613   
614   gtk_text_buffer_set_modified (buffer, FALSE);
615 }
616
617 gboolean
618 fill_file_buffer (GtkTextBuffer *buffer, const char *filename)
619 {
620   FILE* f;
621   gchar buf[2048];
622   gint remaining = 0;
623   GtkTextIter iter, end;
624
625   f = fopen (filename, "r");
626   
627   if (f == NULL)
628     {
629       gchar *err = g_strdup_printf ("Cannot open file '%s': %s",
630                                     filename, g_strerror (errno));
631       msgbox_run (NULL, err, "OK", NULL, NULL, 0);
632       g_free (err);
633       return FALSE;
634     }
635   
636   gtk_text_buffer_get_iter_at_offset (buffer, &iter, 0);
637   while (!feof (f))
638     {
639       gint count;
640       const char *leftover;
641       int to_read = 2047  - remaining;
642
643       count = fread (buf + remaining, 1, to_read, f);
644       buf[count + remaining] = '\0';
645
646       g_utf8_validate (buf, count + remaining, &leftover);
647       
648       g_assert (g_utf8_validate (buf, leftover - buf, NULL));
649       gtk_text_buffer_insert (buffer, &iter, buf, leftover - buf);
650
651       remaining = (buf + remaining + count) - leftover;
652       g_memmove (buf, leftover, remaining);
653
654       if (remaining > 6 || count < to_read)
655           break;
656     }
657
658   if (remaining)
659     {
660       gchar *err = g_strdup_printf ("Invalid UTF-8 data encountered reading file '%s'", filename);
661       msgbox_run (NULL, err, "OK", NULL, NULL, 0);
662       g_free (err);
663     }
664   
665   /* We had a newline in the buffer to begin with. (The buffer always contains
666    * a newline, so we delete to the end of the buffer to clean up.
667    */
668   gtk_text_buffer_get_end_iter (buffer, &end);
669   gtk_text_buffer_delete (buffer, &iter, &end);
670   
671   gtk_text_buffer_set_modified (buffer, FALSE);
672
673   return TRUE;
674 }
675
676 static gint
677 delete_event_cb (GtkWidget *window, GdkEventAny *event, gpointer data)
678 {
679   View *view = view_from_widget (window);
680
681   push_active_window (GTK_WINDOW (window));
682   check_close_view (view);
683   pop_active_window ();
684
685   return TRUE;
686 }
687
688 /*
689  * Menu callbacks
690  */
691
692 static View *
693 get_empty_view (View *view)
694 {
695   if (!view->buffer->filename &&
696       !gtk_text_buffer_modified (view->buffer->buffer))
697     return view;
698   else
699     return create_view (create_buffer ());
700 }
701
702 static View *
703 view_from_widget (GtkWidget *widget)
704 {
705   GtkWidget *app;
706
707   if (GTK_IS_MENU_ITEM (widget))
708     {
709       GtkItemFactory *item_factory = gtk_item_factory_from_widget (widget);
710       return g_object_get_data (G_OBJECT (item_factory), "view");      
711     }
712   else
713     {
714       GtkWidget *app = gtk_widget_get_toplevel (widget);
715       return g_object_get_data (G_OBJECT (app), "view");
716     }
717 }
718
719 static void
720 do_new (gpointer             callback_data,
721         guint                callback_action,
722         GtkWidget           *widget)
723 {
724   create_view (create_buffer ());
725 }
726
727 static void
728 do_new_view (gpointer             callback_data,
729              guint                callback_action,
730              GtkWidget           *widget)
731 {
732   View *view = view_from_widget (widget);
733   
734   create_view (view->buffer);
735 }
736
737 gboolean
738 open_ok_func (const char *filename, gpointer data)
739 {
740   View *view = data;
741   View *new_view = get_empty_view (view);
742
743   if (!fill_file_buffer (new_view->buffer->buffer, filename))
744     {
745       if (new_view != view)
746         close_view (new_view);
747       return FALSE;
748     }
749   else
750     {
751       g_free (new_view->buffer->filename);
752       new_view->buffer->filename = g_strdup (filename);
753       buffer_filename_set (new_view->buffer);
754       
755       return TRUE;
756     }
757 }
758
759 static void
760 do_open (gpointer             callback_data,
761          guint                callback_action,
762          GtkWidget           *widget)
763 {
764   View *view = view_from_widget (widget);
765
766   push_active_window (GTK_WINDOW (view->window));
767   filesel_run (NULL, "Open File", NULL, open_ok_func, view);
768   pop_active_window ();
769 }
770
771 static void
772 do_save_as (gpointer             callback_data,
773             guint                callback_action,
774             GtkWidget           *widget)
775 {
776   View *view = view_from_widget (widget);  
777
778   push_active_window (GTK_WINDOW (view->window));
779   save_as_buffer (view->buffer);
780   pop_active_window ();
781 }
782
783 static void
784 do_save (gpointer             callback_data,
785          guint                callback_action,
786          GtkWidget           *widget)
787 {
788   View *view = view_from_widget (widget);
789
790   push_active_window (GTK_WINDOW (view->window));
791   if (!view->buffer->filename)
792     do_save_as (callback_data, callback_action, widget);
793   else
794     save_buffer (view->buffer);
795   pop_active_window ();
796 }
797
798 static void
799 do_close   (gpointer             callback_data,
800             guint                callback_action,
801             GtkWidget           *widget)
802 {
803   View *view = view_from_widget (widget);
804
805   push_active_window (GTK_WINDOW (view->window));
806   check_close_view (view);
807   pop_active_window ();
808 }
809
810 static void
811 do_exit    (gpointer             callback_data,
812             guint                callback_action,
813             GtkWidget           *widget)
814 {
815   View *view = view_from_widget (widget);
816
817   GSList *tmp_list = buffers;
818
819   push_active_window (GTK_WINDOW (view->window));
820   while (tmp_list)
821     {
822       if (!check_buffer_saved (tmp_list->data))
823         return;
824
825       tmp_list = tmp_list->next;
826     }
827
828   gtk_main_quit ();
829   pop_active_window ();
830 }
831
832 static void
833 do_example (gpointer             callback_data,
834             guint                callback_action,
835             GtkWidget           *widget)
836 {
837   View *view = view_from_widget (widget);
838   View *new_view;
839
840   new_view = get_empty_view (view);
841   
842   fill_example_buffer (new_view->buffer->buffer);
843
844   view_add_example_widgets (new_view);
845 }
846
847 static void
848 do_wrap_changed (gpointer             callback_data,
849                  guint                callback_action,
850                  GtkWidget           *widget)
851 {
852   View *view = view_from_widget (widget);
853
854   gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (view->text_view), callback_action);
855 }
856
857 static void
858 do_direction_changed (gpointer             callback_data,
859                       guint                callback_action,
860                       GtkWidget           *widget)
861 {
862   View *view = view_from_widget (widget);
863   
864   gtk_widget_set_direction (view->text_view, callback_action);
865   gtk_widget_queue_resize (view->text_view);
866 }
867
868
869 static void
870 do_spacing_changed (gpointer             callback_data,
871                     guint                callback_action,
872                     GtkWidget           *widget)
873 {
874   View *view = view_from_widget (widget);
875
876   if (callback_action)
877     {
878       gtk_text_view_set_pixels_above_lines (GTK_TEXT_VIEW (view->text_view),
879                                             23);
880       gtk_text_view_set_pixels_below_lines (GTK_TEXT_VIEW (view->text_view),
881                                             21);
882       gtk_text_view_set_pixels_inside_wrap (GTK_TEXT_VIEW (view->text_view),
883                                             9);
884     }
885   else
886     {
887       gtk_text_view_set_pixels_above_lines (GTK_TEXT_VIEW (view->text_view),
888                                             0);
889       gtk_text_view_set_pixels_below_lines (GTK_TEXT_VIEW (view->text_view),
890                                             0);
891       gtk_text_view_set_pixels_inside_wrap (GTK_TEXT_VIEW (view->text_view),
892                                             0);
893     }
894 }
895
896 static void
897 do_editable_changed (gpointer callback_data,
898                      guint callback_action,
899                      GtkWidget *widget)
900 {
901   View *view = view_from_widget (widget);
902
903   gtk_text_view_set_editable (GTK_TEXT_VIEW (view->text_view), callback_action);
904 }
905
906 static void
907 do_cursor_visible_changed (gpointer callback_data,
908                            guint callback_action,
909                            GtkWidget *widget)
910 {
911   View *view = view_from_widget (widget);
912
913   gtk_text_view_set_cursor_visible (GTK_TEXT_VIEW (view->text_view), callback_action);
914 }
915
916 static void
917 do_color_cycle_changed (gpointer callback_data,
918                         guint callback_action,
919                         GtkWidget *widget)
920 {
921   View *view = view_from_widget (widget);
922
923   buffer_set_colors (view->buffer, callback_action);
924 }
925
926 static void
927 do_apply_editable (gpointer callback_data,
928                    guint callback_action,
929                    GtkWidget *widget)
930 {
931   View *view = view_from_widget (widget);
932   GtkTextIter start;
933   GtkTextIter end;
934   
935   if (gtk_text_buffer_get_selection_bounds (view->buffer->buffer,
936                                             &start, &end))
937     {
938       if (callback_action)
939         {
940           gtk_text_buffer_remove_tag (view->buffer->buffer,
941                                       view->buffer->not_editable_tag,
942                                       &start, &end);
943         }
944       else
945         {
946           gtk_text_buffer_apply_tag (view->buffer->buffer,
947                                      view->buffer->not_editable_tag,
948                                      &start, &end);
949         }
950     }
951 }
952
953 static void
954 do_apply_invisible (gpointer callback_data,
955                     guint callback_action,
956                     GtkWidget *widget)
957 {
958   View *view = view_from_widget (widget);
959   GtkTextIter start;
960   GtkTextIter end;
961   
962   if (gtk_text_buffer_get_selection_bounds (view->buffer->buffer,
963                                             &start, &end))
964     {
965       if (callback_action)
966         {
967           gtk_text_buffer_remove_tag (view->buffer->buffer,
968                                       view->buffer->invisible_tag,
969                                       &start, &end);
970         }
971       else
972         {
973           gtk_text_buffer_apply_tag (view->buffer->buffer,
974                                      view->buffer->invisible_tag,
975                                      &start, &end);
976         }
977     }
978 }
979
980 static void
981 do_apply_tabs (gpointer callback_data,
982                guint callback_action,
983                GtkWidget *widget)
984 {
985   View *view = view_from_widget (widget);
986   GtkTextIter start;
987   GtkTextIter end;
988   
989   if (gtk_text_buffer_get_selection_bounds (view->buffer->buffer,
990                                             &start, &end))
991     {
992       if (callback_action)
993         {
994           gtk_text_buffer_remove_tag (view->buffer->buffer,
995                                       view->buffer->custom_tabs_tag,
996                                       &start, &end);
997         }
998       else
999         {
1000           gtk_text_buffer_apply_tag (view->buffer->buffer,
1001                                      view->buffer->custom_tabs_tag,
1002                                      &start, &end);
1003         }
1004     }
1005 }
1006
1007 static void
1008 do_apply_colors (gpointer callback_data,
1009                  guint callback_action,
1010                  GtkWidget *widget)
1011 {
1012   View *view = view_from_widget (widget);
1013   Buffer *buffer = view->buffer;
1014   GtkTextIter start;
1015   GtkTextIter end;
1016   
1017   if (gtk_text_buffer_get_selection_bounds (view->buffer->buffer,
1018                                             &start, &end))
1019     {
1020       if (!callback_action)
1021         {
1022           GSList *tmp;
1023           
1024           tmp = buffer->color_tags;
1025           while (tmp != NULL)
1026             {
1027               gtk_text_buffer_remove_tag (view->buffer->buffer,
1028                                           tmp->data,
1029                                           &start, &end);              
1030               tmp = g_slist_next (tmp);
1031             }
1032         }
1033       else
1034         {
1035           GSList *tmp;
1036           
1037           tmp = buffer->color_tags;
1038           while (TRUE)
1039             {
1040               GtkTextIter next;
1041               gboolean done = FALSE;
1042               
1043               next = start;
1044               gtk_text_iter_forward_char (&next);
1045               gtk_text_iter_forward_char (&next);
1046
1047               if (gtk_text_iter_compare (&next, &end) > 0)
1048                 {
1049                   next = end;
1050                   done = TRUE;
1051                 }
1052               
1053               gtk_text_buffer_apply_tag (view->buffer->buffer,
1054                                          tmp->data,
1055                                          &start, &next);
1056
1057               start = next;
1058
1059               if (done)
1060                 return;
1061               
1062               tmp = g_slist_next (tmp);
1063               if (tmp == NULL)
1064                 tmp = buffer->color_tags;
1065             } 
1066         }
1067     }
1068 }
1069
1070 enum
1071 {
1072   RESPONSE_FORWARD,
1073   RESPONSE_BACKWARD
1074 };
1075
1076 static void
1077 dialog_response_callback (GtkWidget *dialog, gint response_id, gpointer data)
1078 {
1079   GtkTextBuffer *buffer;
1080   View *view = data;
1081   GtkTextIter start, end;
1082   gchar *search_string;
1083
1084   if (response_id != RESPONSE_FORWARD &&
1085       response_id != RESPONSE_BACKWARD)
1086     {
1087       gtk_widget_destroy (dialog);
1088       return;
1089     }
1090   
1091   buffer = g_object_get_data (G_OBJECT (dialog), "buffer");
1092
1093   gtk_text_buffer_get_bounds (buffer, &start, &end);
1094
1095   /* Remove trailing newline */
1096   gtk_text_iter_backward_char (&end);
1097   
1098   search_string = gtk_text_iter_get_text (&start, &end);
1099
1100   printf ("Searching for `%s'\n", search_string);
1101
1102   if (response_id == RESPONSE_FORWARD)
1103     buffer_search_forward (view->buffer, search_string, view);
1104   else if (response_id == RESPONSE_BACKWARD)
1105     buffer_search_backward (view->buffer, search_string, view);
1106     
1107   g_free (search_string);
1108   
1109   gtk_widget_destroy (dialog);
1110 }
1111
1112 static void
1113 do_search (gpointer callback_data,
1114            guint callback_action,
1115            GtkWidget *widget)
1116 {
1117   View *view = view_from_widget (widget);
1118   GtkWidget *dialog;
1119   GtkWidget *search_text;
1120   GtkTextBuffer *buffer;
1121
1122   dialog = gtk_dialog_new_with_buttons ("Search",
1123                                         GTK_WINDOW (view->window),
1124                                         GTK_DIALOG_DESTROY_WITH_PARENT,
1125                                         "Forward", RESPONSE_FORWARD,
1126                                         "Backward", RESPONSE_BACKWARD,
1127                                         GTK_STOCK_BUTTON_CANCEL,
1128                                         GTK_RESPONSE_NONE, NULL);
1129
1130
1131   buffer = gtk_text_buffer_new (NULL);
1132
1133   search_text = gtk_text_view_new_with_buffer (buffer);
1134
1135   g_object_unref (G_OBJECT (buffer));
1136   
1137   gtk_box_pack_end (GTK_BOX (GTK_DIALOG (dialog)->vbox),
1138                     search_text,
1139                     TRUE, TRUE, 0);
1140
1141   g_object_set_data (G_OBJECT (dialog), "buffer", buffer);
1142   
1143   gtk_signal_connect (GTK_OBJECT (dialog),
1144                       "response",
1145                       GTK_SIGNAL_FUNC (dialog_response_callback),
1146                       view);
1147
1148   gtk_widget_show (search_text);
1149
1150   gtk_widget_grab_focus (search_text);
1151   
1152   gtk_widget_show_all (dialog);
1153 }
1154
1155 static void
1156 view_init_menus (View *view)
1157 {
1158   GtkTextDirection direction = gtk_widget_get_direction (view->text_view);
1159   GtkWrapMode wrap_mode = gtk_text_view_get_wrap_mode (GTK_TEXT_VIEW (view->text_view));
1160   GtkWidget *menu_item = NULL;
1161
1162   switch (direction)
1163     {
1164     case GTK_TEXT_DIR_LTR:
1165       menu_item = gtk_item_factory_get_widget (view->item_factory, "/Settings/Left-to-Right");
1166       break;
1167     case GTK_TEXT_DIR_RTL:
1168       menu_item = gtk_item_factory_get_widget (view->item_factory, "/Settings/Right-to-Left");
1169       break;
1170     default:
1171       break;
1172     }
1173
1174   if (menu_item)
1175     gtk_menu_item_activate (GTK_MENU_ITEM (menu_item));
1176
1177   switch (wrap_mode)
1178     {
1179     case GTK_WRAP_NONE:
1180       menu_item = gtk_item_factory_get_widget (view->item_factory, "/Settings/Wrap Off");
1181       break;
1182     case GTK_WRAP_WORD:
1183       menu_item = gtk_item_factory_get_widget (view->item_factory, "/Settings/Wrap Words");
1184       break;
1185     case GTK_WRAP_CHAR:
1186       menu_item = gtk_item_factory_get_widget (view->item_factory, "/Settings/Wrap Chars");
1187       break;
1188     default:
1189       break;
1190     }
1191   
1192   if (menu_item)
1193     gtk_menu_item_activate (GTK_MENU_ITEM (menu_item));
1194 }
1195
1196 static GtkItemFactoryEntry menu_items[] =
1197 {
1198   { "/_File",            NULL,         0,           0, "<Branch>" },
1199   { "/File/_New",        "<control>N", do_new,      0, NULL },
1200   { "/File/New _View",   NULL,         do_new_view, 0, NULL },
1201   { "/File/_Open",       "<control>O", do_open,     0, NULL },
1202   { "/File/_Save",       "<control>S", do_save,     0, NULL },
1203   { "/File/Save _As...", NULL,         do_save_as,  0, NULL },
1204   { "/File/sep1",        NULL,         0,           0, "<Separator>" },
1205   { "/File/_Close",     "<control>W" , do_close,    0, NULL },
1206   { "/File/E_xit",      "<control>Q" , do_exit,     0, NULL },
1207
1208   { "/_Edit", NULL, 0, 0, "<Branch>" },
1209   { "/Edit/Find...", NULL, do_search, 0, NULL },
1210
1211   { "/_Settings",         NULL,         0,                0, "<Branch>" },
1212   { "/Settings/Wrap _Off",   NULL,      do_wrap_changed,  GTK_WRAP_NONE, "<RadioItem>" },
1213   { "/Settings/Wrap _Words", NULL,      do_wrap_changed,  GTK_WRAP_WORD, "/Settings/Wrap Off" },
1214   { "/Settings/Wrap _Chars", NULL,      do_wrap_changed,  GTK_WRAP_CHAR, "/Settings/Wrap Off" },
1215   { "/Settings/sep1",        NULL,      0,                0, "<Separator>" },
1216   { "/Settings/Editable", NULL,      do_editable_changed,  TRUE, "<RadioItem>" },
1217   { "/Settings/Not editable",    NULL,      do_editable_changed,  FALSE, "/Settings/Editable" },
1218   { "/Settings/sep1",        NULL,      0,                0, "<Separator>" },
1219
1220   { "/Settings/Cursor visible",    NULL,      do_cursor_visible_changed,  TRUE, "<RadioItem>" },
1221   { "/Settings/Cursor not visible", NULL,      do_cursor_visible_changed,  FALSE, "/Settings/Cursor visible" },
1222   { "/Settings/sep1",        NULL,      0,                0, "<Separator>" },
1223   
1224   { "/Settings/Left-to-Right", NULL,    do_direction_changed,  GTK_TEXT_DIR_LTR, "<RadioItem>" },
1225   { "/Settings/Right-to-Left", NULL,    do_direction_changed,  GTK_TEXT_DIR_RTL, "/Settings/Left-to-Right" },
1226
1227   { "/Settings/sep1",        NULL,      0,                0, "<Separator>" },
1228   { "/Settings/Sane spacing", NULL,    do_spacing_changed,  FALSE, "<RadioItem>" },
1229   { "/Settings/Funky spacing", NULL,    do_spacing_changed,  TRUE, "/Settings/Sane spacing" },
1230   { "/Settings/sep1",        NULL,      0,                0, "<Separator>" },
1231   { "/Settings/Don't cycle color tags", NULL,    do_color_cycle_changed,  FALSE, "<RadioItem>" },
1232   { "/Settings/Cycle colors", NULL,    do_color_cycle_changed,  TRUE, "/Settings/Don't cycle color tags" },
1233   { "/_Attributes",       NULL,         0,                0, "<Branch>" },
1234   { "/Attributes/Editable",       NULL,         do_apply_editable, TRUE, NULL },
1235   { "/Attributes/Not editable",           NULL,         do_apply_editable, FALSE, NULL },
1236   { "/Attributes/Invisible",      NULL,         do_apply_invisible, FALSE, NULL },
1237   { "/Attributes/Visible",        NULL,         do_apply_invisible, TRUE, NULL },
1238   { "/Attributes/Custom tabs",            NULL,         do_apply_tabs, FALSE, NULL },
1239   { "/Attributes/Default tabs",           NULL,         do_apply_tabs, TRUE, NULL },
1240   { "/Attributes/Color cycles",           NULL,         do_apply_colors, TRUE, NULL },
1241   { "/Attributes/No colors",              NULL,         do_apply_colors, FALSE, NULL },
1242   { "/_Test",            NULL,         0,           0, "<Branch>" },
1243   { "/Test/_Example",    NULL,         do_example,  0, NULL },
1244 };
1245
1246 static gboolean
1247 save_buffer (Buffer *buffer)
1248 {
1249   GtkTextIter start, end;
1250   gchar *chars;
1251   gboolean result = FALSE;
1252   gboolean have_backup = FALSE;
1253   gchar *bak_filename;
1254   FILE *file;
1255
1256   g_return_val_if_fail (buffer->filename != NULL, FALSE);
1257
1258   bak_filename = g_strconcat (buffer->filename, "~", NULL);
1259   
1260   if (rename (buffer->filename, bak_filename) != 0)
1261     {
1262       if (errno != ENOENT)
1263         {
1264           gchar *err = g_strdup_printf ("Cannot back up '%s' to '%s': %s",
1265                                         buffer->filename, bak_filename, g_strerror (errno));
1266           msgbox_run (NULL, err, "OK", NULL, NULL, 0);
1267           g_free (err);
1268           return FALSE;
1269         }
1270     }
1271   else
1272     have_backup = TRUE;
1273   
1274   file = fopen (buffer->filename, "w");
1275   if (!file)
1276     {
1277       gchar *err = g_strdup_printf ("Cannot back up '%s' to '%s': %s",
1278                                     buffer->filename, bak_filename, g_strerror (errno));
1279       msgbox_run (NULL, err, "OK", NULL, NULL, 0);
1280     }
1281   else
1282     {
1283       gtk_text_buffer_get_iter_at_offset (buffer->buffer, &start, 0);
1284       gtk_text_buffer_get_end_iter (buffer->buffer, &end);
1285   
1286       chars = gtk_text_buffer_get_slice (buffer->buffer, &start, &end, FALSE);
1287
1288       if (fputs (chars, file) == EOF ||
1289           fclose (file) == EOF)
1290         {
1291           gchar *err = g_strdup_printf ("Error writing to '%s': %s",
1292                                         buffer->filename, g_strerror (errno));
1293           msgbox_run (NULL, err, "OK", NULL, NULL, 0);
1294           g_free (err);
1295         }
1296       else
1297         {
1298           /* Success
1299            */
1300           result = TRUE;
1301           gtk_text_buffer_set_modified (buffer->buffer, FALSE);   
1302         }
1303         
1304       g_free (chars);
1305     }
1306
1307   if (!result && have_backup)
1308     {
1309       if (rename (bak_filename, buffer->filename) != 0)
1310         {
1311           gchar *err = g_strdup_printf ("Error restoring backup file '%s' to '%s': %s\nBackup left as '%s'",
1312                                         buffer->filename, bak_filename, g_strerror (errno), bak_filename);
1313           msgbox_run (NULL, err, "OK", NULL, NULL, 0);
1314           g_free (err);
1315         }
1316     }
1317
1318   g_free (bak_filename);
1319   
1320   return result;
1321 }
1322
1323 static gboolean
1324 save_as_ok_func (const char *filename, gpointer data)
1325 {
1326   Buffer *buffer = data;
1327   char *old_filename = buffer->filename;
1328
1329   if (!buffer->filename || strcmp (filename, buffer->filename) != 0)
1330     {
1331       struct stat statbuf;
1332
1333       if (stat (filename, &statbuf) == 0)
1334         {
1335           gchar *err = g_strdup_printf ("Ovewrite existing file '%s'?", filename);
1336           gint result = msgbox_run (NULL, err, "Yes", "No", NULL, 1);
1337           g_free (err);
1338
1339           if (result != 0)
1340             return FALSE;
1341         }
1342     }
1343   
1344   buffer->filename = g_strdup (filename);
1345
1346   if (save_buffer (buffer))
1347     {
1348       g_free (old_filename);
1349       buffer_filename_set (buffer);
1350       return TRUE;
1351     }
1352   else
1353     {
1354       g_free (buffer->filename);
1355       buffer->filename = old_filename;
1356       return FALSE;
1357     }
1358 }
1359
1360 static gboolean
1361 save_as_buffer (Buffer *buffer)
1362 {
1363   return filesel_run (NULL, "Save File", NULL, save_as_ok_func, buffer);
1364 }
1365
1366 static gboolean
1367 check_buffer_saved (Buffer *buffer)
1368 {
1369   if (gtk_text_buffer_modified (buffer->buffer))
1370     {
1371       char *pretty_name = buffer_pretty_name (buffer);
1372       char *msg = g_strdup_printf ("Save changes to '%s'?", pretty_name);
1373       gint result;
1374       
1375       g_free (pretty_name);
1376       
1377       result = msgbox_run (NULL, msg, "Yes", "No", "Cancel", 0);
1378       g_free (msg);
1379   
1380       if (result == 0)
1381         return save_as_buffer (buffer);
1382       else if (result == 1)
1383         return TRUE;
1384       else
1385         return FALSE;
1386     }
1387   else
1388     return TRUE;
1389 }
1390
1391 #define N_COLORS 16
1392
1393 static Buffer *
1394 create_buffer (void)
1395 {
1396   Buffer *buffer;
1397   PangoTabArray *tabs;
1398   gint i;
1399   
1400   buffer = g_new (Buffer, 1);
1401
1402   buffer->buffer = gtk_text_buffer_new (NULL);
1403   
1404   buffer->refcount = 1;
1405   buffer->filename = NULL;
1406   buffer->untitled_serial = -1;
1407
1408   buffer->color_tags = NULL;
1409   buffer->color_cycle_timeout = 0;
1410   buffer->start_hue = 0.0;
1411   
1412   i = 0;
1413   while (i < N_COLORS)
1414     {
1415       GtkTextTag *tag;
1416
1417       tag = gtk_text_buffer_create_tag (buffer->buffer, NULL);
1418       
1419       buffer->color_tags = g_slist_prepend (buffer->color_tags, tag);
1420       
1421       ++i;
1422     }
1423   
1424   buffer->invisible_tag = gtk_text_buffer_create_tag (buffer->buffer, NULL);
1425   g_object_set (G_OBJECT (buffer->invisible_tag),
1426                   "invisible", TRUE,
1427                   NULL);
1428   
1429   buffer->not_editable_tag = gtk_text_buffer_create_tag (buffer->buffer, NULL);
1430   g_object_set (G_OBJECT (buffer->not_editable_tag),
1431                   "editable", FALSE,
1432                   "foreground", "purple", NULL);
1433
1434   buffer->found_text_tag = gtk_text_buffer_create_tag (buffer->buffer, NULL);
1435   g_object_set (G_OBJECT (buffer->found_text_tag),
1436                   "foreground", "red", NULL);
1437
1438   tabs = pango_tab_array_new_with_positions (4,
1439                                              TRUE,
1440                                              PANGO_TAB_LEFT, 10,
1441                                              PANGO_TAB_LEFT, 30,
1442                                              PANGO_TAB_LEFT, 60,
1443                                              PANGO_TAB_LEFT, 120);
1444   
1445   buffer->custom_tabs_tag = gtk_text_buffer_create_tag (buffer->buffer, NULL);
1446   g_object_set (G_OBJECT (buffer->custom_tabs_tag),
1447                   "tabs", tabs,
1448                   "foreground", "green", NULL);
1449
1450   pango_tab_array_free (tabs);
1451   
1452   buffers = g_slist_prepend (buffers, buffer);
1453   
1454   return buffer;
1455 }
1456
1457 static char *
1458 buffer_pretty_name (Buffer *buffer)
1459 {
1460   if (buffer->filename)
1461     {
1462       char *p;
1463       char *result = g_path_get_basename (buffer->filename);
1464       p = strchr (result, '/');
1465       if (p)
1466         *p = '\0';
1467
1468       return result;
1469     }
1470   else
1471     {
1472       if (buffer->untitled_serial == -1)
1473         buffer->untitled_serial = untitled_serial++;
1474
1475       if (buffer->untitled_serial == 1)
1476         return g_strdup ("Untitled");
1477       else
1478         return g_strdup_printf ("Untitled #%d", buffer->untitled_serial);
1479     }
1480 }
1481
1482 static void
1483 buffer_filename_set (Buffer *buffer)
1484 {
1485   GSList *tmp_list = views;
1486
1487   while (tmp_list)
1488     {
1489       View *view = tmp_list->data;
1490
1491       if (view->buffer == buffer)
1492         view_set_title (view);
1493
1494       tmp_list = tmp_list->next;
1495     }
1496 }
1497
1498 static void
1499 buffer_search (Buffer     *buffer,
1500                const char *str,
1501                View       *view,
1502                gboolean forward)
1503 {
1504   GtkTextIter iter;
1505   GtkTextIter start, end;
1506   GtkWidget *dialog;
1507   int i;
1508   
1509   /* remove tag from whole buffer */
1510   gtk_text_buffer_get_bounds (buffer->buffer, &start, &end);
1511   gtk_text_buffer_remove_tag (buffer->buffer,  buffer->found_text_tag,
1512                               &start, &end );
1513   
1514   gtk_text_buffer_get_iter_at_mark (buffer->buffer, &iter,
1515                                     gtk_text_buffer_get_mark (buffer->buffer,
1516                                                               "insert"));
1517
1518   i = 0;
1519   if (*str != '\0')
1520     {
1521       GtkTextIter match_start, match_end;
1522
1523       if (forward)
1524         {
1525           while (gtk_text_iter_forward_search (&iter, str, TRUE, FALSE,
1526                                                &match_start, &match_end,
1527                                                NULL))
1528             {
1529               ++i;
1530               gtk_text_buffer_apply_tag (buffer->buffer, buffer->found_text_tag,
1531                                          &match_start, &match_end);
1532               
1533               iter = match_end;
1534             }
1535         }
1536       else
1537         {
1538           while (gtk_text_iter_backward_search (&iter, str, TRUE, FALSE,
1539                                                 &match_start, &match_end,
1540                                                 NULL))
1541             {
1542               ++i;
1543               gtk_text_buffer_apply_tag (buffer->buffer, buffer->found_text_tag,
1544                                          &match_start, &match_end);
1545               
1546               iter = match_start;
1547             }
1548         }
1549     }
1550
1551   dialog = gtk_message_dialog_new (GTK_WINDOW (view->window),
1552                                    GTK_MESSAGE_INFO,
1553                                    GTK_BUTTONS_OK,
1554                                    GTK_DIALOG_DESTROY_WITH_PARENT,
1555                                    "%d strings found and marked in red",
1556                                    i);
1557
1558   gtk_signal_connect_object (GTK_OBJECT (dialog),
1559                              "response",
1560                              GTK_SIGNAL_FUNC (gtk_widget_destroy),
1561                              GTK_OBJECT (dialog));
1562   
1563   gtk_widget_show (dialog);
1564 }
1565
1566 static void
1567 buffer_search_forward (Buffer *buffer, const char *str,
1568                        View *view)
1569 {
1570   buffer_search (buffer, str, view, TRUE);
1571 }
1572
1573 static void
1574 buffer_search_backward (Buffer *buffer, const char *str,
1575                         View *view)
1576 {
1577   buffer_search (buffer, str, view, FALSE);
1578 }
1579
1580 static void
1581 buffer_ref (Buffer *buffer)
1582 {
1583   buffer->refcount++;
1584 }
1585
1586 static void
1587 buffer_unref (Buffer *buffer)
1588 {
1589   buffer->refcount--;
1590   if (buffer->refcount == 0)
1591     {
1592       buffer_set_colors (buffer, FALSE);
1593       buffers = g_slist_remove (buffers, buffer);
1594       g_object_unref (G_OBJECT (buffer->buffer));
1595       g_free (buffer->filename);
1596       g_free (buffer);
1597     }
1598 }
1599
1600 static void
1601 hsv_to_rgb (gdouble *h,
1602             gdouble *s,
1603             gdouble *v)
1604 {
1605   gdouble hue, saturation, value;
1606   gdouble f, p, q, t;
1607
1608   if (*s == 0.0)
1609     {
1610       *h = *v;
1611       *s = *v;
1612       *v = *v; /* heh */
1613     }
1614   else
1615     {
1616       hue = *h * 6.0;
1617       saturation = *s;
1618       value = *v;
1619       
1620       if (hue >= 6.0)
1621         hue = 0.0;
1622       
1623       f = hue - (int) hue;
1624       p = value * (1.0 - saturation);
1625       q = value * (1.0 - saturation * f);
1626       t = value * (1.0 - saturation * (1.0 - f));
1627       
1628       switch ((int) hue)
1629         {
1630         case 0:
1631           *h = value;
1632           *s = t;
1633           *v = p;
1634           break;
1635           
1636         case 1:
1637           *h = q;
1638           *s = value;
1639           *v = p;
1640           break;
1641           
1642         case 2:
1643           *h = p;
1644           *s = value;
1645           *v = t;
1646           break;
1647           
1648         case 3:
1649           *h = p;
1650           *s = q;
1651           *v = value;
1652           break;
1653           
1654         case 4:
1655           *h = t;
1656           *s = p;
1657           *v = value;
1658           break;
1659           
1660         case 5:
1661           *h = value;
1662           *s = p;
1663           *v = q;
1664           break;
1665           
1666         default:
1667           g_assert_not_reached ();
1668         }
1669     }
1670 }
1671
1672 static void
1673 hue_to_color (gdouble   hue,
1674               GdkColor *color)
1675 {
1676   gdouble h, s, v;
1677
1678   h = hue;
1679   s = 1.0;
1680   v = 1.0;
1681
1682   g_return_if_fail (hue <= 1.0);
1683   
1684   hsv_to_rgb (&h, &s, &v);
1685
1686   color->red = h * 65535;
1687   color->green = s * 65535;
1688   color->blue = v * 65535;
1689 }
1690
1691
1692 static gint
1693 color_cycle_timeout (gpointer data)
1694 {
1695   Buffer *buffer = data;
1696
1697   buffer_cycle_colors (buffer);
1698
1699   return TRUE;
1700 }
1701
1702 static void
1703 buffer_set_colors (Buffer  *buffer,
1704                    gboolean enabled)
1705 {
1706   GSList *tmp;
1707   gdouble hue = 0.0;
1708
1709   if (enabled && buffer->color_cycle_timeout == 0)
1710     buffer->color_cycle_timeout = gtk_timeout_add (200, color_cycle_timeout, buffer);
1711   else if (!enabled && buffer->color_cycle_timeout != 0)
1712     {
1713       gtk_timeout_remove (buffer->color_cycle_timeout);
1714       buffer->color_cycle_timeout = 0;
1715     }
1716     
1717   tmp = buffer->color_tags;
1718   while (tmp != NULL)
1719     {
1720       if (enabled)
1721         {
1722           GdkColor color;
1723           
1724           hue_to_color (hue, &color);
1725
1726           g_object_set (G_OBJECT (tmp->data),
1727                           "foreground_gdk", &color,
1728                           NULL);
1729         }
1730       else
1731         g_object_set (G_OBJECT (tmp->data),
1732                         "foreground_set", FALSE,
1733                         NULL);
1734
1735       hue += 1.0 / N_COLORS;
1736       
1737       tmp = g_slist_next (tmp);
1738     }
1739 }
1740
1741 static void
1742 buffer_cycle_colors (Buffer *buffer)
1743 {
1744   GSList *tmp;
1745   gdouble hue = buffer->start_hue;
1746   
1747   tmp = buffer->color_tags;
1748   while (tmp != NULL)
1749     {
1750       GdkColor color;
1751       
1752       hue_to_color (hue, &color);
1753       
1754       g_object_set (G_OBJECT (tmp->data),
1755                     "foreground_gdk", &color,
1756                     NULL);
1757
1758       hue += 1.0 / N_COLORS;
1759       if (hue > 1.0)
1760         hue = 0.0;
1761       
1762       tmp = g_slist_next (tmp);
1763     }
1764
1765   buffer->start_hue += 1.0 / N_COLORS;
1766   if (buffer->start_hue > 1.0)
1767     buffer->start_hue = 0.0;
1768 }
1769
1770 static void
1771 close_view (View *view)
1772 {
1773   views = g_slist_remove (views, view);
1774   buffer_unref (view->buffer);
1775   gtk_widget_destroy (view->window);
1776   g_object_unref (G_OBJECT (view->item_factory));
1777   
1778   g_free (view);
1779   
1780   if (!views)
1781     gtk_main_quit ();
1782 }
1783
1784 static void
1785 check_close_view (View *view)
1786 {
1787   if (view->buffer->refcount > 1 ||
1788       check_buffer_saved (view->buffer))
1789     close_view (view);
1790 }
1791
1792 static void
1793 view_set_title (View *view)
1794 {
1795   char *pretty_name = buffer_pretty_name (view->buffer);
1796   char *title = g_strconcat ("testtext - ", pretty_name, NULL);
1797
1798   gtk_window_set_title (GTK_WINDOW (view->window), title);
1799
1800   g_free (pretty_name);
1801   g_free (title);
1802 }
1803
1804 static void
1805 cursor_set_callback (GtkTextBuffer     *buffer,
1806                      const GtkTextIter *location,
1807                      GtkTextMark       *mark,
1808                      gpointer           user_data)
1809 {
1810   GtkTextView *text_view;
1811
1812   /* Redraw tab windows if the cursor moves
1813    * on the mapped widget (windows may not exist before realization...
1814    */
1815   
1816   text_view = GTK_TEXT_VIEW (user_data);
1817   
1818   if (GTK_WIDGET_MAPPED (text_view) &&
1819       mark == gtk_text_buffer_get_insert (buffer))
1820     {
1821       GdkWindow *tab_window;
1822
1823       tab_window = gtk_text_view_get_window (text_view,
1824                                              GTK_TEXT_WINDOW_TOP);
1825
1826       gdk_window_invalidate_rect (tab_window, NULL, FALSE);
1827       
1828       tab_window = gtk_text_view_get_window (text_view,
1829                                              GTK_TEXT_WINDOW_BOTTOM);
1830
1831       gdk_window_invalidate_rect (tab_window, NULL, FALSE);
1832     }
1833 }
1834
1835 static gint
1836 tab_stops_expose (GtkWidget      *widget,
1837                   GdkEventExpose *event,
1838                   gpointer        user_data)
1839 {
1840   gint first_x;
1841   gint last_x;
1842   gint i;
1843   GdkWindow *top_win;
1844   GdkWindow *bottom_win;
1845   GtkTextView *text_view;
1846   GtkTextWindowType type;
1847   GdkDrawable *target;
1848   gint *positions = NULL;
1849   gint size;
1850   GtkTextAttributes *attrs;
1851   GtkTextIter insert;
1852   GtkTextBuffer *buffer;
1853   gboolean in_pixels;
1854   
1855   text_view = GTK_TEXT_VIEW (widget);
1856   
1857   /* See if this expose is on the tab stop window */
1858   top_win = gtk_text_view_get_window (text_view,
1859                                       GTK_TEXT_WINDOW_TOP);
1860
1861   bottom_win = gtk_text_view_get_window (text_view,
1862                                          GTK_TEXT_WINDOW_BOTTOM);
1863
1864   if (event->window == top_win)
1865     {
1866       type = GTK_TEXT_WINDOW_TOP;
1867       target = top_win;
1868     }
1869   else if (event->window == bottom_win)
1870     {
1871       type = GTK_TEXT_WINDOW_BOTTOM;
1872       target = bottom_win;
1873     }
1874   else
1875     return FALSE;
1876   
1877   first_x = event->area.x;
1878   last_x = first_x + event->area.width;
1879
1880   gtk_text_view_window_to_buffer_coords (text_view,
1881                                          type,
1882                                          first_x,
1883                                          0,
1884                                          &first_x,
1885                                          NULL);
1886
1887   gtk_text_view_window_to_buffer_coords (text_view,
1888                                          type,
1889                                          last_x,
1890                                          0,
1891                                          &last_x,
1892                                          NULL);
1893
1894   buffer = gtk_text_view_get_buffer (text_view);
1895
1896   gtk_text_buffer_get_iter_at_mark (buffer,
1897                                     &insert,
1898                                     gtk_text_buffer_get_mark (buffer,
1899                                                               "insert"));
1900   
1901   attrs = gtk_text_attributes_new ();
1902
1903   gtk_text_iter_get_attributes (&insert, attrs);
1904
1905   if (attrs->tabs)
1906     {
1907       size = pango_tab_array_get_size (attrs->tabs);
1908       
1909       pango_tab_array_get_tabs (attrs->tabs,
1910                                 NULL,
1911                                 &positions);
1912
1913       in_pixels = pango_tab_array_get_positions_in_pixels (attrs->tabs);
1914     }
1915   else
1916     {
1917       size = 0;
1918       in_pixels = FALSE;
1919     }
1920       
1921   gtk_text_attributes_unref (attrs);
1922   
1923   i = 0;
1924   while (i < size)
1925     {
1926       gint pos;
1927
1928       if (!in_pixels)
1929         positions[i] = PANGO_PIXELS (positions[i]);
1930       
1931       gtk_text_view_buffer_to_window_coords (text_view,
1932                                              type,
1933                                              positions[i],
1934                                              0,
1935                                              &pos,
1936                                              NULL);
1937       
1938       gdk_draw_line (target, 
1939                      widget->style->fg_gc [widget->state],
1940                      pos, 0,
1941                      pos, 15); 
1942       
1943       ++i;
1944     }
1945
1946   g_free (positions);
1947
1948   return TRUE;
1949 }
1950
1951 static void
1952 get_lines (GtkTextView  *text_view,
1953            gint          first_y,
1954            gint          last_y,
1955            GArray       *buffer_coords,
1956            GArray       *numbers,
1957            gint         *countp)
1958 {
1959   GtkTextIter iter;
1960   gint count;
1961   gint size;  
1962
1963   g_array_set_size (buffer_coords, 0);
1964   g_array_set_size (numbers, 0);
1965   
1966   /* Get iter at first y */
1967   gtk_text_view_get_line_at_y (text_view, &iter, first_y, NULL);
1968
1969   /* For each iter, get its location and add it to the arrays.
1970    * Stop when we pass last_y
1971    */
1972   count = 0;
1973   size = 0;
1974
1975   while (!gtk_text_iter_is_end (&iter))
1976     {
1977       gint y, height;
1978       gint line_num;
1979       
1980       gtk_text_view_get_line_yrange (text_view, &iter, &y, &height);
1981
1982       g_array_append_val (buffer_coords, y);
1983       line_num = gtk_text_iter_get_line (&iter);
1984       g_array_append_val (numbers, line_num);
1985       
1986       ++count;
1987
1988       if ((y + height) >= last_y)
1989         break;
1990       
1991       gtk_text_iter_forward_line (&iter);
1992     }
1993
1994   *countp = count;
1995 }
1996
1997 static gint
1998 line_numbers_expose (GtkWidget      *widget,
1999                      GdkEventExpose *event,
2000                      gpointer        user_data)
2001 {
2002   gint count;
2003   GArray *numbers;
2004   GArray *pixels;
2005   gint first_y;
2006   gint last_y;
2007   gint i;
2008   GdkWindow *left_win;
2009   GdkWindow *right_win;
2010   PangoLayout *layout;
2011   GtkTextView *text_view;
2012   GtkTextWindowType type;
2013   GdkDrawable *target;
2014   
2015   text_view = GTK_TEXT_VIEW (widget);
2016   
2017   /* See if this expose is on the line numbers window */
2018   left_win = gtk_text_view_get_window (text_view,
2019                                        GTK_TEXT_WINDOW_LEFT);
2020
2021   right_win = gtk_text_view_get_window (text_view,
2022                                         GTK_TEXT_WINDOW_RIGHT);
2023
2024   if (event->window == left_win)
2025     {
2026       type = GTK_TEXT_WINDOW_LEFT;
2027       target = left_win;
2028     }
2029   else if (event->window == right_win)
2030     {
2031       type = GTK_TEXT_WINDOW_RIGHT;
2032       target = right_win;
2033     }
2034   else
2035     return FALSE;
2036   
2037   first_y = event->area.y;
2038   last_y = first_y + event->area.height;
2039
2040   gtk_text_view_window_to_buffer_coords (text_view,
2041                                          type,
2042                                          0,
2043                                          first_y,
2044                                          NULL,
2045                                          &first_y);
2046
2047   gtk_text_view_window_to_buffer_coords (text_view,
2048                                          type,
2049                                          0,
2050                                          last_y,
2051                                          NULL,
2052                                          &last_y);
2053
2054   numbers = g_array_new (FALSE, FALSE, sizeof (gint));
2055   pixels = g_array_new (FALSE, FALSE, sizeof (gint));
2056   
2057   get_lines (text_view,
2058              first_y,
2059              last_y,
2060              pixels,
2061              numbers,
2062              &count);
2063   
2064   /* Draw fully internationalized numbers! */
2065   
2066   layout = gtk_widget_create_pango_layout (widget, "");
2067   
2068   i = 0;
2069   while (i < count)
2070     {
2071       gint pos;
2072       gchar *str;
2073       
2074       gtk_text_view_buffer_to_window_coords (text_view,
2075                                              type,
2076                                              0,
2077                                              g_array_index (pixels, gint, i),
2078                                              NULL,
2079                                              &pos);
2080
2081       str = g_strdup_printf ("%d", g_array_index (numbers, gint, i));
2082
2083       pango_layout_set_text (layout, str, -1);
2084
2085       gtk_paint_layout (widget->style,
2086                         target,
2087                         GTK_WIDGET_STATE (widget),
2088                         NULL,
2089                         widget,
2090                         NULL,
2091                         2, pos + 2,
2092                         layout);
2093
2094       g_free (str);
2095       
2096       ++i;
2097     }
2098
2099   g_array_free (pixels, TRUE);
2100   g_array_free (numbers, TRUE);
2101   
2102   g_object_unref (G_OBJECT (layout));
2103
2104   return TRUE;
2105 }
2106
2107 static View *
2108 create_view (Buffer *buffer)
2109 {
2110   View *view;
2111   
2112   GtkWidget *sw;
2113   GtkWidget *vbox;
2114   
2115   view = g_new0 (View, 1);
2116   views = g_slist_prepend (views, view);
2117
2118   view->buffer = buffer;
2119   buffer_ref (buffer);
2120   
2121   view->window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
2122   g_object_set_data (G_OBJECT (view->window), "view", view);
2123   
2124   gtk_signal_connect (GTK_OBJECT (view->window), "delete_event",
2125                       GTK_SIGNAL_FUNC (delete_event_cb), NULL);
2126
2127   view->accel_group = gtk_accel_group_new ();
2128   view->item_factory = gtk_item_factory_new (GTK_TYPE_MENU_BAR, "<main>", view->accel_group);
2129   g_object_set_data (G_OBJECT (view->item_factory), "view", view);
2130   
2131   gtk_item_factory_create_items (view->item_factory, G_N_ELEMENTS (menu_items), menu_items, view);
2132
2133   gtk_window_add_accel_group (GTK_WINDOW (view->window), view->accel_group);
2134
2135   vbox = gtk_vbox_new (FALSE, 0);
2136   gtk_container_add (GTK_CONTAINER (view->window), vbox);
2137
2138   gtk_box_pack_start (GTK_BOX (vbox),
2139                       gtk_item_factory_get_widget (view->item_factory, "<main>"),
2140                       FALSE, FALSE, 0);
2141   
2142   sw = gtk_scrolled_window_new (NULL, NULL);
2143   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
2144                                  GTK_POLICY_AUTOMATIC,
2145                                  GTK_POLICY_AUTOMATIC);
2146
2147   view->text_view = gtk_text_view_new_with_buffer (buffer->buffer);
2148   gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (view->text_view),
2149                                GTK_WRAP_WORD);
2150   
2151   /* Draw tab stops in the top and bottom windows. */
2152   
2153   gtk_text_view_set_border_window_size (GTK_TEXT_VIEW (view->text_view),
2154                                         GTK_TEXT_WINDOW_TOP,
2155                                         15);
2156
2157   gtk_text_view_set_border_window_size (GTK_TEXT_VIEW (view->text_view),
2158                                         GTK_TEXT_WINDOW_BOTTOM,
2159                                         15);
2160
2161   gtk_signal_connect (GTK_OBJECT (view->text_view),
2162                       "expose_event",
2163                       GTK_SIGNAL_FUNC (tab_stops_expose),
2164                       NULL);  
2165
2166   g_signal_connect_data (G_OBJECT (view->buffer->buffer),
2167                          "mark_set",
2168                          GTK_SIGNAL_FUNC (cursor_set_callback),
2169                          view->text_view, NULL, FALSE, FALSE);
2170   
2171   /* Draw line numbers in the side windows; we should really be
2172    * more scientific about what width we set them to.
2173    */
2174   gtk_text_view_set_border_window_size (GTK_TEXT_VIEW (view->text_view),
2175                                         GTK_TEXT_WINDOW_RIGHT,
2176                                         30);
2177   
2178   gtk_text_view_set_border_window_size (GTK_TEXT_VIEW (view->text_view),
2179                                         GTK_TEXT_WINDOW_LEFT,
2180                                         30);
2181   
2182   gtk_signal_connect (GTK_OBJECT (view->text_view),
2183                       "expose_event",
2184                       GTK_SIGNAL_FUNC (line_numbers_expose),
2185                       NULL);
2186   
2187   gtk_box_pack_start (GTK_BOX (vbox), sw, TRUE, TRUE, 0);
2188   gtk_container_add (GTK_CONTAINER (sw), view->text_view);
2189
2190   gtk_window_set_default_size (GTK_WINDOW (view->window), 500, 500);
2191
2192   gtk_widget_grab_focus (view->text_view);
2193
2194   view_set_title (view);
2195   view_init_menus (view);
2196
2197   view_add_example_widgets (view);
2198   
2199   gtk_widget_show_all (view->window);
2200   return view;
2201 }
2202
2203 static void
2204 view_add_example_widgets (View *view)
2205 {
2206   GtkTextChildAnchor *anchor;
2207   Buffer *buffer;
2208
2209   return;
2210   
2211   buffer = view->buffer;
2212   
2213   anchor = g_object_get_data (G_OBJECT (buffer->buffer),
2214                               "anchor");
2215
2216   if (anchor && !gtk_text_child_anchor_get_deleted (anchor))
2217     {
2218       GtkWidget *widget;
2219       
2220       widget = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING,
2221                                          GTK_ICON_SIZE_DIALOG);
2222
2223       widget = gtk_button_new_with_label ("Foo");
2224       
2225       gtk_text_view_add_child_at_anchor (GTK_TEXT_VIEW (view->text_view),
2226                                          widget,
2227                                          anchor);
2228
2229       gtk_widget_show (widget);
2230     }
2231 }
2232
2233 static gboolean
2234 file_exists (const char *filename)
2235 {
2236   struct stat statbuf;
2237
2238   return stat (filename, &statbuf) == 0;
2239 }
2240 void
2241 test_init ()
2242 {
2243   if (file_exists ("../gdk-pixbuf/.libs/libpixbufloader-pnm.so"))
2244     {
2245       putenv ("GDK_PIXBUF_MODULEDIR=../gdk-pixbuf/.libs");
2246       putenv ("GTK_IM_MODULE_FILE=./gtk.immodules");
2247     }
2248 }
2249
2250 int
2251 main (int argc, char** argv)
2252 {
2253   Buffer *buffer;
2254   View *view;
2255   int i;
2256
2257   test_init ();
2258   gtk_set_locale ();
2259   gtk_init (&argc, &argv);
2260   
2261   buffer = create_buffer ();
2262   view = create_view (buffer);
2263   buffer_unref (buffer);
2264   
2265   push_active_window (GTK_WINDOW (view->window));
2266   for (i=1; i < argc; i++)
2267     {
2268       char *filename;
2269
2270       /* Quick and dirty canonicalization - better should be in GLib
2271        */
2272
2273       if (!g_path_is_absolute (argv[i]))
2274         {
2275           char *cwd = g_get_current_dir ();
2276           filename = g_strconcat (cwd, "/", argv[i], NULL);
2277           g_free (cwd);
2278         }
2279       else
2280         filename = argv[i];
2281
2282       open_ok_func (filename, view);
2283
2284       if (filename != argv[i])
2285         g_free (filename);
2286     }
2287   pop_active_window ();
2288   
2289   gtk_main ();
2290
2291   return 0;
2292 }
2293
2294