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