]> Pileus Git - ~andy/gtk/blob - gtk/gtkmenushell.c
Mark abstract types as G_TYPE_FLAG_ABSTRACT. (#72383)
[~andy/gtk] / gtk / gtkmenushell.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 #define GTK_MENU_INTERNALS
28
29 #include "gdk/gdkkeysyms.h"
30 #include "gtkbindings.h"
31 #include "gtkmain.h"
32 #include "gtkmarshalers.h"
33 #include "gtkmenubar.h"
34 #include "gtkmenuitem.h"
35 #include "gtkmenushell.h"
36 #include "gtksignal.h"
37 #include "gtktearoffmenuitem.h"
38 #include "gtkwindow.h"
39
40 #define MENU_SHELL_TIMEOUT   500
41
42 enum {
43   DEACTIVATE,
44   SELECTION_DONE,
45   MOVE_CURRENT,
46   ACTIVATE_CURRENT,
47   CANCEL,
48   CYCLE_FOCUS,
49   LAST_SIGNAL
50 };
51
52 typedef void (*GtkMenuShellSignal1) (GtkObject           *object,
53                                      GtkMenuDirectionType arg1,
54                                      gpointer             data);
55 typedef void (*GtkMenuShellSignal2) (GtkObject *object,
56                                      gboolean   arg1,
57                                      gpointer   data);
58
59 /* Terminology:
60  * 
61  * A menu item can be "selected", this means that it is displayed
62  * in the prelight state, and if it has a submenu, that submenu
63  * will be popped up. 
64  * 
65  * A menu is "active" when it is visible onscreen and the user
66  * is selecting from it. A menubar is not active until the user
67  * clicks on one of its menuitems. When a menu is active,
68  * passing the mouse over a submenu will pop it up.
69  *
70  * menu_shell->active_menu_item, is however, not an "active"
71  * menu item (there is no such thing) but rather, the selected
72  * menu item in that MenuShell, if there is one.
73  *
74  * There is also is a concept of the current menu and a current
75  * menu item. The current menu item is the selected menu item
76  * that is furthest down in the heirarchy. (Every active menu_shell
77  * does not necessarily contain a selected menu item, but if
78  * it does, then menu_shell->parent_menu_shell must also contain
79  * a selected menu item. The current menu is the menu that 
80  * contains the current menu_item. It will always have a GTK
81  * grab and receive all key presses.
82  *
83  *
84  * Action signals:
85  *
86  *  ::move_current (GtkMenuDirection *dir)
87  *     Moves the current menu item in direction 'dir':
88  *
89  *       GTK_MENU_DIR_PARENT: To the parent menu shell
90  *       GTK_MENU_DIR_CHILD: To the child menu shell (if this item has
91  *          a submenu.
92  *       GTK_MENU_DIR_NEXT/PREV: To the next or previous item
93  *          in this menu.
94  * 
95  *     As a a bit of a hack to get movement between menus and
96  *     menubars working, if submenu_placement is different for
97  *     the menu and its MenuShell then the following apply:
98  * 
99  *       - For 'parent' the current menu is not just moved to
100  *         the parent, but moved to the previous entry in the parent
101  *       - For 'child', if there is no child, then current is
102  *         moved to the next item in the parent.
103  *
104  * 
105  *  ::activate_current (GBoolean *force_hide)
106  *     Activate the current item. If 'force_hide' is true, hide
107  *     the current menu item always. Otherwise, only hide
108  *     it if menu_item->klass->hide_on_activate is true.
109  *
110  *  ::cancel ()
111  *     Cancels the current selection
112  */
113
114 static void gtk_menu_shell_class_init        (GtkMenuShellClass *klass);
115 static void gtk_menu_shell_init              (GtkMenuShell      *menu_shell);
116 static void gtk_menu_shell_realize           (GtkWidget         *widget);
117 static gint gtk_menu_shell_button_press      (GtkWidget         *widget,
118                                               GdkEventButton    *event);
119 static gint gtk_menu_shell_button_release    (GtkWidget         *widget,
120                                               GdkEventButton    *event);
121 static gint gtk_menu_shell_key_press         (GtkWidget         *widget,
122                                               GdkEventKey       *event);
123 static gint gtk_menu_shell_enter_notify      (GtkWidget         *widget,
124                                               GdkEventCrossing  *event);
125 static gint gtk_menu_shell_leave_notify      (GtkWidget         *widget,
126                                               GdkEventCrossing  *event);
127 static void gtk_menu_shell_add               (GtkContainer      *container,
128                                               GtkWidget         *widget);
129 static void gtk_menu_shell_remove            (GtkContainer      *container,
130                                               GtkWidget         *widget);
131 static void gtk_menu_shell_forall            (GtkContainer      *container,
132                                               gboolean           include_internals,
133                                               GtkCallback        callback,
134                                               gpointer           callback_data);
135 static void gtk_menu_shell_real_insert       (GtkMenuShell *menu_shell,
136                                               GtkWidget    *child,
137                                               gint          position);
138 static void gtk_real_menu_shell_deactivate   (GtkMenuShell      *menu_shell);
139 static gint gtk_menu_shell_is_item           (GtkMenuShell      *menu_shell,
140                                               GtkWidget         *child);
141 static GtkWidget *gtk_menu_shell_get_item    (GtkMenuShell      *menu_shell,
142                                               GdkEvent          *event);
143 static GtkType    gtk_menu_shell_child_type  (GtkContainer      *container);
144 static void gtk_menu_shell_real_select_item  (GtkMenuShell      *menu_shell,
145                                               GtkWidget         *menu_item);
146 static void gtk_menu_shell_select_submenu_first (GtkMenuShell   *menu_shell); 
147
148 static void gtk_real_menu_shell_move_current (GtkMenuShell      *menu_shell,
149                                               GtkMenuDirectionType direction);
150 static void gtk_real_menu_shell_activate_current (GtkMenuShell      *menu_shell,
151                                                   gboolean           force_hide);
152 static void gtk_real_menu_shell_cancel           (GtkMenuShell      *menu_shell);
153 static void gtk_real_menu_shell_cycle_focus      (GtkMenuShell      *menu_shell,
154                                                   GtkDirectionType   dir);
155
156 static GtkContainerClass *parent_class = NULL;
157 static guint menu_shell_signals[LAST_SIGNAL] = { 0 };
158
159
160 GtkType
161 gtk_menu_shell_get_type (void)
162 {
163   static GtkType menu_shell_type = 0;
164
165   if (!menu_shell_type)
166     {
167       static const GTypeInfo menu_shell_info =
168       {
169         sizeof (GtkMenuShellClass),
170         NULL,           /* base_init */
171         NULL,           /* base_finalize */
172         (GClassInitFunc) gtk_menu_shell_class_init,
173         NULL,           /* class_finalize */
174         NULL,           /* class_data */
175         sizeof (GtkMenuShell),
176         0,              /* n_preallocs */
177         (GInstanceInitFunc) gtk_menu_shell_init,
178         NULL            /* value_table */
179       };
180
181       menu_shell_type = g_type_register_static (GTK_TYPE_CONTAINER, "GtkMenuShell",
182                                                 &menu_shell_info, G_TYPE_FLAG_ABSTRACT);
183     }
184
185   return menu_shell_type;
186 }
187
188 static guint
189 binding_signal_new (const gchar        *signal_name,
190                     GType               itype,
191                     GSignalFlags        signal_flags,
192                     GCallback           handler,
193                     GSignalAccumulator  accumulator,
194                     gpointer            accu_data,
195                     GSignalCMarshaller  c_marshaller,
196                     GType               return_type,
197                     guint               n_params,
198                     ...)
199 {
200   va_list args;
201   guint signal_id;
202
203   g_return_val_if_fail (signal_name != NULL, 0);
204   
205   va_start (args, n_params);
206
207   signal_id = g_signal_new_valist (signal_name, itype, signal_flags,
208                                    g_cclosure_new (handler, NULL, NULL),
209                                    accumulator, accu_data, c_marshaller,
210                                    return_type, n_params, args);
211
212   va_end (args);
213  
214   return signal_id;
215 }
216
217 static void
218 gtk_menu_shell_class_init (GtkMenuShellClass *klass)
219 {
220   GtkObjectClass *object_class;
221   GtkWidgetClass *widget_class;
222   GtkContainerClass *container_class;
223
224   GtkBindingSet *binding_set;
225
226   object_class = (GtkObjectClass*) klass;
227   widget_class = (GtkWidgetClass*) klass;
228   container_class = (GtkContainerClass*) klass;
229
230   parent_class = gtk_type_class (gtk_container_get_type ());
231
232   widget_class->realize = gtk_menu_shell_realize;
233   widget_class->button_press_event = gtk_menu_shell_button_press;
234   widget_class->button_release_event = gtk_menu_shell_button_release;
235   widget_class->key_press_event = gtk_menu_shell_key_press;
236   widget_class->enter_notify_event = gtk_menu_shell_enter_notify;
237   widget_class->leave_notify_event = gtk_menu_shell_leave_notify;
238
239   container_class->add = gtk_menu_shell_add;
240   container_class->remove = gtk_menu_shell_remove;
241   container_class->forall = gtk_menu_shell_forall;
242   container_class->child_type = gtk_menu_shell_child_type;
243
244   klass->submenu_placement = GTK_TOP_BOTTOM;
245   klass->deactivate = gtk_real_menu_shell_deactivate;
246   klass->selection_done = NULL;
247   klass->move_current = gtk_real_menu_shell_move_current;
248   klass->activate_current = gtk_real_menu_shell_activate_current;
249   klass->cancel = gtk_real_menu_shell_cancel;
250   klass->select_item = gtk_menu_shell_real_select_item;
251   klass->insert = gtk_menu_shell_real_insert;
252
253   menu_shell_signals[DEACTIVATE] =
254     gtk_signal_new ("deactivate",
255                     GTK_RUN_FIRST,
256                     GTK_CLASS_TYPE (object_class),
257                     GTK_SIGNAL_OFFSET (GtkMenuShellClass, deactivate),
258                     _gtk_marshal_VOID__VOID,
259                     GTK_TYPE_NONE, 0);
260   menu_shell_signals[SELECTION_DONE] =
261     gtk_signal_new ("selection-done",
262                     GTK_RUN_FIRST,
263                     GTK_CLASS_TYPE (object_class),
264                     GTK_SIGNAL_OFFSET (GtkMenuShellClass, selection_done),
265                     _gtk_marshal_VOID__VOID,
266                     GTK_TYPE_NONE, 0);
267   menu_shell_signals[MOVE_CURRENT] =
268     gtk_signal_new ("move_current",
269                     GTK_RUN_LAST | GTK_RUN_ACTION,
270                     GTK_CLASS_TYPE (object_class),
271                     GTK_SIGNAL_OFFSET (GtkMenuShellClass, move_current),
272                     _gtk_marshal_VOID__ENUM,
273                     GTK_TYPE_NONE, 1, 
274                     GTK_TYPE_MENU_DIRECTION_TYPE);
275   menu_shell_signals[ACTIVATE_CURRENT] =
276     gtk_signal_new ("activate_current",
277                     GTK_RUN_LAST | GTK_RUN_ACTION,
278                     GTK_CLASS_TYPE (object_class),
279                     GTK_SIGNAL_OFFSET (GtkMenuShellClass, activate_current),
280                     _gtk_marshal_VOID__BOOLEAN,
281                     GTK_TYPE_NONE, 1, 
282                     GTK_TYPE_BOOL);
283   menu_shell_signals[CANCEL] =
284     gtk_signal_new ("cancel",
285                     GTK_RUN_LAST | GTK_RUN_ACTION,
286                     GTK_CLASS_TYPE (object_class),
287                     GTK_SIGNAL_OFFSET (GtkMenuShellClass, cancel),
288                     _gtk_marshal_VOID__VOID,
289                     GTK_TYPE_NONE, 0);
290   menu_shell_signals[CYCLE_FOCUS] =
291     binding_signal_new ("cycle_focus",
292                         G_OBJECT_CLASS_TYPE (object_class),
293                         G_SIGNAL_RUN_LAST | GTK_RUN_ACTION,
294                         G_CALLBACK (gtk_real_menu_shell_cycle_focus),
295                         NULL, NULL,
296                         _gtk_marshal_VOID__ENUM,
297                         GTK_TYPE_NONE, 1,
298                         GTK_TYPE_DIRECTION_TYPE);
299
300
301   binding_set = gtk_binding_set_by_class (klass);
302   gtk_binding_entry_add_signal (binding_set,
303                                 GDK_Escape, 0,
304                                 "cancel", 0);
305   gtk_binding_entry_add_signal (binding_set,
306                                 GDK_Return, 0,
307                                 "activate_current", 1,
308                                 GTK_TYPE_BOOL,
309                                 TRUE);
310   gtk_binding_entry_add_signal (binding_set,
311                                 GDK_KP_Enter, 0,
312                                 "activate_current", 1,
313                                 GTK_TYPE_BOOL,
314                                 TRUE);
315   gtk_binding_entry_add_signal (binding_set,
316                                 GDK_space, 0,
317                                 "activate_current", 1,
318                                 GTK_TYPE_BOOL,
319                                 FALSE);
320   gtk_binding_entry_add_signal (binding_set,
321                                 GDK_KP_Space, 0,
322                                 "activate_current", 1,
323                                 GTK_TYPE_BOOL,
324                                 FALSE);
325   gtk_binding_entry_add_signal (binding_set,
326                                 GDK_F10, 0,
327                                 "cycle_focus", 1,
328                                 GTK_TYPE_DIRECTION_TYPE, GTK_DIR_TAB_FORWARD);
329   gtk_binding_entry_add_signal (binding_set,
330                                 GDK_F10, GDK_SHIFT_MASK,
331                                 "cycle_focus", 1,
332                                 GTK_TYPE_DIRECTION_TYPE, GTK_DIR_TAB_BACKWARD);
333 }
334
335 static GtkType
336 gtk_menu_shell_child_type (GtkContainer     *container)
337 {
338   return GTK_TYPE_MENU_ITEM;
339 }
340
341 static void
342 gtk_menu_shell_init (GtkMenuShell *menu_shell)
343 {
344   menu_shell->children = NULL;
345   menu_shell->active_menu_item = NULL;
346   menu_shell->parent_menu_shell = NULL;
347   menu_shell->active = FALSE;
348   menu_shell->have_grab = FALSE;
349   menu_shell->have_xgrab = FALSE;
350   menu_shell->ignore_leave = FALSE;
351   menu_shell->button = 0;
352   menu_shell->menu_flag = 0;
353   menu_shell->activate_time = 0;
354 }
355
356 void
357 gtk_menu_shell_append (GtkMenuShell *menu_shell,
358                        GtkWidget    *child)
359 {
360   gtk_menu_shell_insert (menu_shell, child, -1);
361 }
362
363 void
364 gtk_menu_shell_prepend (GtkMenuShell *menu_shell,
365                         GtkWidget    *child)
366 {
367   gtk_menu_shell_insert (menu_shell, child, 0);
368 }
369
370 void
371 gtk_menu_shell_insert (GtkMenuShell *menu_shell,
372                        GtkWidget    *child,
373                        gint          position)
374 {
375   GtkMenuShellClass *class;
376
377   g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
378   g_return_if_fail (GTK_IS_MENU_ITEM (child));
379
380   class = GTK_MENU_SHELL_GET_CLASS (menu_shell);
381
382   if (class->insert)
383     class->insert (menu_shell, child, position);
384 }
385
386 static void
387 gtk_menu_shell_real_insert (GtkMenuShell *menu_shell,
388                             GtkWidget    *child,
389                             gint          position)
390 {
391   menu_shell->children = g_list_insert (menu_shell->children, child, position);
392
393   gtk_widget_set_parent (child, GTK_WIDGET (menu_shell));
394 }
395
396 void
397 gtk_menu_shell_deactivate (GtkMenuShell *menu_shell)
398 {
399   g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
400
401   gtk_signal_emit (GTK_OBJECT (menu_shell), menu_shell_signals[DEACTIVATE]);
402 }
403
404 static void
405 gtk_menu_shell_realize (GtkWidget *widget)
406 {
407   GdkWindowAttr attributes;
408   gint attributes_mask;
409
410   g_return_if_fail (GTK_IS_MENU_SHELL (widget));
411
412   GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
413
414   attributes.x = widget->allocation.x;
415   attributes.y = widget->allocation.y;
416   attributes.width = widget->allocation.width;
417   attributes.height = widget->allocation.height;
418   attributes.window_type = GDK_WINDOW_CHILD;
419   attributes.wclass = GDK_INPUT_OUTPUT;
420   attributes.visual = gtk_widget_get_visual (widget);
421   attributes.colormap = gtk_widget_get_colormap (widget);
422   attributes.event_mask = gtk_widget_get_events (widget);
423   attributes.event_mask |= (GDK_EXPOSURE_MASK |
424                             GDK_BUTTON_PRESS_MASK |
425                             GDK_BUTTON_RELEASE_MASK |
426                             GDK_KEY_PRESS_MASK |
427                             GDK_ENTER_NOTIFY_MASK |
428                             GDK_LEAVE_NOTIFY_MASK);
429
430   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
431   widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
432   gdk_window_set_user_data (widget->window, widget);
433
434   widget->style = gtk_style_attach (widget->style, widget->window);
435   gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
436 }
437
438 void
439 _gtk_menu_shell_activate (GtkMenuShell *menu_shell)
440 {
441   if (!menu_shell->active)
442     {
443       gtk_grab_add (GTK_WIDGET (menu_shell));
444       menu_shell->have_grab = TRUE;
445       menu_shell->active = TRUE;
446     }
447 }
448
449 static gint
450 gtk_menu_shell_button_press (GtkWidget      *widget,
451                              GdkEventButton *event)
452 {
453   GtkMenuShell *menu_shell;
454   GtkWidget *menu_item;
455
456   g_return_val_if_fail (GTK_IS_MENU_SHELL (widget), FALSE);
457   g_return_val_if_fail (event != NULL, FALSE);
458
459   if (event->type != GDK_BUTTON_PRESS)
460     return FALSE;
461
462   menu_shell = GTK_MENU_SHELL (widget);
463
464   if (menu_shell->parent_menu_shell)
465     {
466       return gtk_widget_event (menu_shell->parent_menu_shell, (GdkEvent*) event);
467     }
468   else if (!menu_shell->active || !menu_shell->button)
469     {
470       _gtk_menu_shell_activate (menu_shell);
471       
472       menu_shell->button = event->button;
473
474       menu_item = gtk_menu_shell_get_item (menu_shell, (GdkEvent *)event);
475
476       if (menu_item && _gtk_menu_item_is_selectable (menu_item))
477         {
478           if ((menu_item->parent == widget) &&
479               (menu_item != menu_shell->active_menu_item))
480             {
481               if (GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement == GTK_TOP_BOTTOM)
482                 g_object_set_data (G_OBJECT (menu_shell),
483                                    "gtk-menushell-just-activated",
484                                    GUINT_TO_POINTER (1));
485               gtk_menu_shell_select_item (menu_shell, menu_item);
486             }
487         }
488     }
489   else
490     {
491       widget = gtk_get_event_widget ((GdkEvent*) event);
492       if (widget == GTK_WIDGET (menu_shell))
493         {
494           gtk_menu_shell_deactivate (menu_shell);
495           gtk_signal_emit (GTK_OBJECT (menu_shell), menu_shell_signals[SELECTION_DONE]);
496         }
497     }
498
499   return TRUE;
500 }
501
502 static gint
503 gtk_menu_shell_button_release (GtkWidget      *widget,
504                                GdkEventButton *event)
505 {
506   GtkMenuShell *menu_shell;
507   GtkWidget *menu_item;
508   gint deactivate;
509
510   g_return_val_if_fail (GTK_IS_MENU_SHELL (widget), FALSE);
511   g_return_val_if_fail (event != NULL, FALSE);
512
513   menu_shell = GTK_MENU_SHELL (widget);
514   if (menu_shell->active)
515     {
516       gboolean deactivate_immediately = FALSE;
517
518       if (menu_shell->button && (event->button != menu_shell->button))
519         {
520           menu_shell->button = 0;
521           if (menu_shell->parent_menu_shell)
522             return gtk_widget_event (menu_shell->parent_menu_shell, (GdkEvent*) event);
523         }
524
525       menu_shell->button = 0;
526       menu_item = gtk_menu_shell_get_item (menu_shell, (GdkEvent*) event);
527
528       deactivate = TRUE;
529
530       if (menu_item
531           && GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement == GTK_TOP_BOTTOM)
532         {
533           if (g_object_get_data (G_OBJECT (menu_shell), "gtk-menushell-just-activated"))
534             g_object_set_data (G_OBJECT (menu_shell), "gtk-menushell-just-activated", NULL);
535           else
536             deactivate_immediately = TRUE;
537         }
538
539       if ((event->time - menu_shell->activate_time) > MENU_SHELL_TIMEOUT)
540         {
541           if (deactivate_immediately)
542             {
543               gtk_menu_shell_deactivate (menu_shell);
544               return TRUE;
545             }
546             
547           if (menu_item && (menu_shell->active_menu_item == menu_item) &&
548               _gtk_menu_item_is_selectable (menu_item))
549             {
550               if (GTK_MENU_ITEM (menu_item)->submenu == NULL)
551                 {
552                   gtk_menu_shell_activate_item (menu_shell, menu_item, TRUE);
553                   return TRUE;
554                 }
555             }
556           else if (menu_item && !_gtk_menu_item_is_selectable (menu_item))
557             deactivate = FALSE;
558           else if (menu_shell->parent_menu_shell)
559             {
560               menu_shell->active = TRUE;
561               gtk_widget_event (menu_shell->parent_menu_shell, (GdkEvent*) event);
562               return TRUE;
563             }
564         }
565       else
566         {
567           /* We only ever want to prevent deactivation on the first
568            * press/release. Setting the time to zero is a bit of a
569            * hack, since we could be being triggered in the first
570            * few fractions of a second after a server time wraparound.
571            * the chances of that happening are ~1/10^6, without
572            * serious harm if we lose.
573            */
574           menu_shell->activate_time = 0;
575           deactivate = FALSE;
576         }
577       
578       /* If the button click was very fast, or we ended up on a submenu,
579        * leave the menu up
580        */
581       if (!deactivate || 
582           (menu_item && (menu_shell->active_menu_item == menu_item)))
583         {
584           deactivate = FALSE;
585           menu_shell->ignore_leave = TRUE;
586         }
587       else
588         deactivate = TRUE;
589
590       if (deactivate)
591         {
592           gtk_menu_shell_deactivate (menu_shell);
593           gtk_signal_emit (GTK_OBJECT (menu_shell), menu_shell_signals[SELECTION_DONE]);
594         }
595     }
596
597   return TRUE;
598 }
599
600 static gint
601 gtk_menu_shell_key_press (GtkWidget     *widget,
602                           GdkEventKey *event)
603 {
604   GtkMenuShell *menu_shell;
605   GtkWidget *toplevel;
606   
607   g_return_val_if_fail (GTK_IS_MENU_SHELL (widget), FALSE);
608   g_return_val_if_fail (event != NULL, FALSE);
609       
610   menu_shell = GTK_MENU_SHELL (widget);
611
612   if (!menu_shell->active_menu_item && menu_shell->parent_menu_shell)
613     return gtk_widget_event (menu_shell->parent_menu_shell, (GdkEvent *)event);
614   
615   if (_gtk_bindings_activate_event (GTK_OBJECT (widget), event))
616     return TRUE;
617
618   toplevel = gtk_widget_get_toplevel (widget);
619   if (GTK_IS_WINDOW (toplevel) &&
620       _gtk_window_activate_key (GTK_WINDOW (toplevel), event))
621     return TRUE;
622
623   return FALSE;
624 }
625
626 static gint
627 gtk_menu_shell_enter_notify (GtkWidget        *widget,
628                              GdkEventCrossing *event)
629 {
630   GtkMenuShell *menu_shell;
631   GtkWidget *menu_item;
632
633   g_return_val_if_fail (GTK_IS_MENU_SHELL (widget), FALSE);
634   g_return_val_if_fail (event != NULL, FALSE);
635
636   menu_shell = GTK_MENU_SHELL (widget);
637
638   if (menu_shell->active)
639     {
640       menu_item = gtk_get_event_widget ((GdkEvent*) event);
641
642       if (!menu_item ||
643           (GTK_IS_MENU_ITEM (menu_item) && 
644            !_gtk_menu_item_is_selectable (menu_item)))
645         return TRUE;
646       
647       if ((menu_item->parent == widget) &&
648           (menu_shell->active_menu_item != menu_item) &&
649           GTK_IS_MENU_ITEM (menu_item))
650         {
651           if (menu_shell->ignore_enter)
652             return TRUE;
653           
654           if ((event->detail != GDK_NOTIFY_INFERIOR) &&
655               (GTK_WIDGET_STATE (menu_item) != GTK_STATE_PRELIGHT))
656             {
657               gtk_menu_shell_select_item (menu_shell, menu_item);
658             }
659         }
660       else if (menu_shell->parent_menu_shell)
661         {
662           gtk_widget_event (menu_shell->parent_menu_shell, (GdkEvent*) event);
663         }
664     }
665
666   return TRUE;
667 }
668
669 static gint
670 gtk_menu_shell_leave_notify (GtkWidget        *widget,
671                              GdkEventCrossing *event)
672 {
673   GtkMenuShell *menu_shell;
674   GtkMenuItem *menu_item;
675   GtkWidget *event_widget;
676
677   g_return_val_if_fail (GTK_IS_MENU_SHELL (widget), FALSE);
678   g_return_val_if_fail (event != NULL, FALSE);
679
680   if (GTK_WIDGET_VISIBLE (widget))
681     {
682       menu_shell = GTK_MENU_SHELL (widget);
683       event_widget = gtk_get_event_widget ((GdkEvent*) event);
684
685       if (!event_widget || !GTK_IS_MENU_ITEM (event_widget))
686         return TRUE;
687
688       menu_item = GTK_MENU_ITEM (event_widget);
689
690       if (menu_shell->ignore_leave)
691         {
692           menu_shell->ignore_leave = FALSE;
693           return TRUE;
694         }
695
696       if (!_gtk_menu_item_is_selectable (event_widget))
697         return TRUE;
698
699       if ((menu_shell->active_menu_item == event_widget) &&
700           (menu_item->submenu == NULL))
701         {
702           if ((event->detail != GDK_NOTIFY_INFERIOR) &&
703               (GTK_WIDGET_STATE (menu_item) != GTK_STATE_NORMAL))
704             {
705               gtk_menu_shell_deselect (menu_shell);
706             }
707         }
708       else if (menu_shell->parent_menu_shell)
709         {
710           gtk_widget_event (menu_shell->parent_menu_shell, (GdkEvent*) event);
711         }
712     }
713
714   return TRUE;
715 }
716
717 static void
718 gtk_menu_shell_add (GtkContainer *container,
719                     GtkWidget    *widget)
720 {
721   gtk_menu_shell_append (GTK_MENU_SHELL (container), widget);
722 }
723
724 static void
725 gtk_menu_shell_remove (GtkContainer *container,
726                        GtkWidget    *widget)
727 {
728   GtkMenuShell *menu_shell;
729   gint was_visible;
730   
731   g_return_if_fail (GTK_IS_MENU_SHELL (container));
732   g_return_if_fail (GTK_IS_MENU_ITEM (widget));
733   
734   was_visible = GTK_WIDGET_VISIBLE (widget);
735   menu_shell = GTK_MENU_SHELL (container);
736   menu_shell->children = g_list_remove (menu_shell->children, widget);
737   
738   if (widget == menu_shell->active_menu_item)
739     {
740       gtk_item_deselect (GTK_ITEM (menu_shell->active_menu_item));
741       menu_shell->active_menu_item = NULL;
742     }
743
744   gtk_widget_unparent (widget);
745   
746   /* queue resize regardless of GTK_WIDGET_VISIBLE (container),
747    * since that's what is needed by toplevels.
748    */
749   if (was_visible)
750     gtk_widget_queue_resize (GTK_WIDGET (container));
751 }
752
753 static void
754 gtk_menu_shell_forall (GtkContainer *container,
755                        gboolean      include_internals,
756                        GtkCallback   callback,
757                        gpointer      callback_data)
758 {
759   GtkMenuShell *menu_shell;
760   GtkWidget *child;
761   GList *children;
762
763   g_return_if_fail (GTK_IS_MENU_SHELL (container));
764   g_return_if_fail (callback != NULL);
765
766   menu_shell = GTK_MENU_SHELL (container);
767
768   children = menu_shell->children;
769   while (children)
770     {
771       child = children->data;
772       children = children->next;
773
774       (* callback) (child, callback_data);
775     }
776 }
777
778
779 static void
780 gtk_real_menu_shell_deactivate (GtkMenuShell *menu_shell)
781 {
782   if (menu_shell->active)
783     {
784       menu_shell->button = 0;
785       menu_shell->active = FALSE;
786
787       if (menu_shell->active_menu_item)
788         {
789           gtk_menu_item_deselect (GTK_MENU_ITEM (menu_shell->active_menu_item));
790           menu_shell->active_menu_item = NULL;
791         }
792
793       if (menu_shell->have_grab)
794         {
795           menu_shell->have_grab = FALSE;
796           gtk_grab_remove (GTK_WIDGET (menu_shell));
797         }
798       if (menu_shell->have_xgrab)
799         {
800           GdkDisplay *display = gtk_widget_get_display (GTK_WIDGET (menu_shell));
801           
802           menu_shell->have_xgrab = FALSE;
803           gdk_display_pointer_ungrab (display, GDK_CURRENT_TIME);
804           gdk_display_keyboard_ungrab (display, GDK_CURRENT_TIME);
805         }
806     }
807 }
808
809 static gint
810 gtk_menu_shell_is_item (GtkMenuShell *menu_shell,
811                         GtkWidget    *child)
812 {
813   GtkWidget *parent;
814
815   g_return_val_if_fail (GTK_IS_MENU_SHELL (menu_shell), FALSE);
816   g_return_val_if_fail (child != NULL, FALSE);
817
818   parent = child->parent;
819   while (parent && GTK_IS_MENU_SHELL (parent))
820     {
821       if (parent == (GtkWidget*) menu_shell)
822         return TRUE;
823       parent = GTK_MENU_SHELL (parent)->parent_menu_shell;
824     }
825
826   return FALSE;
827 }
828
829 static GtkWidget*
830 gtk_menu_shell_get_item (GtkMenuShell *menu_shell,
831                          GdkEvent     *event)
832 {
833   GtkWidget *menu_item;
834
835   menu_item = gtk_get_event_widget ((GdkEvent*) event);
836   
837   while (menu_item && !GTK_IS_MENU_ITEM (menu_item))
838     menu_item = menu_item->parent;
839
840   if (menu_item && gtk_menu_shell_is_item (menu_shell, menu_item))
841     return menu_item;
842   else
843     return NULL;
844 }
845
846 /* Handlers for action signals */
847
848 void
849 gtk_menu_shell_select_item (GtkMenuShell *menu_shell,
850                             GtkWidget    *menu_item)
851 {
852   GtkMenuShellClass *class;
853
854   g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
855   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
856
857   class = GTK_MENU_SHELL_GET_CLASS (menu_shell);
858
859   if (class->select_item)
860     class->select_item (menu_shell, menu_item);
861 }
862
863 void _gtk_menu_item_set_placement (GtkMenuItem         *menu_item,
864                                    GtkSubmenuPlacement  placement);
865
866 static void
867 gtk_menu_shell_real_select_item (GtkMenuShell *menu_shell,
868                                  GtkWidget    *menu_item)
869 {
870   gtk_menu_shell_deselect (menu_shell);
871
872   if (!_gtk_menu_item_is_selectable (menu_item))
873     return;
874
875   menu_shell->active_menu_item = menu_item;
876   _gtk_menu_item_set_placement (GTK_MENU_ITEM (menu_shell->active_menu_item),
877                                GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement);
878   gtk_menu_item_select (GTK_MENU_ITEM (menu_shell->active_menu_item));
879
880   /* This allows the bizarre radio buttons-with-submenus-display-history
881    * behavior
882    */
883   if (GTK_MENU_ITEM (menu_shell->active_menu_item)->submenu)
884     gtk_widget_activate (menu_shell->active_menu_item);
885 }
886
887 void
888 gtk_menu_shell_deselect (GtkMenuShell *menu_shell)
889 {
890   g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
891
892   if (menu_shell->active_menu_item)
893     {
894       gtk_menu_item_deselect (GTK_MENU_ITEM (menu_shell->active_menu_item));
895       menu_shell->active_menu_item = NULL;
896     }
897 }
898
899 void
900 gtk_menu_shell_activate_item (GtkMenuShell      *menu_shell,
901                               GtkWidget         *menu_item,
902                               gboolean           force_deactivate)
903 {
904   GSList *slist, *shells = NULL;
905   gboolean deactivate = force_deactivate;
906
907   g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
908   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
909
910   if (!deactivate)
911     deactivate = GTK_MENU_ITEM_GET_CLASS (menu_item)->hide_on_activate;
912
913   gtk_widget_ref (GTK_WIDGET (menu_shell));
914
915   if (deactivate)
916     {
917       GtkMenuShell *parent_menu_shell = menu_shell;
918
919       do
920         {
921           gtk_widget_ref (GTK_WIDGET (parent_menu_shell));
922           shells = g_slist_prepend (shells, parent_menu_shell);
923           parent_menu_shell = (GtkMenuShell*) parent_menu_shell->parent_menu_shell;
924         }
925       while (parent_menu_shell);
926       shells = g_slist_reverse (shells);
927
928       gtk_menu_shell_deactivate (menu_shell);
929   
930       /* flush the x-queue, so any grabs are removed and
931        * the menu is actually taken down
932        */
933       gdk_display_sync (gtk_widget_get_display (menu_item));
934     }
935
936   gtk_widget_activate (menu_item);
937
938   for (slist = shells; slist; slist = slist->next)
939     {
940       gtk_signal_emit (slist->data, menu_shell_signals[SELECTION_DONE]);
941       gtk_widget_unref (slist->data);
942     }
943   g_slist_free (shells);
944
945   gtk_widget_unref (GTK_WIDGET (menu_shell));
946 }
947
948 /* Distance should be +/- 1 */
949 static void
950 gtk_menu_shell_move_selected (GtkMenuShell  *menu_shell, 
951                               gint           distance)
952 {
953   if (menu_shell->active_menu_item)
954     {
955       GList *node = g_list_find (menu_shell->children,
956                                  menu_shell->active_menu_item);
957       GList *start_node = node;
958       
959       if (distance > 0)
960         {
961           node = node->next;
962           while (node != start_node && 
963                  (!node || !_gtk_menu_item_is_selectable (node->data)))
964             {
965               if (!node)
966                 node = menu_shell->children;
967               else
968                 node = node->next;
969             }
970         }
971       else
972         {
973           node = node->prev;
974           while (node != start_node &&
975                  (!node || !_gtk_menu_item_is_selectable (node->data)))
976             {
977               if (!node)
978                 node = g_list_last (menu_shell->children);
979               else
980                 node = node->prev;
981             }
982         }
983       
984       if (node)
985         gtk_menu_shell_select_item (menu_shell, node->data);
986     }
987 }
988
989 /**
990  * _gtk_menu_shell_select_first:
991  * @menu_shell: a #GtkMenuShell
992  * @search_sensitive: if %TRUE, search for the first selectable
993  *                    menu item, otherwise select nothing if
994  *                    the first item isn't sensitive. This
995  *                    should be %FALSE if the menu is being
996  *                    popped up initially.
997  * 
998  * Select the first visible or selectable child of the menu shell;
999  * don't select tearoff items unless the only item is a tearoff
1000  * item.
1001  **/
1002 void
1003 _gtk_menu_shell_select_first (GtkMenuShell *menu_shell,
1004                               gboolean      search_sensitive)
1005 {
1006   GtkWidget *to_select = NULL;
1007   GList *tmp_list;
1008
1009   tmp_list = menu_shell->children;
1010   while (tmp_list)
1011     {
1012       GtkWidget *child = tmp_list->data;
1013       
1014       if ((!search_sensitive && GTK_WIDGET_VISIBLE (child)) ||
1015           _gtk_menu_item_is_selectable (child))
1016         {
1017           to_select = child;
1018           if (!GTK_IS_TEAROFF_MENU_ITEM (child))
1019             break;
1020         }
1021       
1022       tmp_list = tmp_list->next;
1023     }
1024
1025   if (to_select)
1026     gtk_menu_shell_select_item (menu_shell, to_select);
1027 }
1028
1029 static void
1030 gtk_menu_shell_select_last (GtkMenuShell *menu_shell,
1031                             gboolean      search_sensitive)
1032 {
1033   GtkWidget *to_select = NULL;
1034   GList *tmp_list;
1035
1036   tmp_list = g_list_last (menu_shell->children);
1037   while (tmp_list)
1038     {
1039       GtkWidget *child = tmp_list->data;
1040       
1041       if ((!search_sensitive && GTK_WIDGET_VISIBLE (child)) ||
1042           _gtk_menu_item_is_selectable (child))
1043         {
1044           to_select = child;
1045           if (!GTK_IS_TEAROFF_MENU_ITEM (child))
1046             break;
1047         }
1048       
1049       tmp_list = tmp_list->prev;
1050     }
1051
1052   if (to_select)
1053     gtk_menu_shell_select_item (menu_shell, to_select);
1054 }
1055
1056 static void
1057 gtk_menu_shell_select_submenu_first (GtkMenuShell     *menu_shell)
1058 {
1059   GtkMenuItem *menu_item;
1060
1061   menu_item = GTK_MENU_ITEM (menu_shell->active_menu_item); 
1062   
1063   if (menu_item->submenu)
1064     _gtk_menu_shell_select_first (GTK_MENU_SHELL (menu_item->submenu), TRUE);
1065 }
1066
1067 static void
1068 gtk_real_menu_shell_move_current (GtkMenuShell      *menu_shell,
1069                                   GtkMenuDirectionType direction)
1070 {
1071   GtkMenuShell *parent_menu_shell = NULL;
1072   gboolean had_selection;
1073
1074   had_selection = menu_shell->active_menu_item != NULL;
1075
1076   if (menu_shell->parent_menu_shell)
1077     parent_menu_shell = GTK_MENU_SHELL (menu_shell->parent_menu_shell);
1078   
1079   switch (direction)
1080     {
1081     case GTK_MENU_DIR_PARENT:
1082       if (parent_menu_shell)
1083         {
1084           if (GTK_MENU_SHELL_GET_CLASS (parent_menu_shell)->submenu_placement == 
1085                        GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement)
1086             gtk_menu_shell_deselect (menu_shell);
1087           else 
1088             {
1089               gtk_menu_shell_move_selected (parent_menu_shell, -1);
1090               gtk_menu_shell_select_submenu_first (parent_menu_shell); 
1091             }
1092         }
1093       break;
1094       
1095     case GTK_MENU_DIR_CHILD:
1096       if (menu_shell->active_menu_item &&
1097           _gtk_menu_item_is_selectable (menu_shell->active_menu_item) &&
1098           GTK_MENU_ITEM (menu_shell->active_menu_item)->submenu)
1099         {
1100           gtk_menu_shell_select_submenu_first (menu_shell);
1101         }
1102       else
1103         {
1104           /* Try to find a menu running the opposite direction */
1105           while (parent_menu_shell && 
1106                  (GTK_MENU_SHELL_GET_CLASS (parent_menu_shell)->submenu_placement ==
1107                   GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement))
1108             {
1109               GtkWidget *tmp_widget = parent_menu_shell->parent_menu_shell;
1110
1111               if (tmp_widget)
1112                 parent_menu_shell = GTK_MENU_SHELL (tmp_widget);
1113               else
1114                 parent_menu_shell = NULL;
1115             }
1116
1117           if (parent_menu_shell)
1118             {
1119               gtk_menu_shell_move_selected (parent_menu_shell, 1);
1120               gtk_menu_shell_select_submenu_first (parent_menu_shell);
1121             }
1122         }
1123       break;
1124       
1125     case GTK_MENU_DIR_PREV:
1126       gtk_menu_shell_move_selected (menu_shell, -1);
1127       if (!had_selection &&
1128           !menu_shell->active_menu_item &&
1129           menu_shell->children)
1130         gtk_menu_shell_select_last (menu_shell, TRUE);
1131       break;
1132     case GTK_MENU_DIR_NEXT:
1133       gtk_menu_shell_move_selected (menu_shell, 1);
1134       if (!had_selection &&
1135           !menu_shell->active_menu_item &&
1136           menu_shell->children)
1137         _gtk_menu_shell_select_first (menu_shell, TRUE);
1138       break;
1139     }
1140   
1141 }
1142
1143 static void
1144 gtk_real_menu_shell_activate_current (GtkMenuShell      *menu_shell,
1145                                       gboolean           force_hide)
1146 {
1147   if (menu_shell->active_menu_item &&
1148       _gtk_menu_item_is_selectable (menu_shell->active_menu_item) &&
1149       GTK_MENU_ITEM (menu_shell->active_menu_item)->submenu == NULL)
1150     {
1151       gtk_menu_shell_activate_item (menu_shell,
1152                                     menu_shell->active_menu_item,
1153                                     force_hide);
1154     }
1155 }
1156
1157 static void
1158 gtk_real_menu_shell_cancel (GtkMenuShell      *menu_shell)
1159 {
1160   /* Unset the active menu item so gtk_menu_popdown() doesn't see it.
1161    */
1162   gtk_menu_shell_deselect (menu_shell);
1163   
1164   gtk_menu_shell_deactivate (menu_shell);
1165   gtk_signal_emit (GTK_OBJECT (menu_shell), menu_shell_signals[SELECTION_DONE]);
1166 }
1167
1168 static void
1169 gtk_real_menu_shell_cycle_focus (GtkMenuShell      *menu_shell,
1170                                  GtkDirectionType   dir)
1171 {
1172   while (menu_shell && !GTK_IS_MENU_BAR (menu_shell))
1173     {
1174       if (menu_shell->parent_menu_shell)
1175         menu_shell = GTK_MENU_SHELL (menu_shell->parent_menu_shell);
1176       else
1177         menu_shell = NULL;
1178     }
1179
1180   if (menu_shell)
1181     _gtk_menu_bar_cycle_focus (GTK_MENU_BAR (menu_shell), dir);
1182 }