]> Pileus Git - ~andy/gtk/blob - tests/testtext.c
Implement the side windows.
[~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 *not_editable_tag;
24   GtkTextTag *found_text_tag;
25   GtkTextTag *custom_tabs_tag;
26 };
27
28 struct _View
29 {
30   GtkWidget *window;
31   GtkWidget *text_view;
32   GtkAccelGroup *accel_group;
33   GtkItemFactory *item_factory;
34   Buffer *buffer;
35 };
36
37 static void push_active_window (GtkWindow *window);
38 static void pop_active_window (void);
39 static GtkWindow *get_active_window (void);
40
41 static Buffer * create_buffer      (void);
42 static gboolean check_buffer_saved (Buffer *buffer);
43 static gboolean save_buffer        (Buffer *buffer);
44 static gboolean save_as_buffer     (Buffer *buffer);
45 static char *   buffer_pretty_name (Buffer *buffer);
46 static void     buffer_filename_set (Buffer *buffer);
47 static void     buffer_search_forward (Buffer *buffer,
48                                        const char *str,
49                                        View *view);
50
51 static View *view_from_widget (GtkWidget *widget);
52
53 static View *create_view      (Buffer *buffer);
54 static void  check_close_view (View   *view);
55 static void  close_view       (View   *view);
56 static void  view_set_title   (View   *view);
57 static void  view_init_menus  (View   *view);
58
59 GSList *buffers = NULL;
60 GSList *views = NULL;
61
62 static void
63 push_active_window (GtkWindow *window)
64 {
65   gtk_object_ref (GTK_OBJECT (window));
66   active_window_stack = g_slist_prepend (active_window_stack, window);
67 }
68
69 static void
70 pop_active_window (void)
71 {
72   gtk_object_unref (active_window_stack->data);
73   active_window_stack = g_slist_delete_link (active_window_stack, active_window_stack);
74 }
75
76 static GtkWindow *
77 get_active_window (void)
78 {
79   if (active_window_stack)
80     return active_window_stack->data;
81   else
82     return NULL;
83 }
84
85 /*
86  * Filesel utility function
87  */
88
89 typedef gboolean (*FileselOKFunc) (const char *filename, gpointer data);
90
91 static void
92 filesel_ok_cb (GtkWidget *button, GtkWidget *filesel)
93 {
94   FileselOKFunc ok_func = gtk_object_get_data (GTK_OBJECT (filesel), "ok-func");
95   gpointer data = gtk_object_get_data (GTK_OBJECT (filesel), "ok-data");
96   gint *result = gtk_object_get_data (GTK_OBJECT (filesel), "ok-result");
97   
98   gtk_widget_hide (filesel);
99   
100   if ((*ok_func) (gtk_file_selection_get_filename (GTK_FILE_SELECTION (filesel)), data))
101     {
102       gtk_widget_destroy (filesel);
103       *result = TRUE;
104     }
105   else
106     gtk_widget_show (filesel);
107 }
108
109 gboolean
110 filesel_run (GtkWindow    *parent, 
111              const char   *title,
112              const char   *start_file,
113              FileselOKFunc func,
114              gpointer      data)
115 {
116   GtkWidget *filesel = gtk_file_selection_new (title);
117   gboolean result = FALSE;
118
119   if (!parent)
120     parent = get_active_window ();
121   
122   if (parent)
123     gtk_window_set_transient_for (GTK_WINDOW (filesel), parent);
124
125   if (start_file)
126     gtk_file_selection_set_filename (GTK_FILE_SELECTION (filesel), start_file);
127
128   
129   gtk_object_set_data (GTK_OBJECT (filesel), "ok-func", func);
130   gtk_object_set_data (GTK_OBJECT (filesel), "ok-data", data);
131   gtk_object_set_data (GTK_OBJECT (filesel), "ok-result", &result);
132
133   gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION (filesel)->ok_button),
134                       "clicked",
135                       GTK_SIGNAL_FUNC (filesel_ok_cb), filesel);
136   gtk_signal_connect_object (GTK_OBJECT (GTK_FILE_SELECTION (filesel)->cancel_button),
137                              "clicked",
138                              GTK_SIGNAL_FUNC (gtk_widget_destroy), GTK_OBJECT (filesel));
139
140   gtk_signal_connect (GTK_OBJECT (filesel), "destroy",
141                       GTK_SIGNAL_FUNC (gtk_main_quit), NULL);
142   gtk_window_set_modal (GTK_WINDOW (filesel), TRUE);
143
144   gtk_widget_show (filesel);
145   gtk_main ();
146
147   return result;
148 }
149
150 /*
151  * MsgBox utility functions
152  */
153
154 static void
155 msgbox_yes_cb (GtkWidget *widget, gboolean *result)
156 {
157   *result = 0;
158   gtk_object_destroy (GTK_OBJECT (gtk_widget_get_toplevel (widget)));
159 }
160
161 static void
162 msgbox_no_cb (GtkWidget *widget, gboolean *result)
163 {
164   *result = 1;
165   gtk_object_destroy (GTK_OBJECT (gtk_widget_get_toplevel (widget)));
166 }
167
168 static gboolean
169 msgbox_key_press_cb (GtkWidget *widget, GdkEventKey *event, gpointer data)
170 {
171   if (event->keyval == GDK_Escape)
172     {
173       gtk_signal_emit_stop_by_name (GTK_OBJECT (widget), "key_press_event");
174       gtk_object_destroy (GTK_OBJECT (widget));
175       return TRUE;
176     }
177
178   return FALSE;
179 }
180
181 gint
182 msgbox_run (GtkWindow  *parent,
183             const char *message,
184             const char *yes_button,
185             const char *no_button,
186             const char *cancel_button,
187             gint default_index)
188 {
189   gboolean result = -1;
190   GtkWidget *dialog;
191   GtkWidget *button;
192   GtkWidget *label;
193   GtkWidget *vbox;
194   GtkWidget *button_box;
195   GtkWidget *separator;
196
197   g_return_val_if_fail (message != NULL, FALSE);
198   g_return_val_if_fail (default_index >= 0 && default_index <= 1, FALSE);
199
200   if (!parent)
201     parent = get_active_window ();
202   
203   /* Create a dialog
204    */
205   dialog = gtk_window_new (GTK_WINDOW_TOPLEVEL);
206   gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
207   if (parent)
208     gtk_window_set_transient_for (GTK_WINDOW (dialog), parent);
209   gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
210
211   /* Quit our recursive main loop when the dialog is destroyed.
212    */
213   gtk_signal_connect (GTK_OBJECT (dialog), "destroy",
214                       GTK_SIGNAL_FUNC (gtk_main_quit), NULL);
215
216   /* Catch Escape key presses and have them destroy the dialog
217    */
218   gtk_signal_connect (GTK_OBJECT (dialog), "key_press_event",
219                       GTK_SIGNAL_FUNC (msgbox_key_press_cb), NULL);
220
221   /* Fill in the contents of the widget
222    */
223   vbox = gtk_vbox_new (FALSE, 0);
224   gtk_container_add (GTK_CONTAINER (dialog), vbox);
225   
226   label = gtk_label_new (message);
227   gtk_misc_set_padding (GTK_MISC (label), 12, 12);
228   gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
229   gtk_box_pack_start (GTK_BOX (vbox), label, TRUE, TRUE, 0);
230
231   separator = gtk_hseparator_new ();
232   gtk_box_pack_start (GTK_BOX (vbox), separator, FALSE, FALSE, 0);
233
234   button_box = gtk_hbutton_box_new ();
235   gtk_box_pack_start (GTK_BOX (vbox), button_box, FALSE, FALSE, 0);
236   gtk_container_set_border_width (GTK_CONTAINER (button_box), 8);
237   
238
239   /* When Yes is clicked, call the msgbox_yes_cb
240    * This sets the result variable and destroys the dialog
241    */
242   if (yes_button)
243     {
244       button = gtk_button_new_with_label (yes_button);
245       GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
246       gtk_container_add (GTK_CONTAINER (button_box), button);
247
248       if (default_index == 0)
249         gtk_widget_grab_default (button);
250       
251       gtk_signal_connect (GTK_OBJECT (button), "clicked",
252                           GTK_SIGNAL_FUNC (msgbox_yes_cb), &result);
253     }
254
255   /* When No is clicked, call the msgbox_no_cb
256    * This sets the result variable and destroys the dialog
257    */
258   if (no_button)
259     {
260       button = gtk_button_new_with_label (no_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_no_cb), &result);
269     }
270
271   /* When Cancel is clicked, destroy the dialog
272    */
273   if (cancel_button)
274     {
275       button = gtk_button_new_with_label (cancel_button);
276       GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
277       gtk_container_add (GTK_CONTAINER (button_box), button);
278       
279       if (default_index == 1)
280         gtk_widget_grab_default (button);
281       
282       gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
283                                  GTK_SIGNAL_FUNC (gtk_object_destroy), GTK_OBJECT (dialog));
284     }
285
286   gtk_widget_show_all (dialog);
287
288   /* Run a recursive main loop until a button is clicked
289    * or the user destroys the dialog through the window mananger */
290   gtk_main ();
291
292   return result;
293 }
294
295 /*
296  * Example buffer filling code
297  */
298 static gint
299 blink_timeout (gpointer data)
300 {
301   GtkTextTag *tag;
302   static gboolean flip = FALSE;
303   
304   tag = GTK_TEXT_TAG (data);
305
306   gtk_object_set (GTK_OBJECT (tag),
307                  "foreground", flip ? "blue" : "purple",
308                  NULL);
309
310   flip = !flip;
311
312   return TRUE;
313 }
314
315 static gint
316 tag_event_handler (GtkTextTag *tag, GtkWidget *widget, GdkEvent *event,
317                   const GtkTextIter *iter, gpointer user_data)
318 {
319   gint char_index;
320
321   char_index = gtk_text_iter_get_offset (iter);
322   
323   switch (event->type)
324     {
325     case GDK_MOTION_NOTIFY:
326       printf ("Motion event at char %d tag `%s'\n",
327              char_index, tag->name);
328       break;
329         
330     case GDK_BUTTON_PRESS:
331       printf ("Button press at char %d tag `%s'\n",
332              char_index, tag->name);
333       break;
334         
335     case GDK_2BUTTON_PRESS:
336       printf ("Double click at char %d tag `%s'\n",
337              char_index, tag->name);
338       break;
339         
340     case GDK_3BUTTON_PRESS:
341       printf ("Triple click at char %d tag `%s'\n",
342              char_index, tag->name);
343       break;
344         
345     case GDK_BUTTON_RELEASE:
346       printf ("Button release at char %d tag `%s'\n",
347              char_index, tag->name);
348       break;
349         
350     case GDK_KEY_PRESS:
351     case GDK_KEY_RELEASE:
352       printf ("Key event at char %d tag `%s'\n",
353               char_index, tag->name);
354       break;
355       
356     case GDK_ENTER_NOTIFY:
357     case GDK_LEAVE_NOTIFY:
358     case GDK_PROPERTY_NOTIFY:
359     case GDK_SELECTION_CLEAR:
360     case GDK_SELECTION_REQUEST:
361     case GDK_SELECTION_NOTIFY:
362     case GDK_PROXIMITY_IN:
363     case GDK_PROXIMITY_OUT:
364     case GDK_DRAG_ENTER:
365     case GDK_DRAG_LEAVE:
366     case GDK_DRAG_MOTION:
367     case GDK_DRAG_STATUS:
368     case GDK_DROP_START:
369     case GDK_DROP_FINISHED:
370     default:
371       break;
372     }
373
374   return FALSE;
375 }
376
377 static void
378 setup_tag (GtkTextTag *tag)
379 {
380
381   gtk_signal_connect (GTK_OBJECT (tag),
382                      "event",
383                      GTK_SIGNAL_FUNC (tag_event_handler),
384                      NULL);
385 }
386
387 static char  *book_closed_xpm[] = {
388 "16 16 6 1",
389 "       c None s None",
390 ".      c black",
391 "X      c red",
392 "o      c yellow",
393 "O      c #808080",
394 "#      c white",
395 "                ",
396 "       ..       ",
397 "     ..XX.      ",
398 "   ..XXXXX.     ",
399 " ..XXXXXXXX.    ",
400 ".ooXXXXXXXXX.   ",
401 "..ooXXXXXXXXX.  ",
402 ".X.ooXXXXXXXXX. ",
403 ".XX.ooXXXXXX..  ",
404 " .XX.ooXXX..#O  ",
405 "  .XX.oo..##OO. ",
406 "   .XX..##OO..  ",
407 "    .X.#OO..    ",
408 "     ..O..      ",
409 "      ..        ",
410 "                "};
411
412
413
414 void
415 fill_example_buffer (GtkTextBuffer *buffer)
416 {
417   GtkTextIter iter, iter2;
418   GtkTextTag *tag;
419   GdkColor color;
420   GdkColor color2;
421   GdkPixmap *pixmap;
422   GdkBitmap *mask;
423   int i;
424   char *str;
425   
426   tag = gtk_text_buffer_create_tag (buffer, "fg_blue");
427
428   /*       gtk_timeout_add (1000, blink_timeout, tag); */
429       
430   setup_tag (tag);
431   
432   color.red = color.green = 0;
433   color.blue = 0xffff;
434   color2.red = 0xfff;
435   color2.blue = 0x0;
436   color2.green = 0;
437   gtk_object_set (GTK_OBJECT (tag),
438                  "foreground_gdk", &color,
439                  "background_gdk", &color2,
440                  "font", "Sans 24",
441                  NULL);
442
443   tag = gtk_text_buffer_create_tag (buffer, "fg_red");
444
445   setup_tag (tag);
446       
447   color.blue = color.green = 0;
448   color.red = 0xffff;
449   gtk_object_set (GTK_OBJECT (tag),
450                  "offset", -4,
451                  "foreground_gdk", &color,
452                  NULL);
453
454   tag = gtk_text_buffer_create_tag (buffer, "bg_green");
455
456   setup_tag (tag);
457       
458   color.blue = color.red = 0;
459   color.green = 0xffff;
460   gtk_object_set (GTK_OBJECT (tag),
461                  "background_gdk", &color,
462                  "font", "Sans 10",
463                  NULL);
464
465   tag = gtk_text_buffer_create_tag (buffer, "strikethrough");
466
467   setup_tag (tag);
468       
469   gtk_object_set (GTK_OBJECT (tag),
470                  "strikethrough", TRUE,
471                  NULL);
472
473
474   tag = gtk_text_buffer_create_tag (buffer, "underline");
475
476   setup_tag (tag);
477       
478   gtk_object_set (GTK_OBJECT (tag),
479                  "underline", PANGO_UNDERLINE_SINGLE,
480                  NULL);
481
482   setup_tag (tag);
483       
484   gtk_object_set (GTK_OBJECT (tag),
485                  "underline", PANGO_UNDERLINE_SINGLE,
486                  NULL);
487
488   tag = gtk_text_buffer_create_tag (buffer, "centered");
489       
490   gtk_object_set (GTK_OBJECT (tag),
491                  "justify", GTK_JUSTIFY_CENTER,
492                  NULL);
493
494   tag = gtk_text_buffer_create_tag (buffer, "rtl_quote");
495       
496   gtk_object_set (GTK_OBJECT (tag),
497                  "wrap_mode", GTK_WRAPMODE_WORD,
498                  "direction", GTK_TEXT_DIR_RTL,
499                  "left_wrapped_line_margin", 20,
500                  "left_margin", 20,
501                  "right_margin", 20,
502                  NULL);
503   
504   pixmap = gdk_pixmap_colormap_create_from_xpm_d (NULL,
505                                                   gtk_widget_get_default_colormap (),
506                                                   &mask,
507                                                   NULL, book_closed_xpm);
508   
509   g_assert (pixmap != NULL);
510   
511   i = 0;
512   while (i < 100)
513     {
514       GtkTextMark * temp_mark;
515       
516       gtk_text_buffer_get_iter_at_offset (buffer, &iter, 0);
517           
518       gtk_text_buffer_insert_pixmap (buffer, &iter, pixmap, mask);
519           
520       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",
521                             i);
522       
523       gtk_text_buffer_insert (buffer, &iter, str, -1);
524
525       g_free (str);
526       
527       gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, 0, 5);
528           
529       gtk_text_buffer_insert (buffer, &iter,
530                              "(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"
531                              /* This is UTF8 stuff, Emacs doesn't
532                                 really know how to display it */
533                              "German (Deutsch Süd) Grüß Gott Greek (Ελληνικά) Γειά σας Hebrew   שלום Japanese (日本語)\n", -1);
534
535       temp_mark =
536         gtk_text_buffer_create_mark (buffer, "tmp_mark", &iter, TRUE);
537
538 #if 1
539       gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, 0, 6);
540       gtk_text_buffer_get_iter_at_line_offset (buffer, &iter2, 0, 13);
541
542       gtk_text_buffer_apply_tag_by_name (buffer, "fg_blue", &iter, &iter2);
543
544       gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, 1, 10);
545       gtk_text_buffer_get_iter_at_line_offset (buffer, &iter2, 1, 16);
546
547       gtk_text_buffer_apply_tag_by_name (buffer, "underline", &iter, &iter2);
548
549       gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, 1, 14);
550       gtk_text_buffer_get_iter_at_line_offset (buffer, &iter2, 1, 24);
551
552       gtk_text_buffer_apply_tag_by_name (buffer, "strikethrough", &iter, &iter2);
553           
554       gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, 0, 9);
555       gtk_text_buffer_get_iter_at_line_offset (buffer, &iter2, 0, 16);
556
557       gtk_text_buffer_apply_tag_by_name (buffer, "bg_green", &iter, &iter2);
558   
559       gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, 4, 2);
560       gtk_text_buffer_get_iter_at_line_offset (buffer, &iter2, 4, 10);
561
562       gtk_text_buffer_apply_tag_by_name (buffer, "bg_green", &iter, &iter2);
563
564       gtk_text_buffer_get_iter_at_line_offset (buffer, &iter, 4, 8);
565       gtk_text_buffer_get_iter_at_line_offset (buffer, &iter2, 4, 15);
566
567       gtk_text_buffer_apply_tag_by_name (buffer, "fg_red", &iter, &iter2);
568 #endif
569
570       gtk_text_buffer_get_iter_at_mark (buffer, &iter, temp_mark);
571       gtk_text_buffer_insert (buffer, &iter, "Centered text!\n", -1);
572           
573       gtk_text_buffer_get_iter_at_mark (buffer, &iter2, temp_mark);
574       gtk_text_buffer_apply_tag_by_name (buffer, "centered", &iter2, &iter);
575
576       gtk_text_buffer_move_mark (buffer, temp_mark, &iter);
577       gtk_text_buffer_insert (buffer, &iter, "Word wrapped, Right-to-left Quote\n", -1);
578       gtk_text_buffer_insert (buffer, &iter, "وقد بدأ ثلاث من أكثر المؤسسات تقدما في شبكة اكسيون برامجها كمنظمات لا تسعى للربح، ثم تحولت في السنوات الخمس الماضية إلى مؤسسات مالية منظمة، وباتت جزءا من النظام المالي في بلدانها، ولكنها تتخصص في خدمة قطاع المشروعات الصغيرة. وأحد أكثر هذه المؤسسات نجاحا هو »بانكوسول« في بوليفيا.\n", -1);
579       gtk_text_buffer_get_iter_at_mark (buffer, &iter2, temp_mark);
580       gtk_text_buffer_apply_tag_by_name (buffer, "rtl_quote", &iter2, &iter);
581           
582       ++i;
583     }
584
585   gdk_pixmap_unref (pixmap);
586   if (mask)
587     gdk_bitmap_unref (mask);
588   
589   printf ("%d lines %d chars\n",
590          gtk_text_buffer_get_line_count (buffer),
591          gtk_text_buffer_get_char_count (buffer));
592
593   gtk_text_buffer_set_modified (buffer, FALSE);
594 }
595
596 gboolean
597 fill_file_buffer (GtkTextBuffer *buffer, const char *filename)
598 {
599   FILE* f;
600   gchar buf[2048];
601   gint remaining = 0;
602   GtkTextIter iter, end;
603
604   f = fopen (filename, "r");
605   
606   if (f == NULL)
607     {
608       gchar *err = g_strdup_printf ("Cannot open file '%s': %s",
609                                     filename, g_strerror (errno));
610       msgbox_run (NULL, err, "OK", NULL, NULL, 0);
611       g_free (err);
612       return FALSE;
613     }
614   
615   gtk_text_buffer_get_iter_at_offset (buffer, &iter, 0);
616   while (!feof (f))
617     {
618       gint count;
619       char *leftover, *next;
620       int to_read = 2047  - remaining;
621       
622       count = fread (buf + remaining, 1, to_read, f);
623       buf[count + remaining] = '\0';
624
625       leftover = next = buf;
626       while (next)
627         {
628           leftover = next;
629           if (!*leftover)
630             break;
631           
632           next = g_utf8_next_char (next);
633         }
634
635       gtk_text_buffer_insert (buffer, &iter, buf, leftover - buf);
636
637       remaining = buf + remaining + count - leftover;
638       g_memmove (buf, leftover, remaining);
639
640       if (remaining > 6 || count < to_read)
641           break;
642     }
643
644   if (remaining)
645     {
646       gchar *err = g_strdup_printf ("Invalid UTF-8 data encountered reading file '%s'", filename);
647       msgbox_run (NULL, err, "OK", NULL, NULL, 0);
648       g_free (err);
649     }
650   
651   /* We had a newline in the buffer to begin with. (The buffer always contains
652    * a newline, so we delete to the end of the buffer to clean up.
653    */
654   gtk_text_buffer_get_last_iter (buffer, &end);
655   gtk_text_buffer_delete (buffer, &iter, &end);
656   
657   gtk_text_buffer_set_modified (buffer, FALSE);
658
659   return TRUE;
660 }
661
662 static gint
663 delete_event_cb (GtkWidget *window, GdkEventAny *event, gpointer data)
664 {
665   View *view = view_from_widget (window);
666
667   push_active_window (GTK_WINDOW (window));
668   check_close_view (view);
669   pop_active_window ();
670
671   return TRUE;
672 }
673
674 /*
675  * Menu callbacks
676  */
677
678 static View *
679 get_empty_view (View *view)
680 {
681   if (!view->buffer->filename &&
682       !gtk_text_buffer_modified (view->buffer->buffer))
683     return view;
684   else
685     return create_view (create_buffer ());
686 }
687
688 static View *
689 view_from_widget (GtkWidget *widget)
690 {
691   GtkWidget *app;
692
693   if (GTK_IS_MENU_ITEM (widget))
694     {
695       GtkItemFactory *item_factory = gtk_item_factory_from_widget (widget);
696       return gtk_object_get_data (GTK_OBJECT (item_factory), "view");      
697     }
698   else
699     {
700       GtkWidget *app = gtk_widget_get_toplevel (widget);
701       return gtk_object_get_data (GTK_OBJECT (app), "view");
702     }
703 }
704
705 static void
706 do_new (gpointer             callback_data,
707         guint                callback_action,
708         GtkWidget           *widget)
709 {
710   create_view (create_buffer ());
711 }
712
713 static void
714 do_new_view (gpointer             callback_data,
715              guint                callback_action,
716              GtkWidget           *widget)
717 {
718   View *view = view_from_widget (widget);
719   
720   create_view (view->buffer);
721 }
722
723 gboolean
724 open_ok_func (const char *filename, gpointer data)
725 {
726   View *view = data;
727   View *new_view = get_empty_view (view);
728
729   if (!fill_file_buffer (new_view->buffer->buffer, filename))
730     {
731       if (new_view != view)
732         close_view (new_view);
733       return FALSE;
734     }
735   else
736     {
737       g_free (new_view->buffer->filename);
738       new_view->buffer->filename = g_strdup (filename);
739       buffer_filename_set (new_view->buffer);
740       
741       return TRUE;
742     }
743 }
744
745 static void
746 do_open (gpointer             callback_data,
747          guint                callback_action,
748          GtkWidget           *widget)
749 {
750   View *view = view_from_widget (widget);
751
752   push_active_window (GTK_WINDOW (view->window));
753   filesel_run (NULL, "Open File", NULL, open_ok_func, view);
754   pop_active_window ();
755 }
756
757 static void
758 do_save_as (gpointer             callback_data,
759             guint                callback_action,
760             GtkWidget           *widget)
761 {
762   View *view = view_from_widget (widget);  
763
764   push_active_window (GTK_WINDOW (view->window));
765   save_as_buffer (view->buffer);
766   pop_active_window ();
767 }
768
769 static void
770 do_save (gpointer             callback_data,
771          guint                callback_action,
772          GtkWidget           *widget)
773 {
774   View *view = view_from_widget (widget);
775
776   push_active_window (GTK_WINDOW (view->window));
777   if (!view->buffer->filename)
778     do_save_as (callback_data, callback_action, widget);
779   else
780     save_buffer (view->buffer);
781   pop_active_window ();
782 }
783
784 static void
785 do_close   (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   check_close_view (view);
793   pop_active_window ();
794 }
795
796 static void
797 do_exit    (gpointer             callback_data,
798             guint                callback_action,
799             GtkWidget           *widget)
800 {
801   View *view = view_from_widget (widget);
802
803   GSList *tmp_list = buffers;
804
805   push_active_window (GTK_WINDOW (view->window));
806   while (tmp_list)
807     {
808       if (!check_buffer_saved (tmp_list->data))
809         return;
810
811       tmp_list = tmp_list->next;
812     }
813
814   gtk_main_quit ();
815   pop_active_window ();
816 }
817
818 static void
819 do_example (gpointer             callback_data,
820             guint                callback_action,
821             GtkWidget           *widget)
822 {
823   View *view = view_from_widget (widget);
824   View *new_view;
825
826   new_view = get_empty_view (view);
827   
828   fill_example_buffer (new_view->buffer->buffer);
829 }
830
831 static void
832 do_wrap_changed (gpointer             callback_data,
833                  guint                callback_action,
834                  GtkWidget           *widget)
835 {
836   View *view = view_from_widget (widget);
837
838   gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (view->text_view), callback_action);
839 }
840
841 static void
842 do_direction_changed (gpointer             callback_data,
843                       guint                callback_action,
844                       GtkWidget           *widget)
845 {
846   View *view = view_from_widget (widget);
847   
848   gtk_widget_set_direction (view->text_view, callback_action);
849   gtk_widget_queue_resize (view->text_view);
850 }
851
852 static void
853 do_editable_changed (gpointer callback_data,
854                      guint callback_action,
855                      GtkWidget *widget)
856 {
857   View *view = view_from_widget (widget);
858
859   gtk_text_view_set_editable (GTK_TEXT_VIEW (view->text_view), callback_action);
860 }
861
862 static void
863 do_cursor_visible_changed (gpointer callback_data,
864                            guint callback_action,
865                            GtkWidget *widget)
866 {
867   View *view = view_from_widget (widget);
868
869   gtk_text_view_set_cursor_visible (GTK_TEXT_VIEW (view->text_view), callback_action);
870 }
871
872 static void
873 do_apply_editable (gpointer callback_data,
874                    guint callback_action,
875                    GtkWidget *widget)
876 {
877   View *view = view_from_widget (widget);
878   GtkTextIter start;
879   GtkTextIter end;
880   
881   if (gtk_text_buffer_get_selection_bounds (view->buffer->buffer,
882                                             &start, &end))
883     {
884       if (callback_action)
885         {
886           gtk_text_buffer_remove_tag (view->buffer->buffer,
887                                       view->buffer->not_editable_tag,
888                                       &start, &end);
889         }
890       else
891         {
892           gtk_text_buffer_apply_tag (view->buffer->buffer,
893                                      view->buffer->not_editable_tag,
894                                      &start, &end);
895         }
896     }
897 }
898
899
900 static void
901 do_apply_tabs (gpointer callback_data,
902                guint callback_action,
903                GtkWidget *widget)
904 {
905   View *view = view_from_widget (widget);
906   GtkTextIter start;
907   GtkTextIter end;
908   
909   if (gtk_text_buffer_get_selection_bounds (view->buffer->buffer,
910                                             &start, &end))
911     {
912       if (callback_action)
913         {
914           gtk_text_buffer_remove_tag (view->buffer->buffer,
915                                       view->buffer->custom_tabs_tag,
916                                       &start, &end);
917         }
918       else
919         {
920           gtk_text_buffer_apply_tag (view->buffer->buffer,
921                                      view->buffer->custom_tabs_tag,
922                                      &start, &end);
923         }
924     }
925 }
926
927 static void
928 dialog_response_callback (GtkWidget *dialog, gint response_id, gpointer data)
929 {
930   GtkTextBuffer *buffer;
931   View *view = data;
932   GtkTextIter start, end;
933   gchar *search_string;
934   
935   buffer = gtk_object_get_data (GTK_OBJECT (dialog), "buffer");
936
937   gtk_text_buffer_get_bounds (buffer, &start, &end);
938
939   /* Remove trailing newline */
940   gtk_text_iter_prev_char (&end);
941   
942   search_string = gtk_text_iter_get_text (&start, &end);
943
944   printf ("Searching for `%s'\n", search_string);
945   
946   buffer_search_forward (view->buffer, search_string, view);
947
948   g_free (search_string);
949   
950   gtk_widget_destroy (dialog);
951 }
952
953 static void
954 do_search (gpointer callback_data,
955            guint callback_action,
956            GtkWidget *widget)
957 {
958   View *view = view_from_widget (widget);
959   GtkWidget *dialog;
960   GtkWidget *search_text;
961   GtkTextBuffer *buffer;
962
963 #if 0
964   
965   dialog = gtk_dialog_new_with_buttons ("Search",
966                                         GTK_WINDOW (view->window),
967                                         GTK_DIALOG_DESTROY_WITH_PARENT,
968                                         GTK_STOCK_BUTTON_CLOSE,
969                                         GTK_RESPONSE_NONE, NULL);
970
971   buffer = gtk_text_buffer_new (NULL);
972
973   /* FIXME memory leak once buffer is a GObject */
974   search_text = gtk_text_view_new_with_buffer (buffer);
975
976   gtk_box_pack_end (GTK_BOX (GTK_DIALOG (dialog)->vbox),
977                     search_text,
978                     TRUE, TRUE, 0);
979
980   gtk_object_set_data (GTK_OBJECT (dialog), "buffer", buffer);
981   
982   gtk_signal_connect (GTK_OBJECT (dialog),
983                       "response",
984                       GTK_SIGNAL_FUNC (dialog_response_callback),
985                       view);
986
987   gtk_widget_show (search_text);
988
989   gtk_widget_grab_focus (search_text);
990   
991   gtk_widget_show_all (dialog);
992 #endif
993 }
994
995 static void
996 view_init_menus (View *view)
997 {
998   GtkTextDirection direction = gtk_widget_get_direction (view->text_view);
999   GtkWrapMode wrap_mode = gtk_text_view_get_wrap_mode (GTK_TEXT_VIEW (view->text_view));
1000   GtkWidget *menu_item = NULL;
1001
1002   switch (direction)
1003     {
1004     case GTK_TEXT_DIR_LTR:
1005       menu_item = gtk_item_factory_get_widget (view->item_factory, "/Settings/Left-to-Right");
1006       break;
1007     case GTK_TEXT_DIR_RTL:
1008       menu_item = gtk_item_factory_get_widget (view->item_factory, "/Settings/Right-to-Left");
1009       break;
1010     default:
1011       break;
1012     }
1013
1014   if (menu_item)
1015     gtk_menu_item_activate (GTK_MENU_ITEM (menu_item));
1016
1017   switch (wrap_mode)
1018     {
1019     case GTK_WRAPMODE_NONE:
1020       menu_item = gtk_item_factory_get_widget (view->item_factory, "/Settings/Wrap Off");
1021       break;
1022     case GTK_WRAPMODE_WORD:
1023       menu_item = gtk_item_factory_get_widget (view->item_factory, "/Settings/Wrap Words");
1024       break;
1025     default:
1026       break;
1027     }
1028   
1029   if (menu_item)
1030     gtk_menu_item_activate (GTK_MENU_ITEM (menu_item));
1031 }
1032
1033 static GtkItemFactoryEntry menu_items[] =
1034 {
1035   { "/_File",            NULL,         0,           0, "<Branch>" },
1036   { "/File/_New",        "<control>N", do_new,      0, NULL },
1037   { "/File/New _View",   NULL,         do_new_view, 0, NULL },
1038   { "/File/_Open",       "<control>O", do_open,     0, NULL },
1039   { "/File/_Save",       "<control>S", do_save,     0, NULL },
1040   { "/File/Save _As...", NULL,         do_save_as,  0, NULL },
1041   { "/File/sep1",        NULL,         0,           0, "<Separator>" },
1042   { "/File/_Close",     "<control>W" , do_close,    0, NULL },
1043   { "/File/E_xit",      "<control>Q" , do_exit,     0, NULL },
1044
1045   { "/_Edit", NULL, 0, 0, "<Branch>" },
1046   { "/Edit/Find...", NULL, do_search, 0, NULL },
1047
1048   { "/_Settings",         NULL,         0,                0, "<Branch>" },
1049   { "/Settings/Wrap _Off",   NULL,      do_wrap_changed,  GTK_WRAPMODE_NONE, "<RadioItem>" },
1050   { "/Settings/Wrap _Words", NULL,      do_wrap_changed,  GTK_WRAPMODE_WORD, "/Settings/Wrap Off" },
1051   { "/Settings/sep1",        NULL,      0,                0, "<Separator>" },
1052   { "/Settings/Editable", NULL,      do_editable_changed,  TRUE, "<RadioItem>" },
1053   { "/Settings/Not editable",    NULL,      do_editable_changed,  FALSE, "/Settings/Editable" },
1054   { "/Settings/sep1",        NULL,      0,                0, "<Separator>" },
1055
1056   { "/Settings/Cursor visible",    NULL,      do_cursor_visible_changed,  TRUE, "<RadioItem>" },
1057   { "/Settings/Cursor not visible", NULL,      do_cursor_visible_changed,  FALSE, "/Settings/Cursor visible" },
1058   { "/Settings/sep1",        NULL,      0,                0, "<Separator>" },
1059   
1060   { "/Settings/Left-to-Right", NULL,    do_direction_changed,  GTK_TEXT_DIR_LTR, "<RadioItem>" },
1061   { "/Settings/Right-to-Left", NULL,    do_direction_changed,  GTK_TEXT_DIR_RTL, "/Settings/Left-to-Right" },
1062   { "/_Attributes",       NULL,         0,                0, "<Branch>" },
1063   { "/Attributes/Editable",       NULL,         do_apply_editable, TRUE, NULL },
1064   { "/Attributes/Not editable",           NULL,         do_apply_editable, FALSE, NULL },
1065   { "/Attributes/Custom tabs",            NULL,         do_apply_tabs, FALSE, NULL },
1066   { "/Attributes/Default tabs",           NULL,         do_apply_tabs, TRUE, NULL },
1067   { "/_Test",            NULL,         0,           0, "<Branch>" },
1068   { "/Test/_Example",    NULL,         do_example,  0, NULL },
1069 };
1070
1071 static gboolean
1072 save_buffer (Buffer *buffer)
1073 {
1074   GtkTextIter start, end;
1075   gchar *chars;
1076   gboolean result = FALSE;
1077   gboolean have_backup = FALSE;
1078   gchar *bak_filename;
1079   FILE *file;
1080
1081   g_return_val_if_fail (buffer->filename != NULL, FALSE);
1082
1083   bak_filename = g_strconcat (buffer->filename, "~", NULL);
1084   
1085   if (rename (buffer->filename, bak_filename) != 0)
1086     {
1087       if (errno != ENOENT)
1088         {
1089           gchar *err = g_strdup_printf ("Cannot back up '%s' to '%s': %s",
1090                                         buffer->filename, bak_filename, g_strerror (errno));
1091           msgbox_run (NULL, err, "OK", NULL, NULL, 0);
1092           g_free (err);
1093         }
1094       
1095       return FALSE;
1096     }
1097   else
1098     have_backup = TRUE;
1099   
1100   file = fopen (buffer->filename, "w");
1101   if (!file)
1102     {
1103       gchar *err = g_strdup_printf ("Cannot back up '%s' to '%s': %s",
1104                                     buffer->filename, bak_filename, g_strerror (errno));
1105       msgbox_run (NULL, err, "OK", NULL, NULL, 0);
1106     }
1107   else
1108     {
1109       gtk_text_buffer_get_iter_at_offset (buffer->buffer, &start, 0);
1110       gtk_text_buffer_get_last_iter (buffer->buffer, &end);
1111   
1112       chars = gtk_text_buffer_get_slice (buffer->buffer, &start, &end, FALSE);
1113
1114       if (fputs (chars, file) == EOF ||
1115           fclose (file) == EOF)
1116         {
1117           gchar *err = g_strdup_printf ("Error writing to '%s': %s",
1118                                         buffer->filename, g_strerror (errno));
1119           msgbox_run (NULL, err, "OK", NULL, NULL, 0);
1120           g_free (err);
1121         }
1122       else
1123         {
1124           /* Success
1125            */
1126           result = TRUE;
1127           gtk_text_buffer_set_modified (buffer->buffer, FALSE);   
1128         }
1129         
1130       g_free (chars);
1131     }
1132
1133   if (!result && have_backup)
1134     {
1135       if (rename (bak_filename, buffer->filename) != 0)
1136         {
1137           gchar *err = g_strdup_printf ("Error restoring backup file '%s' to '%s': %s\nBackup left as '%s'",
1138                                         buffer->filename, bak_filename, g_strerror (errno), bak_filename);
1139           msgbox_run (NULL, err, "OK", NULL, NULL, 0);
1140           g_free (err);
1141         }
1142     }
1143
1144   g_free (bak_filename);
1145   
1146   return result;
1147 }
1148
1149 static gboolean
1150 save_as_ok_func (const char *filename, gpointer data)
1151 {
1152   Buffer *buffer = data;
1153   char *old_filename = buffer->filename;
1154
1155   if (!buffer->filename || strcmp (filename, buffer->filename) != 0)
1156     {
1157       struct stat statbuf;
1158
1159       if (stat (filename, &statbuf) == 0)
1160         {
1161           gchar *err = g_strdup_printf ("Ovewrite existing file '%s'?", filename);
1162           gint result = msgbox_run (NULL, err, "Yes", "No", NULL, 1);
1163           g_free (err);
1164
1165           if (result != 0)
1166             return FALSE;
1167         }
1168     }
1169   
1170   buffer->filename = g_strdup (filename);
1171
1172   if (save_buffer (buffer))
1173     {
1174       g_free (old_filename);
1175       buffer_filename_set (buffer);
1176       return TRUE;
1177     }
1178   else
1179     {
1180       g_free (buffer->filename);
1181       buffer->filename = old_filename;
1182       return FALSE;
1183     }
1184 }
1185
1186 static gboolean
1187 save_as_buffer (Buffer *buffer)
1188 {
1189   return filesel_run (NULL, "Save File", NULL, save_as_ok_func, buffer);
1190 }
1191
1192 static gboolean
1193 check_buffer_saved (Buffer *buffer)
1194 {
1195   if (gtk_text_buffer_modified (buffer->buffer))
1196     {
1197       char *pretty_name = buffer_pretty_name (buffer);
1198       char *msg = g_strdup_printf ("Save changes to '%s'?", pretty_name);
1199       gint result;
1200       
1201       g_free (pretty_name);
1202       
1203       result = msgbox_run (NULL, msg, "Yes", "No", "Cancel", 0);
1204       g_free (msg);
1205   
1206       if (result == 0)
1207         return save_as_buffer (buffer);
1208       else if (result == 1)
1209         return TRUE;
1210       else
1211         return FALSE;
1212     }
1213   else
1214     return TRUE;
1215 }
1216
1217 static Buffer *
1218 create_buffer (void)
1219 {
1220   Buffer *buffer;
1221   PangoTabArray *tabs;
1222   
1223   buffer = g_new (Buffer, 1);
1224
1225   buffer->buffer = gtk_text_buffer_new (NULL);
1226   gtk_object_ref (GTK_OBJECT (buffer->buffer));
1227   gtk_object_sink (GTK_OBJECT (buffer->buffer));
1228   
1229   buffer->refcount = 1;
1230   buffer->filename = NULL;
1231   buffer->untitled_serial = -1;
1232
1233   buffer->not_editable_tag = gtk_text_buffer_create_tag (buffer->buffer, NULL);
1234   gtk_object_set (GTK_OBJECT (buffer->not_editable_tag),
1235                   "editable", FALSE,
1236                   "foreground", "purple", NULL);
1237
1238   buffer->found_text_tag = gtk_text_buffer_create_tag (buffer->buffer, NULL);
1239   gtk_object_set (GTK_OBJECT (buffer->found_text_tag),
1240                   "foreground", "red", NULL);
1241
1242   tabs = pango_tab_array_new_with_positions (4,
1243                                              TRUE,
1244                                              PANGO_TAB_LEFT, 10,
1245                                              PANGO_TAB_LEFT, 30,
1246                                              PANGO_TAB_LEFT, 60,
1247                                              PANGO_TAB_LEFT, 120);
1248   
1249   buffer->custom_tabs_tag = gtk_text_buffer_create_tag (buffer->buffer, NULL);
1250   gtk_object_set (GTK_OBJECT (buffer->custom_tabs_tag),
1251                   "tabs", tabs,
1252                   "foreground", "green", NULL);
1253
1254   pango_tab_array_free (tabs);
1255   
1256   buffers = g_slist_prepend (buffers, buffer);
1257   
1258   return buffer;
1259 }
1260
1261 static char *
1262 buffer_pretty_name (Buffer *buffer)
1263 {
1264   if (buffer->filename)
1265     {
1266       char *p;
1267       char *result = g_path_get_basename (buffer->filename);
1268       p = strchr (result, '/');
1269       if (p)
1270         *p = '\0';
1271
1272       return result;
1273     }
1274   else
1275     {
1276       if (buffer->untitled_serial == -1)
1277         buffer->untitled_serial = untitled_serial++;
1278
1279       if (buffer->untitled_serial == 1)
1280         return g_strdup ("Untitled");
1281       else
1282         return g_strdup_printf ("Untitled #%d", buffer->untitled_serial);
1283     }
1284 }
1285
1286 static void
1287 buffer_filename_set (Buffer *buffer)
1288 {
1289   GSList *tmp_list = views;
1290
1291   while (tmp_list)
1292     {
1293       View *view = tmp_list->data;
1294
1295       if (view->buffer == buffer)
1296         view_set_title (view);
1297
1298       tmp_list = tmp_list->next;
1299     }
1300 }
1301
1302 static void
1303 buffer_search_forward (Buffer *buffer, const char *str,
1304                        View *view)
1305 {
1306   GtkTextIter iter;
1307   GtkTextIter start, end;
1308   gint char_len;
1309   int i = 0;
1310   GtkWidget *dialog;
1311   
1312   /* remove tag from whole buffer */
1313   gtk_text_buffer_get_bounds (buffer->buffer, &start, &end);
1314   gtk_text_buffer_remove_tag (buffer->buffer,  buffer->found_text_tag,
1315                               &start, &end );
1316   
1317   gtk_text_buffer_get_iter_at_mark (buffer->buffer, &iter,
1318                                     gtk_text_buffer_get_mark (buffer->buffer,
1319                                                               "insert"));
1320
1321
1322   char_len = g_utf8_strlen (str, -1);
1323
1324   if (char_len > 0)
1325     {
1326       while (gtk_text_iter_forward_search (&iter, str, TRUE, FALSE))
1327         {
1328           GtkTextIter end = iter;
1329           
1330           gtk_text_iter_forward_chars (&end, char_len);
1331           
1332           gtk_text_buffer_apply_tag (buffer->buffer, buffer->found_text_tag,
1333                                      &iter, &end);
1334
1335           iter = end;
1336           
1337           ++i;
1338         }
1339     }
1340
1341 #if 0  
1342   dialog = gtk_message_dialog_new (GTK_WINDOW (view->window),
1343                                    GTK_MESSAGE_INFO,
1344                                    GTK_BUTTONS_OK,
1345                                    GTK_DIALOG_DESTROY_WITH_PARENT,
1346                                    "%d strings found and marked in red",
1347                                    i);
1348
1349   gtk_signal_connect_object (GTK_OBJECT (dialog),
1350                              "response",
1351                              GTK_SIGNAL_FUNC (gtk_widget_destroy),
1352                              GTK_OBJECT (dialog));
1353   
1354   gtk_widget_show (dialog);
1355 #endif
1356 }
1357
1358 static void
1359 buffer_ref (Buffer *buffer)
1360 {
1361   buffer->refcount++;
1362 }
1363
1364 static void
1365 buffer_unref (Buffer *buffer)
1366 {
1367   buffer->refcount--;
1368   if (buffer->refcount == 0)
1369     {
1370       buffers = g_slist_remove (buffers, buffer);
1371       gtk_object_unref (GTK_OBJECT (buffer->buffer));
1372       g_free (buffer->filename);
1373       g_free (buffer);
1374     }
1375 }
1376
1377 static void
1378 close_view (View *view)
1379 {
1380   views = g_slist_remove (views, view);
1381   buffer_unref (view->buffer);
1382   gtk_widget_destroy (view->window);
1383   g_object_unref (G_OBJECT (view->item_factory));
1384   
1385   g_free (view);
1386   
1387   if (!views)
1388     gtk_main_quit ();
1389 }
1390
1391 static void
1392 check_close_view (View *view)
1393 {
1394   if (view->buffer->refcount > 1 ||
1395       check_buffer_saved (view->buffer))
1396     close_view (view);
1397 }
1398
1399 static void
1400 view_set_title (View *view)
1401 {
1402   char *pretty_name = buffer_pretty_name (view->buffer);
1403   char *title = g_strconcat ("testtext - ", pretty_name, NULL);
1404
1405   gtk_window_set_title (GTK_WINDOW (view->window), title);
1406
1407   g_free (pretty_name);
1408   g_free (title);
1409 }
1410
1411 static void
1412 get_lines (GtkTextView  *text_view,
1413            gint          first_y,
1414            gint          last_y,
1415            gint        **buffer_coords,
1416            gint        **numbers,
1417            gint         *countp)
1418 {
1419   GtkTextIter iter;
1420   gint count;
1421   gint size;
1422   
1423   if (buffer_coords)
1424     *buffer_coords = NULL;
1425
1426   if (numbers)
1427     *numbers = NULL;
1428   
1429   /* Get iter at first y */
1430   gtk_text_view_get_iter_at_location (text_view, &iter, 0, first_y);
1431
1432   /* Move back to start of its paragraph */
1433   gtk_text_iter_set_line_offset (&iter, 0);
1434
1435   /* For each iter, get its location and add it to the arrays.
1436    * Stop when we pass last_y
1437    */
1438   count = 0;
1439   size = 0;
1440
1441   while (!gtk_text_iter_is_last (&iter))
1442     {
1443       GdkRectangle loc;
1444
1445       gtk_text_view_get_iter_location (text_view, &iter, &loc);
1446
1447       if (loc.y >= last_y)
1448         break;
1449
1450       if (count >= size)
1451         {
1452           size = 2 * size + 2; /* + 2 handles size == 0 case */
1453           
1454           if (buffer_coords)
1455             *buffer_coords = g_realloc (*buffer_coords,
1456                                         size * sizeof (gint));
1457
1458           if (numbers)
1459             *numbers = g_realloc (*numbers,
1460                                   size * sizeof (gint));
1461         }
1462
1463       if (buffer_coords)
1464         (*buffer_coords)[count] = loc.y;
1465
1466       if (numbers)
1467         (*numbers)[count] = gtk_text_iter_get_line (&iter);
1468       
1469       ++count;
1470
1471       gtk_text_iter_forward_line (&iter);
1472     }
1473
1474   *countp = count;
1475 }
1476
1477 static gint
1478 line_numbers_expose (GtkWidget      *widget,
1479                      GdkEventExpose *event,
1480                      gpointer        user_data)
1481 {
1482   gint count;
1483   gint *numbers = NULL;
1484   gint *pixels = NULL;
1485   gint first_y;
1486   gint last_y;
1487   gint i;
1488   GdkWindow *left_win;
1489   PangoLayout *layout;
1490   GtkTextView *text_view;
1491
1492   text_view = GTK_TEXT_VIEW (widget);
1493   
1494   /* See if this expose is on the line numbers window */
1495   left_win = gtk_text_view_get_window (text_view,
1496                                        GTK_TEXT_WINDOW_LEFT);
1497
1498   if (event->window != left_win)
1499     return FALSE;
1500   
1501   first_y = event->area.y;
1502   last_y = first_y + event->area.height;
1503
1504   gtk_text_view_window_to_buffer_coords (text_view,
1505                                          GTK_TEXT_WINDOW_LEFT,
1506                                          first_y,
1507                                          last_y,
1508                                          &first_y,
1509                                          &last_y);
1510
1511   get_lines (text_view,
1512              first_y,
1513              last_y,
1514              &pixels,
1515              &numbers,
1516              &count);
1517
1518
1519   /* Draw fully internationalized numbers! */
1520   
1521   layout = gtk_widget_create_pango_layout (widget, "");
1522   
1523   i = 0;
1524   while (i < count)
1525     {
1526       gint pos;
1527       gchar *str;
1528       
1529       gtk_text_view_buffer_to_window_coords (text_view,
1530                                              GTK_TEXT_WINDOW_LEFT,
1531                                              0,
1532                                              pixels[i],
1533                                              NULL,
1534                                              &pos);
1535
1536       str = g_strdup_printf ("%d", numbers[i]);
1537
1538       pango_layout_set_text (layout, str, -1);
1539       
1540       gdk_draw_layout (left_win,
1541                        widget->style->fg_gc [widget->state],
1542                        /* 2 is just a random padding */
1543                        2, pos + 2,
1544                        layout);
1545
1546       g_free (str);
1547       
1548       ++i;
1549     }
1550
1551   g_free (pixels);
1552   g_free (numbers);
1553   
1554   g_object_unref (G_OBJECT (layout));
1555
1556   return TRUE;
1557 }
1558
1559 static View *
1560 create_view (Buffer *buffer)
1561 {
1562   View *view;
1563   
1564   GtkWidget *sw;
1565   GtkWidget *vbox;
1566
1567   view = g_new0 (View, 1);
1568   views = g_slist_prepend (views, view);
1569
1570   view->buffer = buffer;
1571   buffer_ref (buffer);
1572   
1573   view->window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
1574   gtk_object_set_data (GTK_OBJECT (view->window), "view", view);
1575   
1576   gtk_signal_connect (GTK_OBJECT (view->window), "delete_event",
1577                       GTK_SIGNAL_FUNC (delete_event_cb), NULL);
1578
1579   view->accel_group = gtk_accel_group_new ();
1580   view->item_factory = gtk_item_factory_new (GTK_TYPE_MENU_BAR, "<main>", view->accel_group);
1581   gtk_object_set_data (GTK_OBJECT (view->item_factory), "view", view);
1582   
1583   gtk_item_factory_create_items (view->item_factory, G_N_ELEMENTS (menu_items), menu_items, view);
1584
1585   gtk_window_add_accel_group (GTK_WINDOW (view->window), view->accel_group);
1586
1587   vbox = gtk_vbox_new (FALSE, 0);
1588   gtk_container_add (GTK_CONTAINER (view->window), vbox);
1589
1590   gtk_box_pack_start (GTK_BOX (vbox),
1591                       gtk_item_factory_get_widget (view->item_factory, "<main>"),
1592                       FALSE, FALSE, 0);
1593   
1594   sw = gtk_scrolled_window_new (NULL, NULL);
1595   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
1596                                  GTK_POLICY_AUTOMATIC,
1597                                  GTK_POLICY_AUTOMATIC);
1598
1599   view->text_view = gtk_text_view_new_with_buffer (buffer->buffer);
1600   gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (view->text_view),
1601                                GTK_WRAPMODE_WORD);
1602
1603
1604   /* Set sizes on these windows, just for debugging */
1605   
1606   gtk_text_view_set_right_window_width (GTK_TEXT_VIEW (view->text_view),
1607                                        30);
1608   
1609   gtk_text_view_set_top_window_height (GTK_TEXT_VIEW (view->text_view),
1610                                        15);
1611
1612   gtk_text_view_set_bottom_window_height (GTK_TEXT_VIEW (view->text_view),
1613                                           23);
1614
1615   /* Draw line numbers in the left window; we should really be
1616    * more scientific about what width we set it to.
1617    */
1618   gtk_text_view_set_left_window_width (GTK_TEXT_VIEW (view->text_view),
1619                                        30);
1620   
1621   gtk_signal_connect (GTK_OBJECT (view->text_view),
1622                       "expose_event",
1623                       GTK_SIGNAL_FUNC (line_numbers_expose),
1624                       NULL);
1625   
1626   gtk_box_pack_start (GTK_BOX (vbox), sw, TRUE, TRUE, 0);
1627   gtk_container_add (GTK_CONTAINER (sw), view->text_view);
1628
1629   gtk_window_set_default_size (GTK_WINDOW (view->window), 500, 500);
1630
1631   gtk_widget_grab_focus (view->text_view);
1632
1633   view_set_title (view);
1634   view_init_menus (view);
1635   
1636   gtk_widget_show_all (view->window);
1637   return view;
1638 }
1639
1640 int
1641 main (int argc, char** argv)
1642 {
1643   Buffer *buffer;
1644   View *view;
1645   int i;
1646   
1647   gtk_init (&argc, &argv);
1648   gdk_rgb_init (); /* FIXME remove this */
1649   
1650   buffer = create_buffer ();
1651   view = create_view (buffer);
1652   buffer_unref (buffer);
1653   
1654   push_active_window (GTK_WINDOW (view->window));
1655   for (i=1; i < argc; i++)
1656     {
1657       char *filename;
1658
1659       /* Quick and dirty canonicalization - better should be in GLib
1660        */
1661
1662       if (!g_path_is_absolute (argv[i]))
1663         {
1664           char *cwd = g_get_current_dir ();
1665           filename = g_strconcat (cwd, "/", argv[i], NULL);
1666           g_free (cwd);
1667         }
1668       else
1669         filename = argv[i];
1670
1671       open_ok_func (filename, view);
1672
1673       if (filename != argv[i])
1674         g_free (filename);
1675     }
1676   pop_active_window ();
1677   
1678   gtk_main ();
1679
1680   return 0;
1681 }
1682
1683