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