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