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