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