]> Pileus Git - ~andy/gtk/blob - gtk/gtkmain.c
b5b6dea791e2aa54baaf0b0806d53fd7714fb319
[~andy/gtk] / gtk / gtkmain.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the Free
16  * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17  */
18 #include <X11/Xlocale.h>        /* so we get the right setlocale */
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include "gtkbutton.h"
22 #include "gtkhscrollbar.h"
23 #include "gtkhseparator.h"
24 #include "gtkmain.h"
25 #include "gtkpreview.h"
26 #include "gtkrc.h"
27 #include "gtkselection.h"
28 #include "gtksignal.h"
29 #include "gtktable.h"
30 #include "gtktext.h"
31 #include "gtkvbox.h"
32 #include "gtkvscrollbar.h"
33 #include "gtkwidget.h"
34 #include "gtkwindow.h"
35 #include "gtkprivate.h"
36 #include "gdk/gdki18n.h"
37 #include "../config.h"
38 #include "gtkdebug.h"
39
40
41 /* Private type definitions
42  */
43 typedef struct _GtkInitFunction          GtkInitFunction;
44 typedef struct _GtkQuitFunction          GtkQuitFunction;
45 typedef struct _GtkTimeoutFunction       GtkTimeoutFunction;
46 typedef struct _GtkIdleFunction          GtkIdleFunction;
47 typedef struct _GtkInputFunction         GtkInputFunction;
48 typedef struct _GtkKeySnooperData        GtkKeySnooperData;
49
50 struct _GtkInitFunction
51 {
52   GtkFunction function;
53   gpointer data;
54 };
55
56 struct _GtkQuitFunction
57 {
58   guint id;
59   guint main_level;
60   GtkCallbackMarshal marshal;
61   GtkFunction function;
62   gpointer data;
63   GtkDestroyNotify destroy;
64 };
65
66 struct _GtkTimeoutFunction
67 {
68   guint tag;
69   guint32 start;
70   guint32 interval;
71   guint32 originterval;
72   GtkFunction function;
73   GtkCallbackMarshal marshal;
74   gpointer data;
75   GtkDestroyNotify destroy;
76 };
77
78 struct _GtkIdleFunction
79 {
80   guint tag;
81   gint priority;
82   GtkCallbackMarshal marshal;
83   GtkFunction function;
84   gpointer data;
85   GtkDestroyNotify destroy;
86 };
87
88 struct _GtkInputFunction
89 {
90   GtkCallbackMarshal callback;
91   gpointer data;
92   GtkDestroyNotify destroy;
93 };
94
95 struct _GtkKeySnooperData
96 {
97   GtkKeySnoopFunc func;
98   gpointer func_data;
99   guint id;
100 };
101
102 static void  gtk_exit_func               (void);
103 static gint  gtk_quit_invoke_function    (GtkQuitFunction    *quitf);
104 static void  gtk_quit_destroy            (GtkQuitFunction    *quitf);
105 static void  gtk_timeout_insert          (GtkTimeoutFunction *timeoutf);
106 static void  gtk_handle_current_timeouts (guint32             the_time);
107 static void  gtk_handle_current_idles    (void);
108 static gint  gtk_invoke_key_snoopers     (GtkWidget          *grab_widget,
109                                           GdkEvent           *event);
110 static void  gtk_handle_timeouts         (void);
111 static void  gtk_handle_idle             (void);
112 static void  gtk_handle_timer            (void);
113 static void  gtk_propagate_event         (GtkWidget          *widget,
114                                           GdkEvent           *event);
115 static void  gtk_error                   (gchar              *str);
116 static void  gtk_warning                 (gchar              *str);
117 static void  gtk_message                 (gchar              *str);
118 static void  gtk_print                   (gchar              *str);
119
120 static gint  gtk_idle_compare            (gpointer            a, 
121                                           gpointer            b);
122
123 static gint  gtk_timeout_compare         (gpointer            a, 
124                                           gpointer            b);
125
126 const guint gtk_major_version = GTK_MAJOR_VERSION;
127 const guint gtk_minor_version = GTK_MINOR_VERSION;
128 const guint gtk_micro_version = GTK_MICRO_VERSION;
129
130 static gboolean iteration_done = FALSE;
131 static guint main_level = 0;
132 static gint initialized = FALSE;
133 static GdkEvent *next_event = NULL;
134 static GList *current_events = NULL;
135
136 static GSList *grabs = NULL;               /* A stack of unique grabs. The grabbing
137                                             *  widget is the first one on the list.
138                                             */
139 static GList *init_functions = NULL;       /* A list of init functions.
140                                             */
141 static GList *quit_functions = NULL;       /* A list of quit functions.
142                                             */
143 static GList *timeout_functions = NULL;    /* A list of timeout functions sorted by
144                                             *  when the length of the time interval
145                                             *  remaining. Therefore, the first timeout
146                                             *  function to expire is at the head of
147                                             *  the list and the last to expire is at
148                                             *  the tail of the list.
149                                             */
150 static GList *idle_functions = NULL;       /* A list of idle functions.
151                                             */
152
153 static GList *current_idles = NULL;
154 static GList *current_timeouts = NULL;
155 static GtkIdleFunction    *running_idle = NULL;
156 static GtkTimeoutFunction *running_timeout = NULL;
157 static GMemChunk *timeout_mem_chunk = NULL;
158 static GMemChunk *idle_mem_chunk = NULL;
159 static GMemChunk *quit_mem_chunk = NULL;
160
161 static GSList *key_snoopers = NULL;
162
163 static GdkVisual *gtk_visual;              /* The visual to be used in creating new
164                                             *  widgets.
165                                             */
166 static GdkColormap *gtk_colormap;          /* The colormap to be used in creating new
167                                             *  widgets.
168                                             */
169
170 guint gtk_debug_flags = 0;                 /* Global GTK debug flag */
171
172 #ifdef G_ENABLE_DEBUG
173 static GDebugKey gtk_debug_keys[] = {
174   {"objects", GTK_DEBUG_OBJECTS},
175   {"misc", GTK_DEBUG_MISC}
176 };
177
178 static const guint gtk_ndebug_keys = sizeof (gtk_debug_keys) / sizeof (GDebugKey);
179
180 #endif /* G_ENABLE_DEBUG */
181
182 gint gtk_use_mb = -1;
183
184 void
185 gtk_init (int    *argc,
186           char ***argv)
187 {
188   gchar *current_locale;
189
190   if (0)
191     {
192       g_set_error_handler (gtk_error);
193       g_set_warning_handler (gtk_warning);
194       g_set_message_handler (gtk_message);
195       g_set_print_handler (gtk_print);
196     }
197   
198   /* Initialize "gdk". We pass along the 'argc' and 'argv'
199    *  parameters as they contain information that GDK uses
200    */
201   gdk_init (argc, argv);
202   
203 #ifdef G_ENABLE_DEBUG
204   {
205     gchar *debug_string = getenv("GTK_DEBUG");
206     if (debug_string != NULL)
207       gtk_debug_flags = g_parse_debug_string (debug_string,
208                                               gtk_debug_keys,
209                                               gtk_ndebug_keys);
210   }
211
212   if (argc && argv)
213     {
214       gint i, j, k;
215       
216       for (i = 1; i < *argc;)
217         {
218           if (strcmp ("--gtk-debug", (*argv)[i]) == 0)
219             {
220               (*argv)[i] = NULL;
221
222               if ((i + 1) < *argc && (*argv)[i + 1])
223                 {
224                   gtk_debug_flags |= g_parse_debug_string ((*argv)[i+1],
225                                                            gtk_debug_keys,
226                                                            gtk_ndebug_keys);
227                   (*argv)[i + 1] = NULL;
228                   i += 1;
229                 }
230             }
231           else if (strcmp ("--gtk-no-debug", (*argv)[i]) == 0)
232             {
233               (*argv)[i] = NULL;
234
235               if ((i + 1) < *argc && (*argv)[i + 1])
236                 {
237                   gtk_debug_flags &= ~g_parse_debug_string ((*argv)[i+1],
238                                                             gtk_debug_keys,
239                                                             gtk_ndebug_keys);
240                   (*argv)[i + 1] = NULL;
241                   i += 1;
242                 }
243             }
244           i += 1;
245         }
246
247       for (i = 1; i < *argc; i++)
248         {
249           for (k = i; k < *argc; k++)
250             if ((*argv)[k] != NULL)
251               break;
252           
253           if (k > i)
254             {
255               k -= i;
256               for (j = i + k; j < *argc; j++)
257                 (*argv)[j-k] = (*argv)[j];
258               *argc -= k;
259             }
260         }
261     }
262
263 #endif /* G_ENABLE_DEBUG */
264
265   /* Check if there is a good chance the mb functions will handle things
266    * correctly - set if either mblen("\xc0", MB_CUR_MAX) == 1 in the
267    * C locale, or were using X's mb functions. (-DX_LOCALE && locale != C)
268    */
269
270   current_locale = g_strdup(setlocale (LC_CTYPE, NULL));
271
272 #ifdef X_LOCALE
273   if ((strcmp (current_locale, "C")) && (strcmp (current_locale, "POSIX")))
274     gtk_use_mb = TRUE;
275   else
276 #endif
277     {
278       setlocale (LC_CTYPE, "C");
279       gtk_use_mb = (mblen ("\xc0", MB_CUR_MAX) == 1);
280       setlocale (LC_CTYPE, current_locale);
281     }
282
283   g_free (current_locale);
284
285   GTK_NOTE(MISC, g_print("%s multi-byte string functions.\n", 
286                           gtk_use_mb ? "Using" : "Not using"));
287
288   /* Initialize the default visual and colormap to be
289    *  used in creating widgets. (We want to use the system
290    *  defaults so as to be nice to the colormap).
291    */
292   gtk_visual = gdk_visual_get_system ();
293   gtk_colormap = gdk_colormap_get_system ();
294   gtk_rc_init ();
295   
296   gtk_type_init ();
297   
298   /* Register an exit function to make sure we are able to cleanup.
299    */
300   if (ATEXIT (gtk_exit_func))
301     g_warning ("unable to register exit function");
302   
303   /* Set the 'initialized' flag.
304    */
305   initialized = TRUE;
306 }
307
308 void
309 gtk_exit (int errorcode)
310 {
311   /* Only if "gtk" has been initialized should we de-initialize.
312    */
313   /* de-initialisation is done by the gtk_exit_funct(),
314    * no need to do this here (Alex J.)
315    */
316   gdk_exit(errorcode);
317 }
318
319 gchar*
320 gtk_set_locale ()
321 {
322   return gdk_set_locale ();
323 }
324
325 void
326 gtk_main ()
327 {
328   GList *tmp_list;
329   GList *functions;
330   GtkInitFunction *init;
331   int old_done;
332   
333   main_level++;
334   
335   tmp_list = functions = init_functions;
336   init_functions = NULL;
337   
338   while (tmp_list)
339     {
340       init = tmp_list->data;
341       tmp_list = tmp_list->next;
342       
343       (* init->function) (init->data);
344       g_free (init);
345     }
346   g_list_free (functions);
347   
348   old_done = iteration_done;
349   while (!gtk_main_iteration ())
350     ;
351   iteration_done = old_done;
352
353   if (quit_functions)
354     {
355       GList *reinvoke_list = NULL;
356       GtkQuitFunction *quitf;
357
358       while (quit_functions)
359         {
360           quitf = quit_functions->data;
361
362           quit_functions = g_list_remove_link (quit_functions, quit_functions);
363
364           if ((quitf->main_level && quitf->main_level != main_level) ||
365               gtk_quit_invoke_function (quitf))
366             {
367               reinvoke_list = g_list_prepend (reinvoke_list, quitf);
368             }
369           else
370             {
371               g_list_free (tmp_list);
372               gtk_quit_destroy (quitf);
373             }
374         }
375       if (reinvoke_list)
376         {
377           GList *tmp_list;
378           
379           tmp_list = g_list_last (reinvoke_list);
380           if (quit_functions)
381             quit_functions->prev = tmp_list;
382           tmp_list->next = quit_functions;
383           quit_functions = tmp_list;
384         }
385     }
386               
387   main_level--;
388 }
389
390 guint
391 gtk_main_level (void)
392 {
393   return main_level;
394 }
395
396 void
397 gtk_main_quit ()
398 {
399   iteration_done = TRUE;
400 }
401
402 gint
403 gtk_events_pending (void)
404 {
405   gint result = gdk_events_pending() + ((next_event != NULL) ? 1 : 0);
406
407   if (idle_functions &&
408       (((GtkIdleFunction *)idle_functions->data)->priority <=
409        GTK_PRIORITY_INTERNAL))
410     result += 1;
411
412   return result;
413 }
414
415 gint 
416 gtk_main_iteration ()
417 {
418   return gtk_main_iteration_do (TRUE);
419 }
420
421 gint
422 gtk_main_iteration_do (gboolean blocking)
423 {
424   GtkWidget *event_widget;
425   GtkWidget *grab_widget;
426   GdkEvent *event = NULL;
427   GList *tmp_list;
428   
429   iteration_done = FALSE;
430   
431   /* If this is a recursive call, and there are pending timeouts or
432    * idles, finish them, then return immediately to avoid blocking
433    * in gdk_event_get()
434    */
435   if (current_timeouts)
436     {
437       gtk_handle_current_timeouts( gdk_time_get());
438       return iteration_done;
439     }
440   if (current_idles)
441     {
442       gtk_handle_current_idles ();
443       return iteration_done;
444     }
445   
446   /* If there is a valid event in 'next_event' then move it to 'event'
447    */
448   if (next_event)
449     {
450       event = next_event;
451       next_event = NULL;
452     }
453   
454   /* If we don't have an event then get one.
455    */
456   if (!event)
457     {
458       /* Handle setting of the "gdk" timer. If there are no
459        *  timeout functions, then the timer is turned off.
460        *  If there are timeout functions, then the timer is
461        *  set to the shortest timeout interval (which is
462        *  the first timeout function).
463        */
464       gtk_handle_timer ();
465       
466       if (blocking) event = gdk_event_get ();
467     }
468   
469   /* "gdk_event_get" can return FALSE if the timer goes off
470    *  and no events are pending. Therefore, we should make
471    *  sure that we got an event before continuing.
472    */
473   if (event)
474     {
475       /* If there are any events pending then get the next one.
476        */
477       if (gdk_events_pending () > 0)
478         next_event = gdk_event_get ();
479       
480       /* Try to compress enter/leave notify events. These event
481        *  pairs occur when the mouse is dragged quickly across
482        *  a window with many buttons (or through a menu). Instead
483        *  of highlighting and de-highlighting each widget that
484        *  is crossed it is better to simply de-highlight the widget
485        *  which contained the mouse initially and highlight the
486        *  widget which ends up containing the mouse.
487        */
488       if (next_event)
489         if (((event->type == GDK_ENTER_NOTIFY) ||
490              (event->type == GDK_LEAVE_NOTIFY)) &&
491             ((next_event->type == GDK_ENTER_NOTIFY) ||
492              (next_event->type == GDK_LEAVE_NOTIFY)) &&
493             (next_event->type != event->type) &&
494             (next_event->any.window == event->any.window))
495           {
496             gdk_event_free (event);
497             gdk_event_free (next_event);
498             next_event = NULL;
499             
500             goto event_handling_done;
501           }
502       
503       /* Find the widget which got the event. We store the widget
504        *  in the user_data field of GdkWindow's.
505        *  Ignore the event if we don't have a widget for it, except
506        *  for GDK_PROPERTY_NOTIFY events which are handled specialy.
507        *  Though this happens rarely, bogus events can occour
508        *  for e.g. destroyed GdkWindows. 
509        */
510       event_widget = gtk_get_event_widget (event);
511       if (!event_widget)
512         {
513           /* To handle selection INCR transactions, we select
514            * PropertyNotify events on the requestor window and create
515            * a corresponding (fake) GdkWindow so that events get
516            * here. There won't be a widget though, so we have to handle
517            * them specially
518            */
519           if (event->type == GDK_PROPERTY_NOTIFY)
520             gtk_selection_incr_event (event->any.window,
521                                       &event->property);
522           
523           gdk_event_free (event);
524           
525           goto event_handling_done;
526         }
527       
528       /* Push the event onto a stack of current events for
529        * gtk_current_event_get().
530        */
531       current_events = g_list_prepend (current_events, event);
532       
533       /* If there is a grab in effect...
534        */
535       if (grabs)
536         {
537           grab_widget = grabs->data;
538           
539           /* If the grab widget is an ancestor of the event widget
540            *  then we send the event to the original event widget.
541            *  This is the key to implementing modality.
542            */
543           if (GTK_WIDGET_IS_SENSITIVE (event_widget) &&
544               gtk_widget_is_ancestor (event_widget, grab_widget))
545             grab_widget = event_widget;
546         }
547       else
548         {
549           grab_widget = event_widget;
550         }
551
552       /* Not all events get sent to the grabbing widget.
553        * The delete, destroy, expose, focus change and resize
554        *  events still get sent to the event widget because
555        *  1) these events have no meaning for the grabbing widget
556        *  and 2) redirecting these events to the grabbing widget
557        *  could cause the display to be messed up.
558        */
559       switch (event->type)
560         {
561         case GDK_NOTHING:
562           break;
563           
564         case GDK_DELETE:
565           gtk_widget_ref (event_widget);
566           if (!gtk_widget_event (event_widget, event) &&
567               !GTK_OBJECT_DESTROYED (event_widget))
568             gtk_widget_destroy (event_widget);
569           gtk_widget_unref (event_widget);
570           break;
571           
572         case GDK_DESTROY:
573           gtk_widget_ref (event_widget);
574           gtk_widget_event (event_widget, event);
575           if (!GTK_OBJECT_DESTROYED (event_widget))
576             gtk_widget_destroy (event_widget);
577           gtk_widget_unref (event_widget);
578           break;
579           
580         case GDK_PROPERTY_NOTIFY:
581         case GDK_EXPOSE:
582         case GDK_NO_EXPOSE:
583         case GDK_FOCUS_CHANGE:
584         case GDK_CONFIGURE:
585         case GDK_MAP:
586         case GDK_UNMAP:
587         case GDK_SELECTION_CLEAR:
588         case GDK_SELECTION_REQUEST:
589         case GDK_SELECTION_NOTIFY:
590         case GDK_CLIENT_EVENT:
591         case GDK_DRAG_BEGIN:
592         case GDK_DRAG_REQUEST:
593         case GDK_DROP_ENTER:
594         case GDK_DROP_LEAVE:
595         case GDK_DROP_DATA_AVAIL:
596         case GDK_VISIBILITY_NOTIFY:
597           gtk_widget_event (event_widget, event);
598           break;
599           
600         case GDK_KEY_PRESS:
601         case GDK_KEY_RELEASE:
602           if (key_snoopers)
603             {
604               if (gtk_invoke_key_snoopers (grab_widget, event))
605                 break;
606             }
607           /* else fall through */
608         case GDK_MOTION_NOTIFY:
609         case GDK_BUTTON_PRESS:
610         case GDK_2BUTTON_PRESS:
611         case GDK_3BUTTON_PRESS:
612         case GDK_BUTTON_RELEASE:
613         case GDK_PROXIMITY_IN:
614         case GDK_PROXIMITY_OUT:
615         case GDK_OTHER_EVENT:
616           gtk_propagate_event (grab_widget, event);
617           break;
618           
619         case GDK_ENTER_NOTIFY:
620           if (GTK_WIDGET_IS_SENSITIVE (grab_widget))
621             {
622               gtk_widget_event (grab_widget, event);
623               if (event_widget == grab_widget)
624                 GTK_PRIVATE_SET_FLAG (event_widget, GTK_LEAVE_PENDING);
625             }
626           break;
627           
628         case GDK_LEAVE_NOTIFY:
629           if (GTK_WIDGET_LEAVE_PENDING (event_widget))
630             {
631               GTK_PRIVATE_UNSET_FLAG (event_widget, GTK_LEAVE_PENDING);
632               gtk_widget_event (event_widget, event);
633             }
634           else if (GTK_WIDGET_IS_SENSITIVE (grab_widget))
635             gtk_widget_event (grab_widget, event);
636           break;
637         }
638       
639       tmp_list = current_events;
640       current_events = g_list_remove_link (current_events, tmp_list);
641       g_list_free_1 (tmp_list);
642       
643       gdk_event_free (event);
644     }
645   else
646     {
647       if (gdk_events_pending() == 0)
648         gtk_handle_idle ();
649     }
650   
651 event_handling_done:
652   
653   /* Handle timeout functions that may have expired.
654    */
655   gtk_handle_timeouts ();
656   
657   return iteration_done;
658 }
659
660 gint
661 gtk_true (void)
662 {
663   return TRUE;
664 }
665
666 gint
667 gtk_false (void)
668 {
669   return FALSE;
670 }
671
672 void
673 gtk_grab_add (GtkWidget *widget)
674 {
675   g_return_if_fail (widget != NULL);
676   
677   if (!GTK_WIDGET_HAS_GRAB (widget))
678     {
679       GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_GRAB);
680       
681       grabs = g_slist_prepend (grabs, widget);
682       gtk_widget_ref (widget);
683     }
684 }
685
686 GtkWidget*
687 gtk_grab_get_current (void)
688 {
689   if (grabs)
690     return GTK_WIDGET (grabs->data);
691   return NULL;
692 }
693
694 void
695 gtk_grab_remove (GtkWidget *widget)
696 {
697   g_return_if_fail (widget != NULL);
698   
699   if (GTK_WIDGET_HAS_GRAB (widget))
700     {
701       GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_GRAB);
702       
703       grabs = g_slist_remove (grabs, widget);
704       gtk_widget_unref (widget);
705     }
706 }
707
708 void
709 gtk_init_add (GtkFunction function,
710               gpointer    data)
711 {
712   GtkInitFunction *init;
713   
714   init = g_new (GtkInitFunction, 1);
715   init->function = function;
716   init->data = data;
717   
718   init_functions = g_list_prepend (init_functions, init);
719 }
720
721 guint
722 gtk_key_snooper_install (GtkKeySnoopFunc snooper,
723                          gpointer        func_data)
724 {
725   GtkKeySnooperData *data;
726   static guint snooper_id = 1;
727
728   g_return_val_if_fail (snooper != NULL, 0);
729
730   data = g_new (GtkKeySnooperData, 1);
731   data->func = snooper;
732   data->func_data = func_data;
733   data->id = snooper_id++;
734   key_snoopers = g_slist_prepend (key_snoopers, data);
735
736   return data->id;
737 }
738
739 void
740 gtk_key_snooper_remove (guint            snooper_id)
741 {
742   GtkKeySnooperData *data = NULL;
743   GSList *slist;
744
745   slist = key_snoopers;
746   while (slist)
747     {
748       data = slist->data;
749       if (data->id == snooper_id)
750         break;
751
752       slist = slist->next;
753       data = NULL;
754     }
755   if (data)
756     key_snoopers = g_slist_remove (key_snoopers, data);
757 }
758
759 static gint
760 gtk_invoke_key_snoopers (GtkWidget *grab_widget,
761                          GdkEvent  *event)
762 {
763   GSList *slist;
764   gint return_val = FALSE;
765
766   slist = key_snoopers;
767   while (slist && !return_val)
768     {
769       GtkKeySnooperData *data;
770
771       data = slist->data;
772       slist = slist->next;
773       return_val = (*data->func) (grab_widget, (GdkEventKey*) event, data->func_data);
774     }
775
776   return return_val;
777 }
778
779 guint
780 gtk_timeout_add_full (guint32            interval,
781                       GtkFunction        function,
782                       GtkCallbackMarshal marshal,
783                       gpointer           data,
784                       GtkDestroyNotify   destroy)
785 {
786   static guint timeout_tag = 1;
787   GtkTimeoutFunction *timeoutf;
788   
789   g_return_val_if_fail ((function != NULL) || (marshal != NULL), 0);
790
791   /* Create a new timeout function structure.
792    * The start time is the current time.
793    */
794   if (!timeout_mem_chunk)
795     timeout_mem_chunk = g_mem_chunk_new ("timeout mem chunk", sizeof (GtkTimeoutFunction),
796                                          1024, G_ALLOC_AND_FREE);
797   
798   timeoutf = g_chunk_new (GtkTimeoutFunction, timeout_mem_chunk);
799   
800   timeoutf->tag = timeout_tag++;
801   timeoutf->start = gdk_time_get ();
802   timeoutf->interval = interval;
803   timeoutf->originterval = interval;
804   timeoutf->function = function;
805   timeoutf->marshal = marshal;
806   timeoutf->data = data;
807   timeoutf->destroy = destroy;
808   
809   gtk_timeout_insert (timeoutf);
810   
811   return timeoutf->tag;
812 }
813
814 static void
815 gtk_timeout_destroy (GtkTimeoutFunction *timeoutf)
816 {
817   if (timeoutf->destroy)
818     (timeoutf->destroy) (timeoutf->data);
819   g_mem_chunk_free (timeout_mem_chunk, timeoutf);
820 }
821
822 guint
823 gtk_timeout_add (guint32     interval,
824                  GtkFunction function,
825                  gpointer    data)
826 {
827   return gtk_timeout_add_full (interval, function, FALSE, data, NULL);
828 }
829
830 guint
831 gtk_timeout_add_interp (guint32            interval,
832                         GtkCallbackMarshal function,
833                         gpointer           data,
834                         GtkDestroyNotify   destroy)
835 {
836   return gtk_timeout_add_full (interval, NULL, function, data, destroy);
837 }
838
839 void
840 gtk_timeout_remove (guint tag)
841 {
842   GtkTimeoutFunction *timeoutf;
843   GList *tmp_list;
844   
845   /* Remove a timeout function.
846    * (Which, basically, involves searching the
847    *  list for the tag).
848    */
849   if ((running_timeout) && (running_timeout->tag == tag))
850     {
851       gtk_timeout_destroy (running_timeout);
852       running_timeout = NULL;
853     }
854
855   tmp_list = timeout_functions;
856   while (tmp_list)
857     {
858       timeoutf = tmp_list->data;
859       
860       if (timeoutf->tag == tag)
861         {
862           timeout_functions = g_list_remove_link (timeout_functions, tmp_list);
863           g_list_free (tmp_list);
864           gtk_timeout_destroy (timeoutf);
865           
866           return;
867         }
868       
869       tmp_list = tmp_list->next;
870     }
871   
872   tmp_list = current_timeouts;
873   while (tmp_list)
874     {
875       timeoutf = tmp_list->data;
876       
877       if (timeoutf->tag == tag)
878         {
879           current_timeouts = g_list_remove_link (current_timeouts, tmp_list);
880           g_list_free (tmp_list);
881           gtk_timeout_destroy (timeoutf);
882           
883           return;
884         }
885       
886       tmp_list = tmp_list->next;
887     }
888 }
889
890 /* We rely on some knowledge of how g_list_insert_sorted works to make
891  * sure that we insert at the _end_ of the idles of this priority
892  */
893 static gint
894 gtk_idle_compare (gpointer a, gpointer b)
895 {
896   return (((GtkIdleFunction *)a)->priority < ((GtkIdleFunction *)b)->priority)
897     ? -1 : 1;
898 }
899
900 guint
901 gtk_quit_add_full (guint                main_level,
902                    GtkFunction          function,
903                    GtkCallbackMarshal   marshal,
904                    gpointer             data,
905                    GtkDestroyNotify     destroy)
906 {
907   static guint quit_id = 1;
908   GtkQuitFunction *quitf;
909   
910   g_return_val_if_fail ((function != NULL) || (marshal != NULL), 0);
911
912   if (!quit_mem_chunk)
913     quit_mem_chunk = g_mem_chunk_new ("quit mem chunk", sizeof (GtkQuitFunction),
914                                       512, G_ALLOC_AND_FREE);
915   
916   quitf = g_chunk_new (GtkQuitFunction, quit_mem_chunk);
917   
918   quitf->id = quit_id++;
919   quitf->main_level = main_level;
920   quitf->function = function;
921   quitf->marshal = marshal;
922   quitf->data = data;
923   quitf->destroy = destroy;
924
925   quit_functions = g_list_prepend (quit_functions, quitf);
926   
927   return quitf->id;
928 }
929
930 guint
931 gtk_idle_add_full (gint                 priority,
932                    GtkFunction          function,
933                    GtkCallbackMarshal   marshal,
934                    gpointer             data,
935                    GtkDestroyNotify     destroy)
936 {
937   static guint idle_tag = 1;
938   GtkIdleFunction *idlef;
939   
940   g_return_val_if_fail ((function != NULL) || (marshal != NULL), 0);
941
942   if (!idle_mem_chunk)
943     idle_mem_chunk = g_mem_chunk_new ("idle mem chunk", sizeof (GtkIdleFunction),
944                                       1024, G_ALLOC_AND_FREE);
945   
946   idlef = g_chunk_new (GtkIdleFunction, idle_mem_chunk);
947   
948   idlef->tag = idle_tag++;
949   idlef->priority = priority;
950   idlef->function = function;
951   idlef->marshal = marshal;
952   idlef->data = data;
953   idlef->destroy = destroy;
954
955   idle_functions = g_list_insert_sorted (idle_functions, idlef, gtk_idle_compare);
956   
957   return idlef->tag;
958 }
959
960 guint
961 gtk_idle_add_interp  (GtkCallbackMarshal   marshal,
962                       gpointer             data,
963                       GtkDestroyNotify     destroy)
964 {
965   return gtk_idle_add_full (GTK_PRIORITY_DEFAULT, NULL, marshal, data, destroy);
966 }
967
968 static void
969 gtk_idle_destroy (GtkIdleFunction *idlef)
970 {
971   if (idlef->destroy)
972     idlef->destroy (idlef->data);
973   g_mem_chunk_free (idle_mem_chunk, idlef);
974 }
975
976 static void
977 gtk_quit_destroy (GtkQuitFunction *quitf)
978 {
979   if (quitf->destroy)
980     quitf->destroy (quitf->data);
981   g_mem_chunk_free (quit_mem_chunk, quitf);
982 }
983
984 static gint
985 gtk_quit_destructor (GtkObject **object_p)
986 {
987   if (*object_p)
988     gtk_object_destroy (*object_p);
989   g_free (object_p);
990
991   return FALSE;
992 }
993
994 void
995 gtk_quit_add_destroy (guint              main_level,
996                       GtkObject         *object)
997 {
998   GtkObject **object_p;
999
1000   g_return_if_fail (main_level > 0);
1001   g_return_if_fail (object != NULL);
1002   g_return_if_fail (GTK_IS_OBJECT (object));
1003
1004   object_p = g_new (GtkObject*, 1);
1005   *object_p = object;
1006   gtk_signal_connect (object,
1007                       "destroy",
1008                       GTK_SIGNAL_FUNC (gtk_widget_destroyed),
1009                       object_p);
1010   gtk_quit_add (main_level, (GtkFunction) gtk_quit_destructor, object_p);
1011 }
1012
1013 guint
1014 gtk_quit_add (guint       main_level,
1015               GtkFunction function,
1016               gpointer    data)
1017 {
1018   return gtk_quit_add_full (main_level, function, NULL, data, NULL);
1019 }
1020
1021 guint
1022 gtk_idle_add (GtkFunction function,
1023               gpointer    data)
1024 {
1025   return gtk_idle_add_full (GTK_PRIORITY_DEFAULT, function, NULL, data, NULL);
1026 }
1027
1028 guint       
1029 gtk_idle_add_priority   (gint               priority,
1030                          GtkFunction        function,
1031                          gpointer           data)
1032 {
1033   return gtk_idle_add_full (priority, function, NULL, data, NULL);
1034 }
1035
1036 void
1037 gtk_quit_remove (guint id)
1038 {
1039   GtkQuitFunction *quitf;
1040   GList *tmp_list;
1041   
1042   tmp_list = quit_functions;
1043   while (tmp_list)
1044     {
1045       quitf = tmp_list->data;
1046       
1047       if (quitf->id == id)
1048         {
1049           quit_functions = g_list_remove_link (quit_functions, tmp_list);
1050           g_list_free (tmp_list);
1051           gtk_quit_destroy (quitf);
1052           
1053           return;
1054         }
1055       
1056       tmp_list = tmp_list->next;
1057     }
1058 }
1059
1060 void
1061 gtk_quit_remove_by_data (gpointer data)
1062 {
1063   GtkQuitFunction *quitf;
1064   GList *tmp_list;
1065   
1066   tmp_list = quit_functions;
1067   while (tmp_list)
1068     {
1069       quitf = tmp_list->data;
1070       
1071       if (quitf->data == data)
1072         {
1073           quit_functions = g_list_remove_link (quit_functions, tmp_list);
1074           g_list_free (tmp_list);
1075           gtk_quit_destroy (quitf);
1076
1077           return;
1078         }
1079       
1080       tmp_list = tmp_list->next;
1081     }
1082 }
1083
1084 void
1085 gtk_idle_remove (guint tag)
1086 {
1087   GtkIdleFunction *idlef;
1088   GList *tmp_list;
1089   
1090   if ((running_idle) && (running_idle->tag == tag))
1091     {
1092       gtk_idle_destroy (running_idle);
1093       running_idle = NULL;
1094     }
1095
1096   tmp_list = idle_functions;
1097   while (tmp_list)
1098     {
1099       idlef = tmp_list->data;
1100       
1101       if (idlef->tag == tag)
1102         {
1103           idle_functions = g_list_remove_link (idle_functions, tmp_list);
1104           g_list_free (tmp_list);
1105           gtk_idle_destroy (idlef);
1106           
1107           return;
1108         }
1109       
1110       tmp_list = tmp_list->next;
1111     }
1112   
1113   tmp_list = current_idles;
1114   while (tmp_list)
1115     {
1116       idlef = tmp_list->data;
1117       
1118       if (idlef->tag == tag)
1119         {
1120           current_idles = g_list_remove_link (current_idles, tmp_list);
1121           g_list_free (tmp_list);
1122           gtk_idle_destroy (idlef);
1123           
1124           return;
1125         }
1126       
1127       tmp_list = tmp_list->next;
1128     }
1129 }
1130
1131 void
1132 gtk_idle_remove_by_data (gpointer data)
1133 {
1134   GtkIdleFunction *idlef;
1135   GList *tmp_list;
1136   
1137   tmp_list = idle_functions;
1138   while (tmp_list)
1139     {
1140       idlef = tmp_list->data;
1141       
1142       if (idlef->data == data)
1143         {
1144           idle_functions = g_list_remove_link (idle_functions, tmp_list);
1145           g_list_free (tmp_list);
1146           gtk_idle_destroy (idlef);
1147           
1148           return;
1149         }
1150       
1151       tmp_list = tmp_list->next;
1152     }
1153   
1154   tmp_list = current_idles;
1155   while (tmp_list)
1156     {
1157       idlef = tmp_list->data;
1158       
1159       if (idlef->data == data)
1160         {
1161           current_idles = g_list_remove_link (current_idles, tmp_list);
1162           g_list_free (tmp_list);
1163           gtk_idle_destroy (idlef);
1164           
1165           return;
1166         }
1167       
1168       tmp_list = tmp_list->next;
1169     }
1170 }
1171
1172 static void
1173 gtk_invoke_input_function (GtkInputFunction *input,
1174                            gint source,
1175                            GdkInputCondition condition)
1176 {
1177   GtkArg args[3];
1178   args[0].type = GTK_TYPE_INT;
1179   args[0].name = NULL;
1180   GTK_VALUE_INT(args[0]) = source;
1181   args[1].type = GTK_TYPE_GDK_INPUT_CONDITION;
1182   args[1].name = NULL;
1183   GTK_VALUE_FLAGS(args[1]) = condition;
1184   args[2].type = GTK_TYPE_NONE;
1185   args[2].name = NULL;
1186
1187   input->callback (NULL, input->data, 2, args);
1188 }
1189
1190 static void
1191 gtk_destroy_input_function (GtkInputFunction *input)
1192 {
1193   if (input->destroy)
1194     (input->destroy) (input->data);
1195   g_free (input);
1196 }
1197
1198 guint
1199 gtk_input_add_full (gint source,
1200                     GdkInputCondition condition,
1201                     GdkInputFunction function,
1202                     GtkCallbackMarshal marshal,
1203                     gpointer data,
1204                     GtkDestroyNotify destroy)
1205 {
1206   if (marshal)
1207     {
1208       GtkInputFunction *input;
1209
1210       input = g_new (GtkInputFunction, 1);
1211       input->callback = marshal;
1212       input->data = data;
1213       input->destroy = destroy;
1214
1215       return gdk_input_add_full (source,
1216                                  condition,
1217                                  (GdkInputFunction) gtk_invoke_input_function,
1218                                  input,
1219                                  (GdkDestroyNotify) gtk_destroy_input_function);
1220     }
1221   else
1222     return gdk_input_add_full (source, condition, function, data, destroy);
1223 }
1224
1225 guint
1226 gtk_input_add_interp (gint source,
1227                       GdkInputCondition condition,
1228                       GtkCallbackMarshal callback,
1229                       gpointer data,
1230                       GtkDestroyNotify destroy)
1231 {
1232   return gtk_input_add_full (source, condition, NULL, callback, data, destroy);
1233 }
1234
1235 void
1236 gtk_input_remove (guint tag)
1237 {
1238   gdk_input_remove (tag);
1239 }
1240
1241 GdkEvent *
1242 gtk_get_current_event ()
1243 {
1244   if (current_events)
1245     return gdk_event_copy ((GdkEvent *) current_events->data);
1246   else
1247     return NULL;
1248 }
1249
1250 GtkWidget*
1251 gtk_get_event_widget (GdkEvent *event)
1252 {
1253   GtkWidget *widget;
1254
1255   widget = NULL;
1256   if (event->any.window)
1257     gdk_window_get_user_data (event->any.window, (void**) &widget);
1258   
1259   return widget;
1260 }
1261
1262 static void
1263 gtk_exit_func ()
1264 {
1265   if (initialized)
1266     {
1267       initialized = FALSE;
1268       gtk_preview_uninit ();
1269     }
1270 }
1271
1272
1273 /* We rely on some knowledge of how g_list_insert_sorted works to make
1274  * sure that we insert after timeouts of equal interval
1275  */
1276 static gint
1277 gtk_timeout_compare (gpointer a, gpointer b)
1278 {
1279   return (((GtkTimeoutFunction *)a)->interval < 
1280           ((GtkTimeoutFunction *)b)->interval)
1281     ? -1 : 1;
1282 }
1283
1284 static void
1285 gtk_timeout_insert (GtkTimeoutFunction *timeoutf)
1286 {
1287   g_assert (timeoutf != NULL);
1288   
1289   /* Insert the timeout function appropriately.
1290    * Appropriately meaning sort it into the list
1291    *  of timeout functions.
1292    */
1293   timeout_functions = g_list_insert_sorted (timeout_functions, timeoutf,
1294                                             gtk_timeout_compare);
1295 }
1296
1297 static gint
1298 gtk_invoke_timeout_function (GtkTimeoutFunction *timeoutf)
1299 {
1300   if (!timeoutf->marshal)
1301     return timeoutf->function (timeoutf->data);
1302   else
1303     {
1304       GtkArg args[1];
1305       gint ret_val = FALSE;
1306       args[0].name = NULL;
1307       args[0].type = GTK_TYPE_BOOL;
1308       args[0].d.pointer_data = &ret_val;
1309       timeoutf->marshal (NULL, timeoutf->data,  0, args);
1310       return ret_val;
1311     }
1312 }
1313
1314 static void
1315 gtk_handle_current_timeouts (guint32 the_time)
1316 {
1317   GList *tmp_list;
1318   
1319   while (current_timeouts)
1320     {
1321       tmp_list = current_timeouts;
1322       running_timeout = tmp_list->data;
1323       
1324       current_timeouts = g_list_remove_link (current_timeouts, tmp_list);
1325       g_list_free (tmp_list);
1326       
1327       if ((gtk_invoke_timeout_function (running_timeout) == FALSE) ||
1328           (running_timeout == NULL))
1329         {
1330           if (running_timeout)
1331             gtk_timeout_destroy (running_timeout);
1332         }
1333       else
1334         {
1335           running_timeout->interval = running_timeout->originterval;
1336           running_timeout->start = the_time;
1337           gtk_timeout_insert (running_timeout);
1338         }
1339     }
1340   running_timeout = NULL;
1341 }
1342
1343 static void
1344 gtk_handle_timeouts ()
1345 {
1346   guint32 the_time;
1347   GList *tmp_list;
1348   GList *tmp_list2;
1349   GtkTimeoutFunction *timeoutf;
1350   
1351   /* Caller must already have called gtk_handle_current_timeouts if
1352    * necessary */
1353   g_assert (current_timeouts == NULL);
1354   
1355   if (timeout_functions)
1356     {
1357       the_time = gdk_time_get ();
1358       
1359       tmp_list = timeout_functions;
1360       while (tmp_list)
1361         {
1362           timeoutf = tmp_list->data;
1363           
1364           if (timeoutf->interval <= (the_time - timeoutf->start))
1365             {
1366               tmp_list2 = tmp_list;
1367               tmp_list = tmp_list->next;
1368               
1369               timeout_functions = g_list_remove_link (timeout_functions, tmp_list2);
1370               current_timeouts = g_list_concat (current_timeouts, tmp_list2);
1371             }
1372           else
1373             {
1374               timeoutf->interval -= (the_time - timeoutf->start);
1375               timeoutf->start = the_time;
1376               tmp_list = tmp_list->next;
1377             }
1378         }
1379       
1380       if (current_timeouts)
1381         gtk_handle_current_timeouts(the_time);
1382     }
1383 }
1384
1385 static gint
1386 gtk_quit_invoke_function (GtkQuitFunction *quitf)
1387 {
1388   if (!quitf->marshal)
1389     return quitf->function (quitf->data);
1390   else
1391     {
1392       GtkArg args[1];
1393       gint ret_val = FALSE;
1394
1395       args[0].name = NULL;
1396       args[0].type = GTK_TYPE_BOOL;
1397       args[0].d.pointer_data = &ret_val;
1398       ((GtkCallbackMarshal) quitf->marshal) (NULL,
1399                                              quitf->data,
1400                                              0, args);
1401       return ret_val;
1402     }
1403 }
1404
1405 static gint
1406 gtk_idle_invoke_function (GtkIdleFunction *idlef)
1407 {
1408   if (!idlef->marshal)
1409     return idlef->function (idlef->data);
1410   else
1411     {
1412       GtkArg args[1];
1413       gint ret_val = FALSE;
1414
1415       args[0].name = NULL;
1416       args[0].type = GTK_TYPE_BOOL;
1417       args[0].d.pointer_data = &ret_val;
1418       ((GtkCallbackMarshal) idlef->marshal) (NULL,
1419                                              idlef->data,
1420                                              0, args);
1421       return ret_val;
1422     }
1423 }
1424
1425 static void
1426 gtk_handle_current_idles ()
1427 {
1428   GList *tmp_list;
1429   GList *tmp_list2;
1430   
1431   while (current_idles)
1432     {
1433       tmp_list = current_idles;
1434       running_idle = tmp_list->data;
1435       
1436       current_idles = g_list_remove_link (current_idles, tmp_list);
1437       
1438       if ((gtk_idle_invoke_function (running_idle) == FALSE) ||
1439           (running_idle == NULL))
1440         {
1441           g_list_free (tmp_list);
1442           if (running_idle)
1443             gtk_idle_destroy (running_idle);
1444         }
1445       else
1446         {
1447           /* Insert the idle function back into the list of idle
1448            * functions at the end of the idles of this priority
1449            */
1450           tmp_list2 = idle_functions;
1451           while (tmp_list2 &&
1452                  (((GtkIdleFunction *)tmp_list2->data)->priority <= running_idle->priority))
1453             tmp_list2 = tmp_list2->next;
1454
1455           if (!tmp_list2)
1456             idle_functions = g_list_concat (idle_functions, tmp_list);
1457           else if (tmp_list2 == idle_functions)
1458             {
1459               tmp_list->next = idle_functions;
1460               if (idle_functions)
1461                 idle_functions->prev = tmp_list;
1462               idle_functions = tmp_list;
1463             }
1464           else
1465             {
1466               tmp_list->prev = tmp_list2->prev;
1467               tmp_list->next = tmp_list2;
1468               tmp_list2->prev->next = tmp_list;
1469               tmp_list2->prev = tmp_list;
1470             }
1471         }
1472     }
1473   running_idle = NULL;
1474 }
1475
1476 static void
1477 gtk_handle_idle ()
1478 {
1479   /* Caller must already have called gtk_handle_current_idles if
1480    * necessary
1481    */
1482   g_assert (current_idles == NULL);
1483   
1484   /* Handle only the idle functions that have the highest priority */
1485   if (idle_functions)
1486     {
1487       GList *tmp_list;
1488       gint top_priority;
1489
1490       tmp_list = idle_functions;
1491       top_priority = ((GtkIdleFunction *)tmp_list->data)->priority;
1492  
1493       while (tmp_list &&
1494              (((GtkIdleFunction *)tmp_list->data)->priority == top_priority))
1495         tmp_list = tmp_list->next;
1496
1497       current_idles = idle_functions;
1498       idle_functions = tmp_list;
1499
1500       if (tmp_list) 
1501         {
1502           tmp_list->prev->next = NULL;
1503           tmp_list->prev = NULL;
1504         }
1505       
1506       gtk_handle_current_idles();
1507     }
1508 }
1509
1510 static void
1511 gtk_handle_timer ()
1512 {
1513   GtkTimeoutFunction *timeoutf;
1514   
1515   if (idle_functions)
1516     {
1517       gdk_timer_set (0);
1518       gdk_timer_enable ();
1519     }
1520   else if (timeout_functions)
1521     {
1522       timeoutf = timeout_functions->data;
1523       gdk_timer_set (timeoutf->interval);
1524       gdk_timer_enable ();
1525     }
1526   else
1527     {
1528       gdk_timer_set (0);
1529       gdk_timer_disable ();
1530     }
1531 }
1532
1533 static void
1534 gtk_propagate_event (GtkWidget *widget,
1535                      GdkEvent  *event)
1536 {
1537   gint handled_event;
1538   
1539   g_return_if_fail (widget != NULL);
1540   g_return_if_fail (event != NULL);
1541   
1542   handled_event = FALSE;
1543
1544   if ((event->type == GDK_KEY_PRESS) ||
1545       (event->type == GDK_KEY_RELEASE))
1546     {
1547       /* Only send key events within Window widgets to the Window
1548        *  The Window widget will in turn pass the
1549        *  key event on to the currently focused widget
1550        *  for that window.
1551        */
1552       GtkWidget *window;
1553
1554       window = gtk_widget_get_ancestor (widget, gtk_window_get_type ());
1555       if (window)
1556         {
1557           if (GTK_WIDGET_IS_SENSITIVE (window))
1558             gtk_widget_event (window, event);
1559
1560           handled_event = TRUE; /* don't send to widget */
1561         }
1562     }
1563   
1564   /* Other events get propagated up the widget tree
1565    *  so that parents can see the button and motion
1566    *  events of the children.
1567    */
1568   while (!handled_event && widget)
1569     {
1570       GtkWidget *tmp;
1571
1572       gtk_widget_ref (widget);
1573       handled_event = !GTK_WIDGET_IS_SENSITIVE (widget) || gtk_widget_event (widget, event);
1574       tmp = widget->parent;
1575       gtk_widget_unref (widget);
1576       widget  = tmp;
1577     }
1578 }
1579
1580
1581 static void
1582 gtk_error (gchar *str)
1583 {
1584   gtk_print (str);
1585 }
1586
1587 static void
1588 gtk_warning (gchar *str)
1589 {
1590   gtk_print (str);
1591 }
1592
1593 static void
1594 gtk_message (gchar *str)
1595 {
1596   gtk_print (str);
1597 }
1598
1599 static void
1600 gtk_print (gchar *str)
1601 {
1602   static GtkWidget *window = NULL;
1603   static GtkWidget *text;
1604   static int level = 0;
1605   GtkWidget *box1;
1606   GtkWidget *box2;
1607   GtkWidget *table;
1608   GtkWidget *hscrollbar;
1609   GtkWidget *vscrollbar;
1610   GtkWidget *separator;
1611   GtkWidget *button;
1612   
1613   if (level > 0)
1614     {
1615       fputs (str, stdout);
1616       fflush (stdout);
1617       return;
1618     }
1619   
1620   if (!window)
1621     {
1622       window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
1623       
1624       gtk_signal_connect (GTK_OBJECT (window), "destroy",
1625                           (GtkSignalFunc) gtk_widget_destroyed,
1626                           &window);
1627       
1628       gtk_window_set_title (GTK_WINDOW (window), "Messages");
1629       
1630       box1 = gtk_vbox_new (FALSE, 0);
1631       gtk_container_add (GTK_CONTAINER (window), box1);
1632       gtk_widget_show (box1);
1633       
1634       
1635       box2 = gtk_vbox_new (FALSE, 10);
1636       gtk_container_border_width (GTK_CONTAINER (box2), 10);
1637       gtk_box_pack_start (GTK_BOX (box1), box2, TRUE, TRUE, 0);
1638       gtk_widget_show (box2);
1639       
1640       
1641       table = gtk_table_new (2, 2, FALSE);
1642       gtk_table_set_row_spacing (GTK_TABLE (table), 0, 2);
1643       gtk_table_set_col_spacing (GTK_TABLE (table), 0, 2);
1644       gtk_box_pack_start (GTK_BOX (box2), table, TRUE, TRUE, 0);
1645       gtk_widget_show (table);
1646       
1647       text = gtk_text_new (NULL, NULL);
1648       gtk_text_set_editable (GTK_TEXT (text), FALSE);
1649       gtk_table_attach_defaults (GTK_TABLE (table), text, 0, 1, 0, 1);
1650       gtk_widget_show (text);
1651       gtk_widget_realize (text);
1652       
1653       hscrollbar = gtk_hscrollbar_new (GTK_TEXT (text)->hadj);
1654       gtk_table_attach (GTK_TABLE (table), hscrollbar, 0, 1, 1, 2,
1655                         GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
1656       gtk_widget_show (hscrollbar);
1657       
1658       vscrollbar = gtk_vscrollbar_new (GTK_TEXT (text)->vadj);
1659       gtk_table_attach (GTK_TABLE (table), vscrollbar, 1, 2, 0, 1,
1660                         GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
1661       gtk_widget_show (vscrollbar);
1662       
1663       separator = gtk_hseparator_new ();
1664       gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 0);
1665       gtk_widget_show (separator);
1666       
1667       
1668       box2 = gtk_vbox_new (FALSE, 10);
1669       gtk_container_border_width (GTK_CONTAINER (box2), 10);
1670       gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, TRUE, 0);
1671       gtk_widget_show (box2);
1672       
1673       
1674       button = gtk_button_new_with_label ("close");
1675       gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
1676                                  (GtkSignalFunc) gtk_widget_hide,
1677                                  GTK_OBJECT (window));
1678       gtk_box_pack_start (GTK_BOX (box2), button, TRUE, TRUE, 0);
1679       GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
1680       gtk_widget_grab_default (button);
1681       gtk_widget_show (button);
1682     }
1683   
1684   level += 1;
1685   gtk_text_insert (GTK_TEXT (text), NULL, NULL, NULL, str, -1);
1686   level -= 1;
1687   
1688   if (!GTK_WIDGET_VISIBLE (window))
1689     gtk_widget_show (window);
1690 }