]> Pileus Git - ~andy/gtk/blob - gtk/gtkmain.c
Added notice to look in AUTHORS and ChangeLog files for a list of changes.
[~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
20 /*
21  * Modified by the GTK+ Team and others 1997-1999.  See the AUTHORS
22  * file for a list of people on the GTK+ Team.  See the ChangeLog
23  * files for a list of changes.  These files are distributed with
24  * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
25  */
26
27 #include <X11/Xlocale.h>        /* so we get the right setlocale */
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <gmodule.h>
32 #include "gtkbutton.h"
33 #include "gtkdnd.h"
34 #include "gtkfeatures.h"
35 #include "gtkhscrollbar.h"
36 #include "gtkhseparator.h"
37 #include "gtkmain.h"
38 #include "gtkpreview.h"
39 #include "gtkrc.h"
40 #include "gtkscrolledwindow.h"
41 #include "gtkselection.h"
42 #include "gtksignal.h"
43 #include "gtktable.h"
44 #include "gtktext.h"
45 #include "gtkvbox.h"
46 #include "gtkvscrollbar.h"
47 #include "gtkwidget.h"
48 #include "gtkwindow.h"
49 #include "gtkprivate.h"
50 #include "gdk/gdki18n.h"
51 #include "config.h"
52 #include "gtkdebug.h"
53 #include "gtkintl.h"
54
55 /* Private type definitions
56  */
57 typedef struct _GtkInitFunction          GtkInitFunction;
58 typedef struct _GtkQuitFunction          GtkQuitFunction;
59 typedef struct _GtkClosure               GtkClosure;
60 typedef struct _GtkKeySnooperData        GtkKeySnooperData;
61
62 struct _GtkInitFunction
63 {
64   GtkFunction function;
65   gpointer data;
66 };
67
68 struct _GtkQuitFunction
69 {
70   guint id;
71   guint main_level;
72   GtkCallbackMarshal marshal;
73   GtkFunction function;
74   gpointer data;
75   GtkDestroyNotify destroy;
76 };
77
78 struct _GtkClosure
79 {
80   GtkCallbackMarshal marshal;
81   gpointer data;
82   GtkDestroyNotify destroy;
83 };
84
85 struct _GtkKeySnooperData
86 {
87   GtkKeySnoopFunc func;
88   gpointer func_data;
89   guint id;
90 };
91
92 static void  gtk_exit_func               (void);
93 static gint  gtk_quit_invoke_function    (GtkQuitFunction    *quitf);
94 static void  gtk_quit_destroy            (GtkQuitFunction    *quitf);
95 static gint  gtk_invoke_key_snoopers     (GtkWidget          *grab_widget,
96                                           GdkEvent           *event);
97
98 static void     gtk_destroy_closure      (gpointer            data);
99 static gboolean gtk_invoke_idle_timeout  (gpointer            data);
100 static void     gtk_invoke_input         (gpointer            data,
101                                           gint                source,
102                                           GdkInputCondition   condition);
103
104 #if 0
105 static void  gtk_error                   (gchar              *str);
106 static void  gtk_warning                 (gchar              *str);
107 static void  gtk_message                 (gchar              *str);
108 static void  gtk_print                   (gchar              *str);
109 #endif
110
111 const guint gtk_major_version = GTK_MAJOR_VERSION;
112 const guint gtk_minor_version = GTK_MINOR_VERSION;
113 const guint gtk_micro_version = GTK_MICRO_VERSION;
114 const guint gtk_binary_age = GTK_BINARY_AGE;
115 const guint gtk_interface_age = GTK_INTERFACE_AGE;
116
117 static guint gtk_main_loop_level = 0;
118 static gint gtk_initialized = FALSE;
119 static GList *current_events = NULL;
120
121 static GSList *main_loops = NULL;      /* stack of currently executing main loops */
122
123 static GSList *grabs = NULL;               /* A stack of unique grabs. The grabbing
124                                             *  widget is the first one on the list.
125                                             */
126 static GList *init_functions = NULL;       /* A list of init functions.
127                                             */
128 static GList *quit_functions = NULL;       /* A list of quit functions.
129                                             */
130 static GMemChunk *quit_mem_chunk = NULL;
131
132 static GSList *key_snoopers = NULL;
133
134 static GdkVisual *gtk_visual;              /* The visual to be used in creating new
135                                             *  widgets.
136                                             */
137 static GdkColormap *gtk_colormap;          /* The colormap to be used in creating new
138                                             *  widgets.
139                                             */
140
141 guint gtk_debug_flags = 0;                 /* Global GTK debug flag */
142
143 #ifdef G_ENABLE_DEBUG
144 static const GDebugKey gtk_debug_keys[] = {
145   {"objects", GTK_DEBUG_OBJECTS},
146   {"misc", GTK_DEBUG_MISC},
147   {"signals", GTK_DEBUG_SIGNALS},
148   {"dnd", GTK_DEBUG_DND},
149   {"plugsocket", GTK_DEBUG_PLUGSOCKET}
150 };
151
152 static const guint gtk_ndebug_keys = sizeof (gtk_debug_keys) / sizeof (GDebugKey);
153
154 #endif /* G_ENABLE_DEBUG */
155
156 gchar*
157 gtk_check_version (guint required_major,
158                    guint required_minor,
159                    guint required_micro)
160 {
161   if (required_major > GTK_MAJOR_VERSION)
162     return "Gtk+ version too old (major mismatch)";
163   if (required_major < GTK_MAJOR_VERSION)
164     return "Gtk+ version too new (major mismatch)";
165   if (required_minor > GTK_MINOR_VERSION)
166     return "Gtk+ version too old (minor mismatch)";
167   if (required_minor < GTK_MINOR_VERSION)
168     return "Gtk+ version too new (minor mismatch)";
169   if (required_micro < GTK_MICRO_VERSION - GTK_BINARY_AGE)
170     return "Gtk+ version too new (micro mismatch)";
171   if (required_micro > GTK_MICRO_VERSION)
172     return "Gtk+ version too old (micro mismatch)";
173   return NULL;
174 }
175
176
177 gboolean
178 gtk_init_check (int      *argc,
179                 char   ***argv)
180 {
181   GSList *gtk_modules = NULL;
182   GSList *slist;
183   gchar *env_string = NULL;
184
185   if (gtk_initialized)
186     return TRUE;
187
188 #if     0
189   g_set_error_handler (gtk_error);
190   g_set_warning_handler (gtk_warning);
191   g_set_message_handler (gtk_message);
192   g_set_print_handler (gtk_print);
193 #endif
194   
195   /* Initialize "gdk". We pass along the 'argc' and 'argv'
196    *  parameters as they contain information that GDK uses
197    */
198   if (!gdk_init_check (argc, argv))
199     return FALSE;
200
201   gdk_event_handler_set ((GdkEventFunc)gtk_main_do_event, NULL, NULL);
202   
203 #ifdef G_ENABLE_DEBUG
204   env_string = getenv ("GTK_DEBUG");
205   if (env_string != NULL)
206     {
207       gtk_debug_flags = g_parse_debug_string (env_string,
208                                               gtk_debug_keys,
209                                               gtk_ndebug_keys);
210       env_string = NULL;
211     }
212 #endif  /* G_ENABLE_DEBUG */
213
214   env_string = getenv ("GTK_MODULES");
215   if (env_string)
216     {
217       gchar **modules, **as;
218
219       modules = g_strsplit (env_string, ":", -1);
220       for (as = modules; *as; as++)
221         {
222           if (**as)
223             gtk_modules = g_slist_prepend (gtk_modules, *as);
224           else
225             g_free (*as);
226         }
227       g_free (modules);
228       env_string = NULL;
229     }
230
231   if (argc && argv)
232     {
233       gint i, j, k;
234       
235       for (i = 1; i < *argc;)
236         {
237           if (strcmp ("--gtk-module", (*argv)[i]) == 0 ||
238               strncmp ("--gtk-module=", (*argv)[i], 13) == 0)
239             {
240               gchar *module_name = (*argv)[i] + 12;
241               
242               if (*module_name == '=')
243                 module_name++;
244               else
245                 {
246                   (*argv)[i] = NULL;
247                   i += 1;
248                   module_name = (*argv)[i];
249                 }
250               (*argv)[i] = NULL;
251
252               gtk_modules = g_slist_prepend (gtk_modules, g_strdup (module_name));
253             }
254           else if (strcmp ("--g-fatal-warnings", (*argv)[i]) == 0)
255             {
256               GLogLevelFlags fatal_mask;
257               
258               fatal_mask = g_log_set_always_fatal (G_LOG_FATAL_MASK);
259               fatal_mask |= G_LOG_LEVEL_WARNING | G_LOG_LEVEL_CRITICAL;
260               g_log_set_always_fatal (fatal_mask);
261               (*argv)[i] = NULL;
262             }
263 #ifdef G_ENABLE_DEBUG
264           else if ((strcmp ("--gtk-debug", (*argv)[i]) == 0) ||
265                    (strncmp ("--gtk-debug=", (*argv)[i], 12) == 0))
266             {
267               gchar *equal_pos = strchr ((*argv)[i], '=');
268               
269               if (equal_pos != NULL)
270                 {
271                   gtk_debug_flags |= g_parse_debug_string (equal_pos+1,
272                                                            gtk_debug_keys,
273                                                            gtk_ndebug_keys);
274                 }
275               else if ((i + 1) < *argc && (*argv)[i + 1])
276                 {
277                   gtk_debug_flags |= g_parse_debug_string ((*argv)[i+1],
278                                                            gtk_debug_keys,
279                                                            gtk_ndebug_keys);
280                   (*argv)[i] = NULL;
281                   i += 1;
282                 }
283               (*argv)[i] = NULL;
284             }
285           else if ((strcmp ("--gtk-no-debug", (*argv)[i]) == 0) ||
286                    (strncmp ("--gtk-no-debug=", (*argv)[i], 15) == 0))
287             {
288               gchar *equal_pos = strchr ((*argv)[i], '=');
289               
290               if (equal_pos != NULL)
291                 {
292                   gtk_debug_flags &= ~g_parse_debug_string (equal_pos+1,
293                                                             gtk_debug_keys,
294                                                             gtk_ndebug_keys);
295                 }
296               else if ((i + 1) < *argc && (*argv)[i + 1])
297                 {
298                   gtk_debug_flags &= ~g_parse_debug_string ((*argv)[i+1],
299                                                             gtk_debug_keys,
300                                                             gtk_ndebug_keys);
301                   (*argv)[i] = NULL;
302                   i += 1;
303                 }
304               (*argv)[i] = NULL;
305             }
306 #endif /* G_ENABLE_DEBUG */
307           i += 1;
308         }
309       
310       for (i = 1; i < *argc; i++)
311         {
312           for (k = i; k < *argc; k++)
313             if ((*argv)[k] != NULL)
314               break;
315           
316           if (k > i)
317             {
318               k -= i;
319               for (j = i + k; j < *argc; j++)
320                 (*argv)[j-k] = (*argv)[j];
321               *argc -= k;
322             }
323         }
324     }
325   
326   /* load gtk modules */
327   gtk_modules = g_slist_reverse (gtk_modules);
328   for (slist = gtk_modules; slist; slist = slist->next)
329     {
330       gchar *module_name;
331       GModule *module = NULL;
332       GtkModuleInitFunc modinit_func = NULL;
333       
334       module_name = slist->data;
335       slist->data = NULL;
336       if (!(module_name[0] == '/' ||
337             (module_name[0] == 'l' &&
338              module_name[1] == 'i' &&
339              module_name[2] == 'b')))
340         {
341           gchar *old = module_name;
342           
343           module_name = g_strconcat ("lib", module_name, ".so", NULL);
344           g_free (old);
345         }
346       if (g_module_supported ())
347         {
348           module = g_module_open (module_name, G_MODULE_BIND_LAZY);
349           if (module &&
350               g_module_symbol (module, "gtk_module_init", (gpointer*) &modinit_func) &&
351               modinit_func)
352             {
353               if (!g_slist_find (gtk_modules, modinit_func))
354                 {
355                   g_module_make_resident (module);
356                   slist->data = modinit_func;
357                 }
358               else
359                 {
360                   g_module_close (module);
361                   module = NULL;
362                 }
363             }
364         }
365       if (!modinit_func)
366         {
367           g_warning ("Failed to load module \"%s\": %s",
368                      module ? g_module_name (module) : module_name,
369                      g_module_error ());
370           if (module)
371             g_module_close (module);
372         }
373       g_free (module_name);
374     }
375
376 #ifdef ENABLE_NLS
377   bindtextdomain("gtk+", GTK_LOCALEDIR);
378 #endif  
379
380   /* Initialize the default visual and colormap to be
381    *  used in creating widgets. (We want to use the system
382    *  defaults so as to be nice to the colormap).
383    */
384   gtk_visual = gdk_visual_get_system ();
385   gtk_colormap = gdk_colormap_get_system ();
386
387   gtk_type_init ();
388   gtk_signal_init ();
389   gtk_rc_init ();
390   
391   
392   /* Register an exit function to make sure we are able to cleanup.
393    */
394   g_atexit (gtk_exit_func);
395   
396   /* Set the 'initialized' flag.
397    */
398   gtk_initialized = TRUE;
399
400   /* initialize gtk modules
401    */
402   for (slist = gtk_modules; slist; slist = slist->next)
403     {
404       if (slist->data)
405         {
406           GtkModuleInitFunc modinit;
407           
408           modinit = slist->data;
409           modinit (argc, argv);
410         }
411     }
412   g_slist_free (gtk_modules);
413
414   return TRUE;
415 }
416
417 void
418 gtk_init (int *argc, char ***argv)
419 {
420   if (!gtk_init_check (argc, argv))
421     {
422       g_warning ("cannot open display: %s", gdk_get_display ());
423       exit(1);
424     }
425 }
426
427 void
428 gtk_exit (int errorcode)
429 {
430   /* Only if "gtk" has been initialized should we de-initialize.
431    */
432   /* de-initialisation is done by the gtk_exit_funct(),
433    * no need to do this here (Alex J.)
434    */
435   gdk_exit(errorcode);
436 }
437
438 gchar*
439 gtk_set_locale (void)
440 {
441   return gdk_set_locale ();
442 }
443
444 void
445 gtk_main (void)
446 {
447   GList *tmp_list;
448   GList *functions;
449   GtkInitFunction *init;
450   GMainLoop *loop;
451
452   gtk_main_loop_level++;
453   
454   loop = g_main_new (TRUE);
455   main_loops = g_slist_prepend (main_loops, loop);
456
457   tmp_list = functions = init_functions;
458   init_functions = NULL;
459   
460   while (tmp_list)
461     {
462       init = tmp_list->data;
463       tmp_list = tmp_list->next;
464       
465       (* init->function) (init->data);
466       g_free (init);
467     }
468   g_list_free (functions);
469
470   if (g_main_is_running (main_loops->data))
471     {
472       GDK_THREADS_LEAVE ();
473       g_main_run (loop);
474       GDK_THREADS_ENTER ();
475       gdk_flush ();
476     }
477
478   if (quit_functions)
479     {
480       GList *reinvoke_list = NULL;
481       GtkQuitFunction *quitf;
482
483       while (quit_functions)
484         {
485           quitf = quit_functions->data;
486
487           quit_functions = g_list_remove_link (quit_functions, quit_functions);
488
489           if ((quitf->main_level && quitf->main_level != gtk_main_loop_level) ||
490               gtk_quit_invoke_function (quitf))
491             {
492               reinvoke_list = g_list_prepend (reinvoke_list, quitf);
493             }
494           else
495             {
496               g_list_free (tmp_list);
497               gtk_quit_destroy (quitf);
498             }
499         }
500       if (reinvoke_list)
501         {
502           GList *work;
503           
504           work = g_list_last (reinvoke_list);
505           if (quit_functions)
506             quit_functions->prev = work;
507           work->next = quit_functions;
508           quit_functions = work;
509         }
510
511       gdk_flush ();
512     }
513               
514   main_loops = g_slist_remove (main_loops, loop);
515
516   g_main_destroy (loop);
517
518   gtk_main_loop_level--;
519 }
520
521 guint
522 gtk_main_level (void)
523 {
524   return gtk_main_loop_level;
525 }
526
527 void
528 gtk_main_quit (void)
529 {
530   g_return_if_fail (main_loops != NULL);
531
532   g_main_quit (main_loops->data);
533 }
534
535 gint
536 gtk_events_pending (void)
537 {
538   return g_main_pending();
539 }
540
541 gint 
542 gtk_main_iteration (void)
543 {
544   g_main_iteration (TRUE);
545
546   if (main_loops)
547     return !g_main_is_running (main_loops->data);
548   else
549     return TRUE;
550 }
551
552 gint 
553 gtk_main_iteration_do (gboolean blocking)
554 {
555   g_main_iteration (blocking);
556
557   if (main_loops)
558     return !g_main_is_running (main_loops->data);
559   else
560     return TRUE;
561 }
562
563 void 
564 gtk_main_do_event (GdkEvent *event)
565 {
566   GtkWidget *event_widget;
567   GtkWidget *grab_widget;
568   GdkEvent *next_event;
569   GList *tmp_list;
570
571   /* If there are any events pending then get the next one.
572    */
573   next_event = gdk_event_peek ();
574   
575   /* Try to compress enter/leave notify events. These event
576    *  pairs occur when the mouse is dragged quickly across
577    *  a window with many buttons (or through a menu). Instead
578    *  of highlighting and de-highlighting each widget that
579    *  is crossed it is better to simply de-highlight the widget
580    *  which contained the mouse initially and highlight the
581    *  widget which ends up containing the mouse.
582    */
583   if (next_event)
584     if (((event->type == GDK_ENTER_NOTIFY) ||
585          (event->type == GDK_LEAVE_NOTIFY)) &&
586         ((next_event->type == GDK_ENTER_NOTIFY) ||
587          (next_event->type == GDK_LEAVE_NOTIFY)) &&
588         (next_event->type != event->type) &&
589         (next_event->any.window == event->any.window))
590       {
591         /* Throw both the peeked copy and the queued copy away 
592          */
593         gdk_event_free (next_event);
594         next_event = gdk_event_get ();
595         gdk_event_free (next_event);
596         
597         return;
598       }
599
600   if (next_event)
601     gdk_event_free (next_event);
602
603   /* Find the widget which got the event. We store the widget
604    *  in the user_data field of GdkWindow's.
605    *  Ignore the event if we don't have a widget for it, except
606    *  for GDK_PROPERTY_NOTIFY events which are handled specialy.
607    *  Though this happens rarely, bogus events can occour
608    *  for e.g. destroyed GdkWindows. 
609    */
610   event_widget = gtk_get_event_widget (event);
611   if (!event_widget)
612     {
613       /* To handle selection INCR transactions, we select
614        * PropertyNotify events on the requestor window and create
615        * a corresponding (fake) GdkWindow so that events get
616        * here. There won't be a widget though, so we have to handle
617            * them specially
618            */
619       if (event->type == GDK_PROPERTY_NOTIFY)
620         gtk_selection_incr_event (event->any.window,
621                                   &event->property);
622       
623       return;
624     }
625   
626   /* Push the event onto a stack of current events for
627    * gtk_current_event_get().
628    */
629   current_events = g_list_prepend (current_events, event);
630   
631   /* If there is a grab in effect...
632    */
633   if (grabs)
634     {
635       grab_widget = grabs->data;
636       
637       /* If the grab widget is an ancestor of the event widget
638        *  then we send the event to the original event widget.
639        *  This is the key to implementing modality.
640        */
641       if (GTK_WIDGET_IS_SENSITIVE (event_widget) &&
642           gtk_widget_is_ancestor (event_widget, grab_widget))
643         grab_widget = event_widget;
644     }
645   else
646     {
647       grab_widget = event_widget;
648     }
649
650   /* Not all events get sent to the grabbing widget.
651    * The delete, destroy, expose, focus change and resize
652    *  events still get sent to the event widget because
653    *  1) these events have no meaning for the grabbing widget
654    *  and 2) redirecting these events to the grabbing widget
655    *  could cause the display to be messed up.
656    * 
657    * Drag events are also not redirected, since it isn't
658    *  clear what the semantics of that would be.
659    */
660   switch (event->type)
661     {
662     case GDK_NOTHING:
663       break;
664       
665     case GDK_DELETE:
666       gtk_widget_ref (event_widget);
667       if (!gtk_widget_event (event_widget, event) &&
668           !GTK_OBJECT_DESTROYED (event_widget))
669         gtk_widget_destroy (event_widget);
670       gtk_widget_unref (event_widget);
671       break;
672       
673     case GDK_DESTROY:
674       gtk_widget_ref (event_widget);
675       gtk_widget_event (event_widget, event);
676       if (!GTK_OBJECT_DESTROYED (event_widget))
677         gtk_widget_destroy (event_widget);
678       gtk_widget_unref (event_widget);
679       break;
680       
681     case GDK_PROPERTY_NOTIFY:
682     case GDK_EXPOSE:
683     case GDK_NO_EXPOSE:
684     case GDK_FOCUS_CHANGE:
685     case GDK_CONFIGURE:
686     case GDK_MAP:
687     case GDK_UNMAP:
688     case GDK_SELECTION_CLEAR:
689     case GDK_SELECTION_REQUEST:
690     case GDK_SELECTION_NOTIFY:
691     case GDK_CLIENT_EVENT:
692     case GDK_VISIBILITY_NOTIFY:
693       gtk_widget_event (event_widget, event);
694       break;
695
696     case GDK_BUTTON_PRESS:
697     case GDK_2BUTTON_PRESS:
698     case GDK_3BUTTON_PRESS:
699     /* We treat button 4-5 specially, assume we have
700      * a MS-style scrollwheel mouse, and try to find
701      * a plausible widget to scroll. We also trap
702      * button 4-5 double and triple clicks here, since
703      * they will be generated if the user scrolls quickly.
704      */
705       if ((grab_widget == event_widget) &&
706           (event->button.button == 4 || event->button.button == 5))
707         {
708           GtkWidget *range = NULL;
709           GtkWidget *scrollwin;
710           
711           if (GTK_IS_RANGE (event_widget))
712             range = event_widget;
713           else
714             {
715               scrollwin = gtk_widget_get_ancestor (event_widget,
716                                                    GTK_TYPE_SCROLLED_WINDOW);
717               if (scrollwin)
718                 range = GTK_SCROLLED_WINDOW (scrollwin)->vscrollbar;
719             }
720           
721           if (range && GTK_WIDGET_VISIBLE (range))
722             {
723               if (event->type == GDK_BUTTON_PRESS)
724                 {
725                   GtkAdjustment *adj = GTK_RANGE (range)->adjustment;
726                   gfloat new_value = adj->value + ((event->button.button == 4) ? 
727                                                    -adj->page_increment / 2: 
728                                                     adj->page_increment / 2);
729                   new_value = CLAMP (new_value, adj->lower, adj->upper - adj->page_size);
730                   gtk_adjustment_set_value (adj, new_value);
731                 }
732               break;
733             }
734         }
735       gtk_propagate_event (grab_widget, event);
736       break;
737
738     case GDK_KEY_PRESS:
739     case GDK_KEY_RELEASE:
740       if (key_snoopers)
741         {
742           if (gtk_invoke_key_snoopers (grab_widget, event))
743             break;
744         }
745       /* else fall through */
746     case GDK_MOTION_NOTIFY:
747     case GDK_BUTTON_RELEASE:
748     case GDK_PROXIMITY_IN:
749     case GDK_PROXIMITY_OUT:
750       gtk_propagate_event (grab_widget, event);
751       break;
752       
753     case GDK_ENTER_NOTIFY:
754       if (GTK_WIDGET_IS_SENSITIVE (grab_widget))
755         {
756           gtk_widget_event (grab_widget, event);
757           if (event_widget == grab_widget)
758             GTK_PRIVATE_SET_FLAG (event_widget, GTK_LEAVE_PENDING);
759         }
760       break;
761       
762     case GDK_LEAVE_NOTIFY:
763       if (GTK_WIDGET_LEAVE_PENDING (event_widget))
764         {
765           GTK_PRIVATE_UNSET_FLAG (event_widget, GTK_LEAVE_PENDING);
766           gtk_widget_event (event_widget, event);
767         }
768       else if (GTK_WIDGET_IS_SENSITIVE (grab_widget))
769         gtk_widget_event (grab_widget, event);
770       break;
771       
772     case GDK_DRAG_STATUS:
773     case GDK_DROP_FINISHED:
774       gtk_drag_source_handle_event (event_widget, event);
775       break;
776     case GDK_DRAG_ENTER:
777     case GDK_DRAG_LEAVE:
778     case GDK_DRAG_MOTION:
779     case GDK_DROP_START:
780       gtk_drag_dest_handle_event (event_widget, event);
781       break;
782     }
783   
784   tmp_list = current_events;
785   current_events = g_list_remove_link (current_events, tmp_list);
786   g_list_free_1 (tmp_list);
787 }
788
789 gint
790 gtk_true (void)
791 {
792   return TRUE;
793 }
794
795 gint
796 gtk_false (void)
797 {
798   return FALSE;
799 }
800
801 void
802 gtk_grab_add (GtkWidget *widget)
803 {
804   g_return_if_fail (widget != NULL);
805   
806   if (!GTK_WIDGET_HAS_GRAB (widget))
807     {
808       GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_GRAB);
809       
810       grabs = g_slist_prepend (grabs, widget);
811       gtk_widget_ref (widget);
812     }
813 }
814
815 GtkWidget*
816 gtk_grab_get_current (void)
817 {
818   if (grabs)
819     return GTK_WIDGET (grabs->data);
820   return NULL;
821 }
822
823 void
824 gtk_grab_remove (GtkWidget *widget)
825 {
826   g_return_if_fail (widget != NULL);
827   
828   if (GTK_WIDGET_HAS_GRAB (widget))
829     {
830       GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_GRAB);
831       
832       grabs = g_slist_remove (grabs, widget);
833       gtk_widget_unref (widget);
834     }
835 }
836
837 void
838 gtk_init_add (GtkFunction function,
839               gpointer    data)
840 {
841   GtkInitFunction *init;
842   
843   init = g_new (GtkInitFunction, 1);
844   init->function = function;
845   init->data = data;
846   
847   init_functions = g_list_prepend (init_functions, init);
848 }
849
850 guint
851 gtk_key_snooper_install (GtkKeySnoopFunc snooper,
852                          gpointer        func_data)
853 {
854   GtkKeySnooperData *data;
855   static guint snooper_id = 1;
856
857   g_return_val_if_fail (snooper != NULL, 0);
858
859   data = g_new (GtkKeySnooperData, 1);
860   data->func = snooper;
861   data->func_data = func_data;
862   data->id = snooper_id++;
863   key_snoopers = g_slist_prepend (key_snoopers, data);
864
865   return data->id;
866 }
867
868 void
869 gtk_key_snooper_remove (guint            snooper_id)
870 {
871   GtkKeySnooperData *data = NULL;
872   GSList *slist;
873
874   slist = key_snoopers;
875   while (slist)
876     {
877       data = slist->data;
878       if (data->id == snooper_id)
879         break;
880
881       slist = slist->next;
882       data = NULL;
883     }
884   if (data)
885     key_snoopers = g_slist_remove (key_snoopers, data);
886 }
887
888 static gint
889 gtk_invoke_key_snoopers (GtkWidget *grab_widget,
890                          GdkEvent  *event)
891 {
892   GSList *slist;
893   gint return_val = FALSE;
894
895   slist = key_snoopers;
896   while (slist && !return_val)
897     {
898       GtkKeySnooperData *data;
899
900       data = slist->data;
901       slist = slist->next;
902       return_val = (*data->func) (grab_widget, (GdkEventKey*) event, data->func_data);
903     }
904
905   return return_val;
906 }
907
908 guint
909 gtk_quit_add_full (guint                main_level,
910                    GtkFunction          function,
911                    GtkCallbackMarshal   marshal,
912                    gpointer             data,
913                    GtkDestroyNotify     destroy)
914 {
915   static guint quit_id = 1;
916   GtkQuitFunction *quitf;
917   
918   g_return_val_if_fail ((function != NULL) || (marshal != NULL), 0);
919
920   if (!quit_mem_chunk)
921     quit_mem_chunk = g_mem_chunk_new ("quit mem chunk", sizeof (GtkQuitFunction),
922                                       512, G_ALLOC_AND_FREE);
923   
924   quitf = g_chunk_new (GtkQuitFunction, quit_mem_chunk);
925   
926   quitf->id = quit_id++;
927   quitf->main_level = main_level;
928   quitf->function = function;
929   quitf->marshal = marshal;
930   quitf->data = data;
931   quitf->destroy = destroy;
932
933   quit_functions = g_list_prepend (quit_functions, quitf);
934   
935   return quitf->id;
936 }
937
938 static void
939 gtk_quit_destroy (GtkQuitFunction *quitf)
940 {
941   if (quitf->destroy)
942     quitf->destroy (quitf->data);
943   g_mem_chunk_free (quit_mem_chunk, quitf);
944 }
945
946 static gint
947 gtk_quit_destructor (GtkObject **object_p)
948 {
949   if (*object_p)
950     gtk_object_destroy (*object_p);
951   g_free (object_p);
952
953   return FALSE;
954 }
955
956 void
957 gtk_quit_add_destroy (guint              main_level,
958                       GtkObject         *object)
959 {
960   GtkObject **object_p;
961
962   g_return_if_fail (main_level > 0);
963   g_return_if_fail (object != NULL);
964   g_return_if_fail (GTK_IS_OBJECT (object));
965
966   object_p = g_new (GtkObject*, 1);
967   *object_p = object;
968   gtk_signal_connect (object,
969                       "destroy",
970                       GTK_SIGNAL_FUNC (gtk_widget_destroyed),
971                       object_p);
972   gtk_quit_add (main_level, (GtkFunction) gtk_quit_destructor, object_p);
973 }
974
975 guint
976 gtk_quit_add (guint       main_level,
977               GtkFunction function,
978               gpointer    data)
979 {
980   return gtk_quit_add_full (main_level, function, NULL, data, NULL);
981 }
982
983 void
984 gtk_quit_remove (guint id)
985 {
986   GtkQuitFunction *quitf;
987   GList *tmp_list;
988   
989   tmp_list = quit_functions;
990   while (tmp_list)
991     {
992       quitf = tmp_list->data;
993       
994       if (quitf->id == id)
995         {
996           quit_functions = g_list_remove_link (quit_functions, tmp_list);
997           g_list_free (tmp_list);
998           gtk_quit_destroy (quitf);
999           
1000           return;
1001         }
1002       
1003       tmp_list = tmp_list->next;
1004     }
1005 }
1006
1007 void
1008 gtk_quit_remove_by_data (gpointer data)
1009 {
1010   GtkQuitFunction *quitf;
1011   GList *tmp_list;
1012   
1013   tmp_list = quit_functions;
1014   while (tmp_list)
1015     {
1016       quitf = tmp_list->data;
1017       
1018       if (quitf->data == data)
1019         {
1020           quit_functions = g_list_remove_link (quit_functions, tmp_list);
1021           g_list_free (tmp_list);
1022           gtk_quit_destroy (quitf);
1023
1024           return;
1025         }
1026       
1027       tmp_list = tmp_list->next;
1028     }
1029 }
1030
1031 guint
1032 gtk_timeout_add_full (guint32            interval,
1033                       GtkFunction        function,
1034                       GtkCallbackMarshal marshal,
1035                       gpointer           data,
1036                       GtkDestroyNotify   destroy)
1037 {
1038   if (marshal)
1039     {
1040       GtkClosure *closure;
1041
1042       closure = g_new (GtkClosure, 1);
1043       closure->marshal = marshal;
1044       closure->data = data;
1045       closure->destroy = destroy;
1046
1047       return g_timeout_add_full (0, interval, 
1048                                  gtk_invoke_idle_timeout,
1049                                  closure,
1050                                  gtk_destroy_closure);
1051     }
1052   else
1053     return g_timeout_add_full (0, interval, function, data, destroy);
1054 }
1055
1056 guint
1057 gtk_timeout_add (guint32     interval,
1058                  GtkFunction function,
1059                  gpointer    data)
1060 {
1061   return g_timeout_add_full (0, interval, function, data, NULL);
1062 }
1063
1064 void
1065 gtk_timeout_remove (guint tag)
1066 {
1067   g_source_remove (tag);
1068 }
1069
1070 guint
1071 gtk_idle_add_full (gint                 priority,
1072                    GtkFunction          function,
1073                    GtkCallbackMarshal   marshal,
1074                    gpointer             data,
1075                    GtkDestroyNotify     destroy)
1076 {
1077   if (marshal)
1078     {
1079       GtkClosure *closure;
1080
1081       closure = g_new (GtkClosure, 1);
1082       closure->marshal = marshal;
1083       closure->data = data;
1084       closure->destroy = destroy;
1085
1086       return g_idle_add_full (priority,
1087                               gtk_invoke_idle_timeout,
1088                               closure,
1089                               gtk_destroy_closure);
1090     }
1091   else
1092     return g_idle_add_full (priority, function, data, destroy);
1093 }
1094
1095 guint
1096 gtk_idle_add (GtkFunction function,
1097               gpointer    data)
1098 {
1099   return g_idle_add_full (GTK_PRIORITY_DEFAULT, function, data, NULL);
1100 }
1101
1102 guint       
1103 gtk_idle_add_priority   (gint               priority,
1104                          GtkFunction        function,
1105                          gpointer           data)
1106 {
1107   return g_idle_add_full (priority, function, data, NULL);
1108 }
1109
1110 void
1111 gtk_idle_remove (guint tag)
1112 {
1113   g_source_remove (tag);
1114 }
1115
1116 void
1117 gtk_idle_remove_by_data (gpointer data)
1118 {
1119   if (!g_idle_remove_by_data (data))
1120     g_warning ("gtk_idle_remove_by_data(%p): no such idle", data);
1121 }
1122
1123 guint
1124 gtk_input_add_full (gint                source,
1125                     GdkInputCondition   condition,
1126                     GdkInputFunction    function,
1127                     GtkCallbackMarshal  marshal,
1128                     gpointer            data,
1129                     GtkDestroyNotify    destroy)
1130 {
1131   if (marshal)
1132     {
1133       GtkClosure *closure;
1134
1135       closure = g_new (GtkClosure, 1);
1136       closure->marshal = marshal;
1137       closure->data = data;
1138       closure->destroy = destroy;
1139
1140       return gdk_input_add_full (source,
1141                                  condition,
1142                                  (GdkInputFunction) gtk_invoke_input,
1143                                  closure,
1144                                  (GdkDestroyNotify) gtk_destroy_closure);
1145     }
1146   else
1147     return gdk_input_add_full (source, condition, function, data, destroy);
1148 }
1149
1150 void
1151 gtk_input_remove (guint tag)
1152 {
1153   g_source_remove (tag);
1154 }
1155
1156 static void
1157 gtk_destroy_closure (gpointer data)
1158 {
1159   GtkClosure *closure = data;
1160
1161   if (closure->destroy)
1162     (closure->destroy) (closure->data);
1163   g_free (closure);
1164 }
1165
1166 static gboolean
1167 gtk_invoke_idle_timeout (gpointer data)
1168 {
1169   GtkClosure *closure = data;
1170
1171   GtkArg args[1];
1172   gint ret_val = FALSE;
1173   args[0].name = NULL;
1174   args[0].type = GTK_TYPE_BOOL;
1175   args[0].d.pointer_data = &ret_val;
1176   closure->marshal (NULL, closure->data,  0, args);
1177   return ret_val;
1178 }
1179
1180 static void
1181 gtk_invoke_input (gpointer          data,
1182                   gint              source,
1183                   GdkInputCondition condition)
1184 {
1185   GtkClosure *closure = data;
1186
1187   GtkArg args[3];
1188   args[0].type = GTK_TYPE_INT;
1189   args[0].name = NULL;
1190   GTK_VALUE_INT(args[0]) = source;
1191   args[1].type = GTK_TYPE_GDK_INPUT_CONDITION;
1192   args[1].name = NULL;
1193   GTK_VALUE_FLAGS(args[1]) = condition;
1194   args[2].type = GTK_TYPE_NONE;
1195   args[2].name = NULL;
1196
1197   closure->marshal (NULL, closure->data, 2, args);
1198 }
1199
1200 GdkEvent*
1201 gtk_get_current_event (void)
1202 {
1203   if (current_events)
1204     return gdk_event_copy ((GdkEvent *) current_events->data);
1205   else
1206     return NULL;
1207 }
1208
1209 GtkWidget*
1210 gtk_get_event_widget (GdkEvent *event)
1211 {
1212   GtkWidget *widget;
1213
1214   widget = NULL;
1215   if (event && event->any.window)
1216     gdk_window_get_user_data (event->any.window, (void**) &widget);
1217   
1218   return widget;
1219 }
1220
1221 static void
1222 gtk_exit_func (void)
1223 {
1224   if (gtk_initialized)
1225     {
1226       gtk_initialized = FALSE;
1227       gtk_preview_uninit ();
1228     }
1229 }
1230
1231
1232 static gint
1233 gtk_quit_invoke_function (GtkQuitFunction *quitf)
1234 {
1235   if (!quitf->marshal)
1236     return quitf->function (quitf->data);
1237   else
1238     {
1239       GtkArg args[1];
1240       gint ret_val = FALSE;
1241
1242       args[0].name = NULL;
1243       args[0].type = GTK_TYPE_BOOL;
1244       args[0].d.pointer_data = &ret_val;
1245       ((GtkCallbackMarshal) quitf->marshal) (NULL,
1246                                              quitf->data,
1247                                              0, args);
1248       return ret_val;
1249     }
1250 }
1251
1252 void
1253 gtk_propagate_event (GtkWidget *widget,
1254                      GdkEvent  *event)
1255 {
1256   gint handled_event;
1257   
1258   g_return_if_fail (widget != NULL);
1259   g_return_if_fail (GTK_IS_WIDGET (widget));
1260   g_return_if_fail (event != NULL);
1261   
1262   handled_event = FALSE;
1263
1264   if ((event->type == GDK_KEY_PRESS) ||
1265       (event->type == GDK_KEY_RELEASE))
1266     {
1267       /* Only send key events within Window widgets to the Window
1268        *  The Window widget will in turn pass the
1269        *  key event on to the currently focused widget
1270        *  for that window.
1271        */
1272       GtkWidget *window;
1273
1274       window = gtk_widget_get_ancestor (widget, GTK_TYPE_WINDOW);
1275       if (window)
1276         {
1277           if (GTK_WIDGET_IS_SENSITIVE (window))
1278             gtk_widget_event (window, event);
1279
1280           handled_event = TRUE; /* don't send to widget */
1281         }
1282     }
1283   
1284   /* Other events get propagated up the widget tree
1285    *  so that parents can see the button and motion
1286    *  events of the children.
1287    */
1288   while (!handled_event && widget)
1289     {
1290       GtkWidget *tmp;
1291
1292       gtk_widget_ref (widget);
1293       handled_event = !GTK_WIDGET_IS_SENSITIVE (widget) || gtk_widget_event (widget, event);
1294       tmp = widget->parent;
1295       gtk_widget_unref (widget);
1296       widget  = tmp;
1297     }
1298 }
1299
1300 #if 0
1301 static void
1302 gtk_error (gchar *str)
1303 {
1304   gtk_print (str);
1305 }
1306
1307 static void
1308 gtk_warning (gchar *str)
1309 {
1310   gtk_print (str);
1311 }
1312
1313 static void
1314 gtk_message (gchar *str)
1315 {
1316   gtk_print (str);
1317 }
1318
1319 static void
1320 gtk_print (gchar *str)
1321 {
1322   static GtkWidget *window = NULL;
1323   static GtkWidget *text;
1324   static int level = 0;
1325   GtkWidget *box1;
1326   GtkWidget *box2;
1327   GtkWidget *table;
1328   GtkWidget *hscrollbar;
1329   GtkWidget *vscrollbar;
1330   GtkWidget *separator;
1331   GtkWidget *button;
1332   
1333   if (level > 0)
1334     {
1335       fputs (str, stdout);
1336       fflush (stdout);
1337       return;
1338     }
1339   
1340   if (!window)
1341     {
1342       window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
1343       
1344       gtk_signal_connect (GTK_OBJECT (window), "destroy",
1345                           (GtkSignalFunc) gtk_widget_destroyed,
1346                           &window);
1347       
1348       gtk_window_set_title (GTK_WINDOW (window), "Messages");
1349       
1350       box1 = gtk_vbox_new (FALSE, 0);
1351       gtk_container_add (GTK_CONTAINER (window), box1);
1352       gtk_widget_show (box1);
1353       
1354       
1355       box2 = gtk_vbox_new (FALSE, 10);
1356       gtk_container_set_border_width (GTK_CONTAINER (box2), 10);
1357       gtk_box_pack_start (GTK_BOX (box1), box2, TRUE, TRUE, 0);
1358       gtk_widget_show (box2);
1359       
1360       
1361       table = gtk_table_new (2, 2, FALSE);
1362       gtk_table_set_row_spacing (GTK_TABLE (table), 0, 2);
1363       gtk_table_set_col_spacing (GTK_TABLE (table), 0, 2);
1364       gtk_box_pack_start (GTK_BOX (box2), table, TRUE, TRUE, 0);
1365       gtk_widget_show (table);
1366       
1367       text = gtk_text_new (NULL, NULL);
1368       gtk_text_set_editable (GTK_TEXT (text), FALSE);
1369       gtk_table_attach_defaults (GTK_TABLE (table), text, 0, 1, 0, 1);
1370       gtk_widget_show (text);
1371       gtk_widget_realize (text);
1372       
1373       hscrollbar = gtk_hscrollbar_new (GTK_TEXT (text)->hadj);
1374       gtk_table_attach (GTK_TABLE (table), hscrollbar, 0, 1, 1, 2,
1375                         GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
1376       gtk_widget_show (hscrollbar);
1377       
1378       vscrollbar = gtk_vscrollbar_new (GTK_TEXT (text)->vadj);
1379       gtk_table_attach (GTK_TABLE (table), vscrollbar, 1, 2, 0, 1,
1380                         GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
1381       gtk_widget_show (vscrollbar);
1382       
1383       separator = gtk_hseparator_new ();
1384       gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 0);
1385       gtk_widget_show (separator);
1386       
1387       
1388       box2 = gtk_vbox_new (FALSE, 10);
1389       gtk_container_set_border_width (GTK_CONTAINER (box2), 10);
1390       gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, TRUE, 0);
1391       gtk_widget_show (box2);
1392       
1393       
1394       button = gtk_button_new_with_label ("close");
1395       gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
1396                                  (GtkSignalFunc) gtk_widget_hide,
1397                                  GTK_OBJECT (window));
1398       gtk_box_pack_start (GTK_BOX (box2), button, TRUE, TRUE, 0);
1399       GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
1400       gtk_widget_grab_default (button);
1401       gtk_widget_show (button);
1402     }
1403   
1404   level += 1;
1405   gtk_text_insert (GTK_TEXT (text), NULL, NULL, NULL, str, -1);
1406   level -= 1;
1407   
1408   if (!GTK_WIDGET_VISIBLE (window))
1409     gtk_widget_show (window);
1410 }
1411 #endif