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