]> Pileus Git - ~andy/gtk/blob - gtk/gtkmain.c
Updated Korean translation
[~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 #if GDK_WINDOWING == 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   bindtextdomain("gtk+", GTK_LOCALEDIR);
405 #endif  
406
407   /* Initialize the default visual and colormap to be
408    *  used in creating widgets. (We want to use the system
409    *  defaults so as to be nice to the colormap).
410    */
411   gtk_visual = gdk_visual_get_system ();
412   gtk_colormap = gdk_colormap_get_system ();
413
414   gtk_type_init ();
415   gtk_object_post_arg_parsing_init ();
416   gtk_signal_init ();
417   gtk_rc_init ();
418   
419   
420   /* Register an exit function to make sure we are able to cleanup.
421    */
422   g_atexit (gtk_exit_func);
423   
424   /* Set the 'initialized' flag.
425    */
426   gtk_initialized = TRUE;
427
428   /* initialize gtk modules
429    */
430   for (slist = gtk_modules; slist; slist = slist->next)
431     {
432       if (slist->data)
433         {
434           GtkModuleInitFunc modinit;
435           
436           modinit = slist->data;
437           modinit (argc, argv);
438         }
439     }
440   g_slist_free (gtk_modules);
441   
442   g_warning (""              "YOU ARE USING THE DEVEL BRANCH 1.3.x OF GTK+ WHICH IS CURRENTLY\n"
443              "                UNDER HEAVY DEVELOPMENT AND FREQUENTLY INTRODUCES INSTABILITIES.\n"
444              "                if you don't know why you are getting this, you probably want to\n"
445              "                use the stable branch which can be retrived from\n"
446              "                ftp://ftp.gtk.org/pub/gtk/v1.2/ or via CVS with\n"
447              "                cvs checkout -r glib-1-2 glib; cvs checkout -r gtk-1-2 gtk+");
448   
449   return TRUE;
450 }
451 void
452 gtk_init (int *argc, char ***argv)
453 {
454   if (!gtk_init_check (argc, argv))
455     {
456       g_warning ("cannot open display: %s", gdk_get_display ());
457       exit(1);
458     }
459 }
460
461 void
462 gtk_exit (int errorcode)
463 {
464   /* Only if "gtk" has been initialized should we de-initialize.
465    */
466   /* de-initialisation is done by the gtk_exit_funct(),
467    * no need to do this here (Alex J.)
468    */
469   gdk_exit(errorcode);
470 }
471
472 gchar*
473 gtk_set_locale (void)
474 {
475   return gdk_set_locale ();
476 }
477
478 void
479 gtk_main (void)
480 {
481   GList *tmp_list;
482   GList *functions;
483   GtkInitFunction *init;
484   GMainLoop *loop;
485
486   gtk_main_loop_level++;
487   
488   loop = g_main_new (TRUE);
489   main_loops = g_slist_prepend (main_loops, loop);
490
491   tmp_list = functions = init_functions;
492   init_functions = NULL;
493   
494   while (tmp_list)
495     {
496       init = tmp_list->data;
497       tmp_list = tmp_list->next;
498       
499       (* init->function) (init->data);
500       g_free (init);
501     }
502   g_list_free (functions);
503
504   if (g_main_is_running (main_loops->data))
505     {
506       GDK_THREADS_LEAVE ();
507       g_main_run (loop);
508       GDK_THREADS_ENTER ();
509       gdk_flush ();
510     }
511
512   if (quit_functions)
513     {
514       GList *reinvoke_list = NULL;
515       GtkQuitFunction *quitf;
516
517       while (quit_functions)
518         {
519           quitf = quit_functions->data;
520
521           quit_functions = g_list_remove_link (quit_functions, quit_functions);
522
523           if ((quitf->main_level && quitf->main_level != gtk_main_loop_level) ||
524               gtk_quit_invoke_function (quitf))
525             {
526               reinvoke_list = g_list_prepend (reinvoke_list, quitf);
527             }
528           else
529             {
530               g_list_free (tmp_list);
531               gtk_quit_destroy (quitf);
532             }
533         }
534       if (reinvoke_list)
535         {
536           GList *work;
537           
538           work = g_list_last (reinvoke_list);
539           if (quit_functions)
540             quit_functions->prev = work;
541           work->next = quit_functions;
542           quit_functions = work;
543         }
544
545       gdk_flush ();
546     }
547               
548   main_loops = g_slist_remove (main_loops, loop);
549
550   g_main_destroy (loop);
551
552   gtk_main_loop_level--;
553 }
554
555 guint
556 gtk_main_level (void)
557 {
558   return gtk_main_loop_level;
559 }
560
561 void
562 gtk_main_quit (void)
563 {
564   g_return_if_fail (main_loops != NULL);
565
566   g_main_quit (main_loops->data);
567 }
568
569 gint
570 gtk_events_pending (void)
571 {
572   return g_main_pending();
573 }
574
575 gint 
576 gtk_main_iteration (void)
577 {
578   g_main_iteration (TRUE);
579
580   if (main_loops)
581     return !g_main_is_running (main_loops->data);
582   else
583     return TRUE;
584 }
585
586 gint 
587 gtk_main_iteration_do (gboolean blocking)
588 {
589   g_main_iteration (blocking);
590
591   if (main_loops)
592     return !g_main_is_running (main_loops->data);
593   else
594     return TRUE;
595 }
596
597 void 
598 gtk_main_do_event (GdkEvent *event)
599 {
600   GtkWidget *event_widget;
601   GtkWidget *grab_widget;
602   GdkEvent *next_event;
603   GList *tmp_list;
604
605   /* If there are any events pending then get the next one.
606    */
607   next_event = gdk_event_peek ();
608   
609   /* Try to compress enter/leave notify events. These event
610    *  pairs occur when the mouse is dragged quickly across
611    *  a window with many buttons (or through a menu). Instead
612    *  of highlighting and de-highlighting each widget that
613    *  is crossed it is better to simply de-highlight the widget
614    *  which contained the mouse initially and highlight the
615    *  widget which ends up containing the mouse.
616    */
617   if (next_event)
618     if (((event->type == GDK_ENTER_NOTIFY) ||
619          (event->type == GDK_LEAVE_NOTIFY)) &&
620         ((next_event->type == GDK_ENTER_NOTIFY) ||
621          (next_event->type == GDK_LEAVE_NOTIFY)) &&
622         (next_event->type != event->type) &&
623         (next_event->any.window == event->any.window))
624       {
625         /* Throw both the peeked copy and the queued copy away 
626          */
627         gdk_event_free (next_event);
628         next_event = gdk_event_get ();
629         gdk_event_free (next_event);
630         
631         return;
632       }
633
634   if (next_event)
635     gdk_event_free (next_event);
636
637   /* Find the widget which got the event. We store the widget
638    *  in the user_data field of GdkWindow's.
639    *  Ignore the event if we don't have a widget for it, except
640    *  for GDK_PROPERTY_NOTIFY events which are handled specialy.
641    *  Though this happens rarely, bogus events can occour
642    *  for e.g. destroyed GdkWindows. 
643    */
644   event_widget = gtk_get_event_widget (event);
645   if (!event_widget)
646     {
647       /* To handle selection INCR transactions, we select
648        * PropertyNotify events on the requestor window and create
649        * a corresponding (fake) GdkWindow so that events get
650        * here. There won't be a widget though, so we have to handle
651            * them specially
652            */
653       if (event->type == GDK_PROPERTY_NOTIFY)
654         gtk_selection_incr_event (event->any.window,
655                                   &event->property);
656       
657       return;
658     }
659   
660   /* Push the event onto a stack of current events for
661    * gtk_current_event_get().
662    */
663   current_events = g_list_prepend (current_events, event);
664   
665   /* If there is a grab in effect...
666    */
667   if (grabs)
668     {
669       grab_widget = grabs->data;
670       
671       /* If the grab widget is an ancestor of the event widget
672        *  then we send the event to the original event widget.
673        *  This is the key to implementing modality.
674        */
675       if (GTK_WIDGET_IS_SENSITIVE (event_widget) &&
676           gtk_widget_is_ancestor (event_widget, grab_widget))
677         grab_widget = event_widget;
678     }
679   else
680     {
681       grab_widget = event_widget;
682     }
683
684   /* Not all events get sent to the grabbing widget.
685    * The delete, destroy, expose, focus change and resize
686    *  events still get sent to the event widget because
687    *  1) these events have no meaning for the grabbing widget
688    *  and 2) redirecting these events to the grabbing widget
689    *  could cause the display to be messed up.
690    * 
691    * Drag events are also not redirected, since it isn't
692    *  clear what the semantics of that would be.
693    */
694   switch (event->type)
695     {
696     case GDK_NOTHING:
697       break;
698       
699     case GDK_DELETE:
700       gtk_widget_ref (event_widget);
701       if (!gtk_widget_event (event_widget, event) &&
702           !GTK_OBJECT_DESTROYED (event_widget))
703         gtk_widget_destroy (event_widget);
704       gtk_widget_unref (event_widget);
705       break;
706       
707     case GDK_DESTROY:
708       gtk_widget_ref (event_widget);
709       gtk_widget_event (event_widget, event);
710       if (!GTK_OBJECT_DESTROYED (event_widget))
711         gtk_widget_destroy (event_widget);
712       gtk_widget_unref (event_widget);
713       break;
714       
715     case GDK_PROPERTY_NOTIFY:
716     case GDK_EXPOSE:
717     case GDK_NO_EXPOSE:
718     case GDK_FOCUS_CHANGE:
719     case GDK_CONFIGURE:
720     case GDK_MAP:
721     case GDK_UNMAP:
722     case GDK_SELECTION_CLEAR:
723     case GDK_SELECTION_REQUEST:
724     case GDK_SELECTION_NOTIFY:
725     case GDK_CLIENT_EVENT:
726     case GDK_VISIBILITY_NOTIFY:
727       gtk_widget_event (event_widget, event);
728       break;
729
730     case GDK_BUTTON_PRESS:
731     case GDK_2BUTTON_PRESS:
732     case GDK_3BUTTON_PRESS:
733     /* We treat button 4-5 specially, assume we have
734      * a MS-style scrollwheel mouse, and try to find
735      * a plausible widget to scroll. We also trap
736      * button 4-5 double and triple clicks here, since
737      * they will be generated if the user scrolls quickly.
738      */
739       if ((grab_widget == event_widget) &&
740           (event->button.button == 4 || event->button.button == 5))
741         {
742           GtkWidget *range = NULL;
743           GtkWidget *scrollwin;
744           
745           if (GTK_IS_RANGE (event_widget))
746             range = event_widget;
747           else
748             {
749               scrollwin = gtk_widget_get_ancestor (event_widget,
750                                                    GTK_TYPE_SCROLLED_WINDOW);
751               if (scrollwin)
752                 range = GTK_SCROLLED_WINDOW (scrollwin)->vscrollbar;
753             }
754           
755           if (range && GTK_WIDGET_VISIBLE (range))
756             {
757               if (event->type == GDK_BUTTON_PRESS)
758                 {
759                   GtkAdjustment *adj = GTK_RANGE (range)->adjustment;
760                   gfloat new_value = adj->value + ((event->button.button == 4) ? 
761                                                    -adj->page_increment / 2: 
762                                                     adj->page_increment / 2);
763                   new_value = CLAMP (new_value, adj->lower, adj->upper - adj->page_size);
764                   gtk_adjustment_set_value (adj, new_value);
765                 }
766               break;
767             }
768         }
769       gtk_propagate_event (grab_widget, event);
770       break;
771
772     case GDK_KEY_PRESS:
773     case GDK_KEY_RELEASE:
774       if (key_snoopers)
775         {
776           if (gtk_invoke_key_snoopers (grab_widget, event))
777             break;
778         }
779       /* else fall through */
780     case GDK_MOTION_NOTIFY:
781     case GDK_BUTTON_RELEASE:
782     case GDK_PROXIMITY_IN:
783     case GDK_PROXIMITY_OUT:
784       gtk_propagate_event (grab_widget, event);
785       break;
786       
787     case GDK_ENTER_NOTIFY:
788       if (GTK_WIDGET_IS_SENSITIVE (grab_widget))
789         {
790           gtk_widget_event (grab_widget, event);
791           if (event_widget == grab_widget)
792             GTK_PRIVATE_SET_FLAG (event_widget, GTK_LEAVE_PENDING);
793         }
794       break;
795       
796     case GDK_LEAVE_NOTIFY:
797       if (GTK_WIDGET_LEAVE_PENDING (event_widget))
798         {
799           GTK_PRIVATE_UNSET_FLAG (event_widget, GTK_LEAVE_PENDING);
800           gtk_widget_event (event_widget, event);
801         }
802       else if (GTK_WIDGET_IS_SENSITIVE (grab_widget))
803         gtk_widget_event (grab_widget, event);
804       break;
805       
806     case GDK_DRAG_STATUS:
807     case GDK_DROP_FINISHED:
808       gtk_drag_source_handle_event (event_widget, event);
809       break;
810     case GDK_DRAG_ENTER:
811     case GDK_DRAG_LEAVE:
812     case GDK_DRAG_MOTION:
813     case GDK_DROP_START:
814       gtk_drag_dest_handle_event (event_widget, event);
815       break;
816     }
817   
818   tmp_list = current_events;
819   current_events = g_list_remove_link (current_events, tmp_list);
820   g_list_free_1 (tmp_list);
821 }
822
823 gint
824 gtk_true (void)
825 {
826   return TRUE;
827 }
828
829 gint
830 gtk_false (void)
831 {
832   return FALSE;
833 }
834
835 void
836 gtk_grab_add (GtkWidget *widget)
837 {
838   g_return_if_fail (widget != NULL);
839   
840   if (!GTK_WIDGET_HAS_GRAB (widget))
841     {
842       GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_GRAB);
843       
844       grabs = g_slist_prepend (grabs, widget);
845       gtk_widget_ref (widget);
846     }
847 }
848
849 GtkWidget*
850 gtk_grab_get_current (void)
851 {
852   if (grabs)
853     return GTK_WIDGET (grabs->data);
854   return NULL;
855 }
856
857 void
858 gtk_grab_remove (GtkWidget *widget)
859 {
860   g_return_if_fail (widget != NULL);
861   
862   if (GTK_WIDGET_HAS_GRAB (widget))
863     {
864       GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_GRAB);
865       
866       grabs = g_slist_remove (grabs, widget);
867       gtk_widget_unref (widget);
868     }
869 }
870
871 void
872 gtk_init_add (GtkFunction function,
873               gpointer    data)
874 {
875   GtkInitFunction *init;
876   
877   init = g_new (GtkInitFunction, 1);
878   init->function = function;
879   init->data = data;
880   
881   init_functions = g_list_prepend (init_functions, init);
882 }
883
884 guint
885 gtk_key_snooper_install (GtkKeySnoopFunc snooper,
886                          gpointer        func_data)
887 {
888   GtkKeySnooperData *data;
889   static guint snooper_id = 1;
890
891   g_return_val_if_fail (snooper != NULL, 0);
892
893   data = g_new (GtkKeySnooperData, 1);
894   data->func = snooper;
895   data->func_data = func_data;
896   data->id = snooper_id++;
897   key_snoopers = g_slist_prepend (key_snoopers, data);
898
899   return data->id;
900 }
901
902 void
903 gtk_key_snooper_remove (guint            snooper_id)
904 {
905   GtkKeySnooperData *data = NULL;
906   GSList *slist;
907
908   slist = key_snoopers;
909   while (slist)
910     {
911       data = slist->data;
912       if (data->id == snooper_id)
913         break;
914
915       slist = slist->next;
916       data = NULL;
917     }
918   if (data)
919     key_snoopers = g_slist_remove (key_snoopers, data);
920 }
921
922 static gint
923 gtk_invoke_key_snoopers (GtkWidget *grab_widget,
924                          GdkEvent  *event)
925 {
926   GSList *slist;
927   gint return_val = FALSE;
928
929   slist = key_snoopers;
930   while (slist && !return_val)
931     {
932       GtkKeySnooperData *data;
933
934       data = slist->data;
935       slist = slist->next;
936       return_val = (*data->func) (grab_widget, (GdkEventKey*) event, data->func_data);
937     }
938
939   return return_val;
940 }
941
942 guint
943 gtk_quit_add_full (guint                main_level,
944                    GtkFunction          function,
945                    GtkCallbackMarshal   marshal,
946                    gpointer             data,
947                    GtkDestroyNotify     destroy)
948 {
949   static guint quit_id = 1;
950   GtkQuitFunction *quitf;
951   
952   g_return_val_if_fail ((function != NULL) || (marshal != NULL), 0);
953
954   if (!quit_mem_chunk)
955     quit_mem_chunk = g_mem_chunk_new ("quit mem chunk", sizeof (GtkQuitFunction),
956                                       512, G_ALLOC_AND_FREE);
957   
958   quitf = g_chunk_new (GtkQuitFunction, quit_mem_chunk);
959   
960   quitf->id = quit_id++;
961   quitf->main_level = main_level;
962   quitf->function = function;
963   quitf->marshal = marshal;
964   quitf->data = data;
965   quitf->destroy = destroy;
966
967   quit_functions = g_list_prepend (quit_functions, quitf);
968   
969   return quitf->id;
970 }
971
972 static void
973 gtk_quit_destroy (GtkQuitFunction *quitf)
974 {
975   if (quitf->destroy)
976     quitf->destroy (quitf->data);
977   g_mem_chunk_free (quit_mem_chunk, quitf);
978 }
979
980 static gint
981 gtk_quit_destructor (GtkObject **object_p)
982 {
983   if (*object_p)
984     gtk_object_destroy (*object_p);
985   g_free (object_p);
986
987   return FALSE;
988 }
989
990 void
991 gtk_quit_add_destroy (guint              main_level,
992                       GtkObject         *object)
993 {
994   GtkObject **object_p;
995
996   g_return_if_fail (main_level > 0);
997   g_return_if_fail (object != NULL);
998   g_return_if_fail (GTK_IS_OBJECT (object));
999
1000   object_p = g_new (GtkObject*, 1);
1001   *object_p = object;
1002   gtk_signal_connect (object,
1003                       "destroy",
1004                       GTK_SIGNAL_FUNC (gtk_widget_destroyed),
1005                       object_p);
1006   gtk_quit_add (main_level, (GtkFunction) gtk_quit_destructor, object_p);
1007 }
1008
1009 guint
1010 gtk_quit_add (guint       main_level,
1011               GtkFunction function,
1012               gpointer    data)
1013 {
1014   return gtk_quit_add_full (main_level, function, NULL, data, NULL);
1015 }
1016
1017 void
1018 gtk_quit_remove (guint id)
1019 {
1020   GtkQuitFunction *quitf;
1021   GList *tmp_list;
1022   
1023   tmp_list = quit_functions;
1024   while (tmp_list)
1025     {
1026       quitf = tmp_list->data;
1027       
1028       if (quitf->id == id)
1029         {
1030           quit_functions = g_list_remove_link (quit_functions, tmp_list);
1031           g_list_free (tmp_list);
1032           gtk_quit_destroy (quitf);
1033           
1034           return;
1035         }
1036       
1037       tmp_list = tmp_list->next;
1038     }
1039 }
1040
1041 void
1042 gtk_quit_remove_by_data (gpointer data)
1043 {
1044   GtkQuitFunction *quitf;
1045   GList *tmp_list;
1046   
1047   tmp_list = quit_functions;
1048   while (tmp_list)
1049     {
1050       quitf = tmp_list->data;
1051       
1052       if (quitf->data == data)
1053         {
1054           quit_functions = g_list_remove_link (quit_functions, tmp_list);
1055           g_list_free (tmp_list);
1056           gtk_quit_destroy (quitf);
1057
1058           return;
1059         }
1060       
1061       tmp_list = tmp_list->next;
1062     }
1063 }
1064
1065 guint
1066 gtk_timeout_add_full (guint32            interval,
1067                       GtkFunction        function,
1068                       GtkCallbackMarshal marshal,
1069                       gpointer           data,
1070                       GtkDestroyNotify   destroy)
1071 {
1072   if (marshal)
1073     {
1074       GtkClosure *closure;
1075
1076       closure = g_new (GtkClosure, 1);
1077       closure->marshal = marshal;
1078       closure->data = data;
1079       closure->destroy = destroy;
1080
1081       return g_timeout_add_full (0, interval, 
1082                                  gtk_invoke_idle_timeout,
1083                                  closure,
1084                                  gtk_destroy_closure);
1085     }
1086   else
1087     return g_timeout_add_full (0, interval, function, data, destroy);
1088 }
1089
1090 guint
1091 gtk_timeout_add (guint32     interval,
1092                  GtkFunction function,
1093                  gpointer    data)
1094 {
1095   return g_timeout_add_full (0, interval, function, data, NULL);
1096 }
1097
1098 void
1099 gtk_timeout_remove (guint tag)
1100 {
1101   g_source_remove (tag);
1102 }
1103
1104 guint
1105 gtk_idle_add_full (gint                 priority,
1106                    GtkFunction          function,
1107                    GtkCallbackMarshal   marshal,
1108                    gpointer             data,
1109                    GtkDestroyNotify     destroy)
1110 {
1111   if (marshal)
1112     {
1113       GtkClosure *closure;
1114
1115       closure = g_new (GtkClosure, 1);
1116       closure->marshal = marshal;
1117       closure->data = data;
1118       closure->destroy = destroy;
1119
1120       return g_idle_add_full (priority,
1121                               gtk_invoke_idle_timeout,
1122                               closure,
1123                               gtk_destroy_closure);
1124     }
1125   else
1126     return g_idle_add_full (priority, function, data, destroy);
1127 }
1128
1129 guint
1130 gtk_idle_add (GtkFunction function,
1131               gpointer    data)
1132 {
1133   return g_idle_add_full (GTK_PRIORITY_DEFAULT, function, data, NULL);
1134 }
1135
1136 guint       
1137 gtk_idle_add_priority   (gint               priority,
1138                          GtkFunction        function,
1139                          gpointer           data)
1140 {
1141   return g_idle_add_full (priority, function, data, NULL);
1142 }
1143
1144 void
1145 gtk_idle_remove (guint tag)
1146 {
1147   g_source_remove (tag);
1148 }
1149
1150 void
1151 gtk_idle_remove_by_data (gpointer data)
1152 {
1153   if (!g_idle_remove_by_data (data))
1154     g_warning ("gtk_idle_remove_by_data(%p): no such idle", data);
1155 }
1156
1157 guint
1158 gtk_input_add_full (gint                source,
1159                     GdkInputCondition   condition,
1160                     GdkInputFunction    function,
1161                     GtkCallbackMarshal  marshal,
1162                     gpointer            data,
1163                     GtkDestroyNotify    destroy)
1164 {
1165   if (marshal)
1166     {
1167       GtkClosure *closure;
1168
1169       closure = g_new (GtkClosure, 1);
1170       closure->marshal = marshal;
1171       closure->data = data;
1172       closure->destroy = destroy;
1173
1174       return gdk_input_add_full (source,
1175                                  condition,
1176                                  (GdkInputFunction) gtk_invoke_input,
1177                                  closure,
1178                                  (GdkDestroyNotify) gtk_destroy_closure);
1179     }
1180   else
1181     return gdk_input_add_full (source, condition, function, data, destroy);
1182 }
1183
1184 void
1185 gtk_input_remove (guint tag)
1186 {
1187   g_source_remove (tag);
1188 }
1189
1190 static void
1191 gtk_destroy_closure (gpointer data)
1192 {
1193   GtkClosure *closure = data;
1194
1195   if (closure->destroy)
1196     (closure->destroy) (closure->data);
1197   g_free (closure);
1198 }
1199
1200 static gboolean
1201 gtk_invoke_idle_timeout (gpointer data)
1202 {
1203   GtkClosure *closure = data;
1204
1205   GtkArg args[1];
1206   gint ret_val = FALSE;
1207   args[0].name = NULL;
1208   args[0].type = GTK_TYPE_BOOL;
1209   args[0].d.pointer_data = &ret_val;
1210   closure->marshal (NULL, closure->data,  0, args);
1211   return ret_val;
1212 }
1213
1214 static void
1215 gtk_invoke_input (gpointer          data,
1216                   gint              source,
1217                   GdkInputCondition condition)
1218 {
1219   GtkClosure *closure = data;
1220
1221   GtkArg args[3];
1222   args[0].type = GTK_TYPE_INT;
1223   args[0].name = NULL;
1224   GTK_VALUE_INT(args[0]) = source;
1225   args[1].type = GTK_TYPE_GDK_INPUT_CONDITION;
1226   args[1].name = NULL;
1227   GTK_VALUE_FLAGS(args[1]) = condition;
1228   args[2].type = GTK_TYPE_NONE;
1229   args[2].name = NULL;
1230
1231   closure->marshal (NULL, closure->data, 2, args);
1232 }
1233
1234 GdkEvent*
1235 gtk_get_current_event (void)
1236 {
1237   if (current_events)
1238     return gdk_event_copy ((GdkEvent *) current_events->data);
1239   else
1240     return NULL;
1241 }
1242
1243 GtkWidget*
1244 gtk_get_event_widget (GdkEvent *event)
1245 {
1246   GtkWidget *widget;
1247
1248   widget = NULL;
1249   if (event && event->any.window)
1250     gdk_window_get_user_data (event->any.window, (void**) &widget);
1251   
1252   return widget;
1253 }
1254
1255 static void
1256 gtk_exit_func (void)
1257 {
1258   if (gtk_initialized)
1259     {
1260       gtk_initialized = FALSE;
1261       gtk_preview_uninit ();
1262     }
1263 }
1264
1265
1266 static gint
1267 gtk_quit_invoke_function (GtkQuitFunction *quitf)
1268 {
1269   if (!quitf->marshal)
1270     return quitf->function (quitf->data);
1271   else
1272     {
1273       GtkArg args[1];
1274       gint ret_val = FALSE;
1275
1276       args[0].name = NULL;
1277       args[0].type = GTK_TYPE_BOOL;
1278       args[0].d.pointer_data = &ret_val;
1279       ((GtkCallbackMarshal) quitf->marshal) (NULL,
1280                                              quitf->data,
1281                                              0, args);
1282       return ret_val;
1283     }
1284 }
1285
1286 void
1287 gtk_propagate_event (GtkWidget *widget,
1288                      GdkEvent  *event)
1289 {
1290   gint handled_event;
1291   
1292   g_return_if_fail (widget != NULL);
1293   g_return_if_fail (GTK_IS_WIDGET (widget));
1294   g_return_if_fail (event != NULL);
1295   
1296   handled_event = FALSE;
1297
1298   if ((event->type == GDK_KEY_PRESS) ||
1299       (event->type == GDK_KEY_RELEASE))
1300     {
1301       /* Only send key events within Window widgets to the Window
1302        *  The Window widget will in turn pass the
1303        *  key event on to the currently focused widget
1304        *  for that window.
1305        */
1306       GtkWidget *window;
1307
1308       window = gtk_widget_get_ancestor (widget, GTK_TYPE_WINDOW);
1309       if (window)
1310         {
1311           if (GTK_WIDGET_IS_SENSITIVE (window))
1312             gtk_widget_event (window, event);
1313
1314           handled_event = TRUE; /* don't send to widget */
1315         }
1316     }
1317   
1318   /* Other events get propagated up the widget tree
1319    *  so that parents can see the button and motion
1320    *  events of the children.
1321    */
1322   while (!handled_event && widget)
1323     {
1324       GtkWidget *tmp;
1325
1326       gtk_widget_ref (widget);
1327       handled_event = !GTK_WIDGET_IS_SENSITIVE (widget) || gtk_widget_event (widget, event);
1328       tmp = widget->parent;
1329       gtk_widget_unref (widget);
1330       widget  = tmp;
1331     }
1332 }
1333
1334 #if 0
1335 static void
1336 gtk_error (gchar *str)
1337 {
1338   gtk_print (str);
1339 }
1340
1341 static void
1342 gtk_warning (gchar *str)
1343 {
1344   gtk_print (str);
1345 }
1346
1347 static void
1348 gtk_message (gchar *str)
1349 {
1350   gtk_print (str);
1351 }
1352
1353 static void
1354 gtk_print (gchar *str)
1355 {
1356   static GtkWidget *window = NULL;
1357   static GtkWidget *text;
1358   static int level = 0;
1359   GtkWidget *box1;
1360   GtkWidget *box2;
1361   GtkWidget *table;
1362   GtkWidget *hscrollbar;
1363   GtkWidget *vscrollbar;
1364   GtkWidget *separator;
1365   GtkWidget *button;
1366   
1367   if (level > 0)
1368     {
1369       fputs (str, stdout);
1370       fflush (stdout);
1371       return;
1372     }
1373   
1374   if (!window)
1375     {
1376       window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
1377       
1378       gtk_signal_connect (GTK_OBJECT (window), "destroy",
1379                           (GtkSignalFunc) gtk_widget_destroyed,
1380                           &window);
1381       
1382       gtk_window_set_title (GTK_WINDOW (window), "Messages");
1383       
1384       box1 = gtk_vbox_new (FALSE, 0);
1385       gtk_container_add (GTK_CONTAINER (window), box1);
1386       gtk_widget_show (box1);
1387       
1388       
1389       box2 = gtk_vbox_new (FALSE, 10);
1390       gtk_container_set_border_width (GTK_CONTAINER (box2), 10);
1391       gtk_box_pack_start (GTK_BOX (box1), box2, TRUE, TRUE, 0);
1392       gtk_widget_show (box2);
1393       
1394       
1395       table = gtk_table_new (2, 2, FALSE);
1396       gtk_table_set_row_spacing (GTK_TABLE (table), 0, 2);
1397       gtk_table_set_col_spacing (GTK_TABLE (table), 0, 2);
1398       gtk_box_pack_start (GTK_BOX (box2), table, TRUE, TRUE, 0);
1399       gtk_widget_show (table);
1400       
1401       text = gtk_text_new (NULL, NULL);
1402       gtk_text_set_editable (GTK_TEXT (text), FALSE);
1403       gtk_table_attach_defaults (GTK_TABLE (table), text, 0, 1, 0, 1);
1404       gtk_widget_show (text);
1405       gtk_widget_realize (text);
1406       
1407       hscrollbar = gtk_hscrollbar_new (GTK_TEXT (text)->hadj);
1408       gtk_table_attach (GTK_TABLE (table), hscrollbar, 0, 1, 1, 2,
1409                         GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
1410       gtk_widget_show (hscrollbar);
1411       
1412       vscrollbar = gtk_vscrollbar_new (GTK_TEXT (text)->vadj);
1413       gtk_table_attach (GTK_TABLE (table), vscrollbar, 1, 2, 0, 1,
1414                         GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
1415       gtk_widget_show (vscrollbar);
1416       
1417       separator = gtk_hseparator_new ();
1418       gtk_box_pack_start (GTK_BOX (box1), separator, FALSE, TRUE, 0);
1419       gtk_widget_show (separator);
1420       
1421       
1422       box2 = gtk_vbox_new (FALSE, 10);
1423       gtk_container_set_border_width (GTK_CONTAINER (box2), 10);
1424       gtk_box_pack_start (GTK_BOX (box1), box2, FALSE, TRUE, 0);
1425       gtk_widget_show (box2);
1426       
1427       
1428       button = gtk_button_new_with_label ("close");
1429       gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
1430                                  (GtkSignalFunc) gtk_widget_hide,
1431                                  GTK_OBJECT (window));
1432       gtk_box_pack_start (GTK_BOX (box2), button, TRUE, TRUE, 0);
1433       GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
1434       gtk_widget_grab_default (button);
1435       gtk_widget_show (button);
1436     }
1437   
1438   level += 1;
1439   gtk_text_insert (GTK_TEXT (text), NULL, NULL, NULL, str, -1);
1440   level -= 1;
1441   
1442   if (!GTK_WIDGET_VISIBLE (window))
1443     gtk_widget_show (window);
1444 }
1445 #endif