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