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