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