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