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