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