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