]> Pileus Git - ~andy/gtk/blob - gtk/gtkmenushell.c
Improve the behaviour of automatic mnemonics
[~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 "config.h"
30 #include "gdk/gdkkeysyms.h"
31 #include "gtkbindings.h"
32 #include "gtkkeyhash.h"
33 #include "gtklabel.h"
34 #include "gtkmain.h"
35 #include "gtkmarshalers.h"
36 #include "gtkmenu.h"
37 #include "gtkmenubar.h"
38 #include "gtkmenuitem.h"
39 #include "gtkmenushell.h"
40 #include "gtkmnemonichash.h"
41 #include "gtktearoffmenuitem.h"
42 #include "gtkwindow.h"
43 #include "gtkprivate.h"
44 #include "gtkintl.h"
45 #include "gtkalias.h"
46
47 #define MENU_SHELL_TIMEOUT   500
48
49 #define PACK_DIRECTION(m)                                 \
50    (GTK_IS_MENU_BAR (m)                                   \
51      ? gtk_menu_bar_get_pack_direction (GTK_MENU_BAR (m)) \
52      : GTK_PACK_DIRECTION_LTR)
53
54 enum {
55   DEACTIVATE,
56   SELECTION_DONE,
57   MOVE_CURRENT,
58   ACTIVATE_CURRENT,
59   CANCEL,
60   CYCLE_FOCUS,
61   MOVE_SELECTED,
62   LAST_SIGNAL
63 };
64
65 enum {
66   PROP_0,
67   PROP_TAKE_FOCUS
68 };
69
70 /* Terminology:
71  * 
72  * A menu item can be "selected", this means that it is displayed
73  * in the prelight state, and if it has a submenu, that submenu
74  * will be popped up. 
75  * 
76  * A menu is "active" when it is visible onscreen and the user
77  * is selecting from it. A menubar is not active until the user
78  * clicks on one of its menuitems. When a menu is active,
79  * passing the mouse over a submenu will pop it up.
80  *
81  * menu_shell->active_menu_item, is however, not an "active"
82  * menu item (there is no such thing) but rather, the selected
83  * menu item in that MenuShell, if there is one.
84  *
85  * There is also is a concept of the current menu and a current
86  * menu item. The current menu item is the selected menu item
87  * that is furthest down in the hierarchy. (Every active menu_shell
88  * does not necessarily contain a selected menu item, but if
89  * it does, then menu_shell->parent_menu_shell must also contain
90  * a selected menu item. The current menu is the menu that 
91  * contains the current menu_item. It will always have a GTK
92  * grab and receive all key presses.
93  *
94  *
95  * Action signals:
96  *
97  *  ::move_current (GtkMenuDirection *dir)
98  *     Moves the current menu item in direction 'dir':
99  *
100  *       GTK_MENU_DIR_PARENT: To the parent menu shell
101  *       GTK_MENU_DIR_CHILD: To the child menu shell (if this item has
102  *          a submenu.
103  *       GTK_MENU_DIR_NEXT/PREV: To the next or previous item
104  *          in this menu.
105  * 
106  *     As a a bit of a hack to get movement between menus and
107  *     menubars working, if submenu_placement is different for
108  *     the menu and its MenuShell then the following apply:
109  * 
110  *       - For 'parent' the current menu is not just moved to
111  *         the parent, but moved to the previous entry in the parent
112  *       - For 'child', if there is no child, then current is
113  *         moved to the next item in the parent.
114  *
115  *    Note that the above explanation of ::move_current was written
116  *    before menus and menubars had support for RTL flipping and
117  *    different packing directions, and therefore only applies for
118  *    when text direction and packing direction are both left-to-right.
119  * 
120  *  ::activate_current (GBoolean *force_hide)
121  *     Activate the current item. If 'force_hide' is true, hide
122  *     the current menu item always. Otherwise, only hide
123  *     it if menu_item->klass->hide_on_activate is true.
124  *
125  *  ::cancel ()
126  *     Cancels the current selection
127  */
128
129 #define GTK_MENU_SHELL_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_MENU_SHELL, GtkMenuShellPrivate))
130
131 typedef struct _GtkMenuShellPrivate GtkMenuShellPrivate;
132
133 struct _GtkMenuShellPrivate
134 {
135   GtkMnemonicHash *mnemonic_hash;
136   GtkKeyHash *key_hash;
137
138   guint take_focus : 1;
139   guint activated_submenu : 1;
140   /* This flag is a crutch to keep mnemonics in the same menu
141    * if the user moves the mouse over an unselectable menuitem.
142    */
143   guint in_unselectable_item : 1;
144 };
145
146 static void gtk_menu_shell_set_property      (GObject           *object,
147                                               guint              prop_id,
148                                               const GValue      *value,
149                                               GParamSpec        *pspec);
150 static void gtk_menu_shell_get_property      (GObject           *object,
151                                               guint              prop_id,
152                                               GValue            *value,
153                                               GParamSpec        *pspec);
154 static void gtk_menu_shell_realize           (GtkWidget         *widget);
155 static void gtk_menu_shell_finalize          (GObject           *object);
156 static gint gtk_menu_shell_button_press      (GtkWidget         *widget,
157                                               GdkEventButton    *event);
158 static gint gtk_menu_shell_button_release    (GtkWidget         *widget,
159                                               GdkEventButton    *event);
160 static gint gtk_menu_shell_key_press         (GtkWidget         *widget,
161                                               GdkEventKey       *event);
162 static gint gtk_menu_shell_enter_notify      (GtkWidget         *widget,
163                                               GdkEventCrossing  *event);
164 static gint gtk_menu_shell_leave_notify      (GtkWidget         *widget,
165                                               GdkEventCrossing  *event);
166 static void gtk_menu_shell_screen_changed    (GtkWidget         *widget,
167                                               GdkScreen         *previous_screen);
168 static gboolean gtk_menu_shell_grab_broken       (GtkWidget         *widget,
169                                               GdkEventGrabBroken *event);
170 static void gtk_menu_shell_add               (GtkContainer      *container,
171                                               GtkWidget         *widget);
172 static void gtk_menu_shell_remove            (GtkContainer      *container,
173                                               GtkWidget         *widget);
174 static void gtk_menu_shell_forall            (GtkContainer      *container,
175                                               gboolean           include_internals,
176                                               GtkCallback        callback,
177                                               gpointer           callback_data);
178 static void gtk_menu_shell_real_insert       (GtkMenuShell *menu_shell,
179                                               GtkWidget    *child,
180                                               gint          position);
181 static void gtk_real_menu_shell_deactivate   (GtkMenuShell      *menu_shell);
182 static gint gtk_menu_shell_is_item           (GtkMenuShell      *menu_shell,
183                                               GtkWidget         *child);
184 static GtkWidget *gtk_menu_shell_get_item    (GtkMenuShell      *menu_shell,
185                                               GdkEvent          *event);
186 static GType    gtk_menu_shell_child_type  (GtkContainer      *container);
187 static void gtk_menu_shell_real_select_item  (GtkMenuShell      *menu_shell,
188                                               GtkWidget         *menu_item);
189 static gboolean gtk_menu_shell_select_submenu_first (GtkMenuShell   *menu_shell); 
190
191 static void gtk_real_menu_shell_move_current (GtkMenuShell      *menu_shell,
192                                               GtkMenuDirectionType direction);
193 static void gtk_real_menu_shell_activate_current (GtkMenuShell      *menu_shell,
194                                                   gboolean           force_hide);
195 static void gtk_real_menu_shell_cancel           (GtkMenuShell      *menu_shell);
196 static void gtk_real_menu_shell_cycle_focus      (GtkMenuShell      *menu_shell,
197                                                   GtkDirectionType   dir);
198
199 static void     gtk_menu_shell_reset_key_hash    (GtkMenuShell *menu_shell);
200 static gboolean gtk_menu_shell_activate_mnemonic (GtkMenuShell *menu_shell,
201                                                   GdkEventKey  *event);
202 static gboolean gtk_menu_shell_real_move_selected (GtkMenuShell  *menu_shell, 
203                                                    gint           distance);
204
205 static guint menu_shell_signals[LAST_SIGNAL] = { 0 };
206
207 G_DEFINE_ABSTRACT_TYPE (GtkMenuShell, gtk_menu_shell, GTK_TYPE_CONTAINER)
208
209 static void
210 gtk_menu_shell_class_init (GtkMenuShellClass *klass)
211 {
212   GObjectClass *object_class;
213   GtkWidgetClass *widget_class;
214   GtkContainerClass *container_class;
215
216   GtkBindingSet *binding_set;
217
218   object_class = (GObjectClass*) klass;
219   widget_class = (GtkWidgetClass*) klass;
220   container_class = (GtkContainerClass*) klass;
221
222   object_class->set_property = gtk_menu_shell_set_property;
223   object_class->get_property = gtk_menu_shell_get_property;
224   object_class->finalize = gtk_menu_shell_finalize;
225
226   widget_class->realize = gtk_menu_shell_realize;
227   widget_class->button_press_event = gtk_menu_shell_button_press;
228   widget_class->button_release_event = gtk_menu_shell_button_release;
229   widget_class->grab_broken_event = gtk_menu_shell_grab_broken;
230   widget_class->key_press_event = gtk_menu_shell_key_press;
231   widget_class->enter_notify_event = gtk_menu_shell_enter_notify;
232   widget_class->leave_notify_event = gtk_menu_shell_leave_notify;
233   widget_class->screen_changed = gtk_menu_shell_screen_changed;
234
235   container_class->add = gtk_menu_shell_add;
236   container_class->remove = gtk_menu_shell_remove;
237   container_class->forall = gtk_menu_shell_forall;
238   container_class->child_type = gtk_menu_shell_child_type;
239
240   klass->submenu_placement = GTK_TOP_BOTTOM;
241   klass->deactivate = gtk_real_menu_shell_deactivate;
242   klass->selection_done = NULL;
243   klass->move_current = gtk_real_menu_shell_move_current;
244   klass->activate_current = gtk_real_menu_shell_activate_current;
245   klass->cancel = gtk_real_menu_shell_cancel;
246   klass->select_item = gtk_menu_shell_real_select_item;
247   klass->insert = gtk_menu_shell_real_insert;
248   klass->move_selected = gtk_menu_shell_real_move_selected;
249
250   menu_shell_signals[DEACTIVATE] =
251     g_signal_new (I_("deactivate"),
252                   G_OBJECT_CLASS_TYPE (object_class),
253                   G_SIGNAL_RUN_FIRST,
254                   G_STRUCT_OFFSET (GtkMenuShellClass, deactivate),
255                   NULL, NULL,
256                   _gtk_marshal_VOID__VOID,
257                   G_TYPE_NONE, 0);
258
259   menu_shell_signals[SELECTION_DONE] =
260     g_signal_new (I_("selection-done"),
261                   G_OBJECT_CLASS_TYPE (object_class),
262                   G_SIGNAL_RUN_FIRST,
263                   G_STRUCT_OFFSET (GtkMenuShellClass, selection_done),
264                   NULL, NULL,
265                   _gtk_marshal_VOID__VOID,
266                   G_TYPE_NONE, 0);
267
268   menu_shell_signals[MOVE_CURRENT] =
269     g_signal_new (I_("move-current"),
270                   G_OBJECT_CLASS_TYPE (object_class),
271                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
272                   G_STRUCT_OFFSET (GtkMenuShellClass, move_current),
273                   NULL, NULL,
274                   _gtk_marshal_VOID__ENUM,
275                   G_TYPE_NONE, 1,
276                   GTK_TYPE_MENU_DIRECTION_TYPE);
277
278   menu_shell_signals[ACTIVATE_CURRENT] =
279     g_signal_new (I_("activate-current"),
280                   G_OBJECT_CLASS_TYPE (object_class),
281                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
282                   G_STRUCT_OFFSET (GtkMenuShellClass, activate_current),
283                   NULL, NULL,
284                   _gtk_marshal_VOID__BOOLEAN,
285                   G_TYPE_NONE, 1,
286                   G_TYPE_BOOLEAN);
287
288   menu_shell_signals[CANCEL] =
289     g_signal_new (I_("cancel"),
290                   G_OBJECT_CLASS_TYPE (object_class),
291                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
292                   G_STRUCT_OFFSET (GtkMenuShellClass, cancel),
293                   NULL, NULL,
294                   _gtk_marshal_VOID__VOID,
295                   G_TYPE_NONE, 0);
296
297   menu_shell_signals[CYCLE_FOCUS] =
298     g_signal_new_class_handler (I_("cycle-focus"),
299                                 G_OBJECT_CLASS_TYPE (object_class),
300                                 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
301                                 G_CALLBACK (gtk_real_menu_shell_cycle_focus),
302                                 NULL, NULL,
303                                 _gtk_marshal_VOID__ENUM,
304                                 G_TYPE_NONE, 1,
305                                 GTK_TYPE_DIRECTION_TYPE);
306
307   /**
308    * GtkMenuShell::move-selected:
309    * @menu_shell: the object on which the signal is emitted
310    * @distance: +1 to move to the next item, -1 to move to the previous
311    *
312    * The ::move-selected signal is emitted to move the selection to
313    * another item.
314    *
315    * Returns: %TRUE to stop the signal emission, %FALSE to continue
316    *
317    * Since: 2.12
318    */
319   menu_shell_signals[MOVE_SELECTED] =
320     g_signal_new (I_("move-selected"),
321                   G_OBJECT_CLASS_TYPE (object_class),
322                   G_SIGNAL_RUN_LAST,
323                   G_STRUCT_OFFSET (GtkMenuShellClass, move_selected),
324                   _gtk_boolean_handled_accumulator, NULL,
325                   _gtk_marshal_BOOLEAN__INT,
326                   G_TYPE_BOOLEAN, 1,
327                   G_TYPE_INT);
328
329   binding_set = gtk_binding_set_by_class (klass);
330   gtk_binding_entry_add_signal (binding_set,
331                                 GDK_Escape, 0,
332                                 "cancel", 0);
333   gtk_binding_entry_add_signal (binding_set,
334                                 GDK_Return, 0,
335                                 "activate-current", 1,
336                                 G_TYPE_BOOLEAN,
337                                 TRUE);
338   gtk_binding_entry_add_signal (binding_set,
339                                 GDK_ISO_Enter, 0,
340                                 "activate-current", 1,
341                                 G_TYPE_BOOLEAN,
342                                 TRUE);
343   gtk_binding_entry_add_signal (binding_set,
344                                 GDK_KP_Enter, 0,
345                                 "activate-current", 1,
346                                 G_TYPE_BOOLEAN,
347                                 TRUE);
348   gtk_binding_entry_add_signal (binding_set,
349                                 GDK_space, 0,
350                                 "activate-current", 1,
351                                 G_TYPE_BOOLEAN,
352                                 FALSE);
353   gtk_binding_entry_add_signal (binding_set,
354                                 GDK_KP_Space, 0,
355                                 "activate-current", 1,
356                                 G_TYPE_BOOLEAN,
357                                 FALSE);
358   gtk_binding_entry_add_signal (binding_set,
359                                 GDK_F10, 0,
360                                 "cycle-focus", 1,
361                                 GTK_TYPE_DIRECTION_TYPE, GTK_DIR_TAB_FORWARD);
362   gtk_binding_entry_add_signal (binding_set,
363                                 GDK_F10, GDK_SHIFT_MASK,
364                                 "cycle-focus", 1,
365                                 GTK_TYPE_DIRECTION_TYPE, GTK_DIR_TAB_BACKWARD);
366
367   /**
368    * GtkMenuShell:take-focus:
369    *
370    * A boolean that determines whether the menu and its submenus grab the
371    * keyboard focus. See gtk_menu_shell_set_take_focus() and
372    * gtk_menu_shell_get_take_focus().
373    *
374    * Since: 2.8
375    **/
376   g_object_class_install_property (object_class,
377                                    PROP_TAKE_FOCUS,
378                                    g_param_spec_boolean ("take-focus",
379                                                          P_("Take Focus"),
380                                                          P_("A boolean that determines whether the menu grabs the keyboard focus"),
381                                                          TRUE,
382                                                          GTK_PARAM_READWRITE));
383
384   g_type_class_add_private (object_class, sizeof (GtkMenuShellPrivate));
385 }
386
387 static GType
388 gtk_menu_shell_child_type (GtkContainer     *container)
389 {
390   return GTK_TYPE_MENU_ITEM;
391 }
392
393 static void
394 gtk_menu_shell_init (GtkMenuShell *menu_shell)
395 {
396   GtkMenuShellPrivate *priv = GTK_MENU_SHELL_GET_PRIVATE (menu_shell);
397
398   menu_shell->children = NULL;
399   menu_shell->active_menu_item = NULL;
400   menu_shell->parent_menu_shell = NULL;
401   menu_shell->active = FALSE;
402   menu_shell->have_grab = FALSE;
403   menu_shell->have_xgrab = FALSE;
404   menu_shell->button = 0;
405   menu_shell->activate_time = 0;
406
407   priv->mnemonic_hash = NULL;
408   priv->key_hash = NULL;
409   priv->take_focus = TRUE;
410   priv->activated_submenu = FALSE;
411 }
412
413 static void
414 gtk_menu_shell_set_property (GObject      *object,
415                              guint         prop_id,
416                              const GValue *value,
417                              GParamSpec   *pspec)
418 {
419   GtkMenuShell *menu_shell = GTK_MENU_SHELL (object);
420
421   switch (prop_id)
422     {
423     case PROP_TAKE_FOCUS:
424       gtk_menu_shell_set_take_focus (menu_shell, g_value_get_boolean (value));
425       break;
426     default:
427       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
428       break;
429     }
430 }
431
432 static void
433 gtk_menu_shell_get_property (GObject     *object,
434                              guint        prop_id,
435                              GValue      *value,
436                              GParamSpec  *pspec)
437 {
438   GtkMenuShell *menu_shell = GTK_MENU_SHELL (object);
439
440   switch (prop_id)
441     {
442     case PROP_TAKE_FOCUS:
443       g_value_set_boolean (value, gtk_menu_shell_get_take_focus (menu_shell));
444       break;
445     default:
446       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
447       break;
448     }
449 }
450
451 static void
452 gtk_menu_shell_finalize (GObject *object)
453 {
454   GtkMenuShell *menu_shell = GTK_MENU_SHELL (object);
455   GtkMenuShellPrivate *priv = GTK_MENU_SHELL_GET_PRIVATE (menu_shell);
456
457   if (priv->mnemonic_hash)
458     _gtk_mnemonic_hash_free (priv->mnemonic_hash);
459   if (priv->key_hash)
460     _gtk_key_hash_free (priv->key_hash);
461
462   G_OBJECT_CLASS (gtk_menu_shell_parent_class)->finalize (object);
463 }
464
465
466 void
467 gtk_menu_shell_append (GtkMenuShell *menu_shell,
468                        GtkWidget    *child)
469 {
470   gtk_menu_shell_insert (menu_shell, child, -1);
471 }
472
473 void
474 gtk_menu_shell_prepend (GtkMenuShell *menu_shell,
475                         GtkWidget    *child)
476 {
477   gtk_menu_shell_insert (menu_shell, child, 0);
478 }
479
480 void
481 gtk_menu_shell_insert (GtkMenuShell *menu_shell,
482                        GtkWidget    *child,
483                        gint          position)
484 {
485   GtkMenuShellClass *class;
486
487   g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
488   g_return_if_fail (GTK_IS_MENU_ITEM (child));
489
490   class = GTK_MENU_SHELL_GET_CLASS (menu_shell);
491
492   if (class->insert)
493     class->insert (menu_shell, child, position);
494 }
495
496 static void
497 gtk_menu_shell_real_insert (GtkMenuShell *menu_shell,
498                             GtkWidget    *child,
499                             gint          position)
500 {
501   menu_shell->children = g_list_insert (menu_shell->children, child, position);
502
503   gtk_widget_set_parent (child, GTK_WIDGET (menu_shell));
504 }
505
506 void
507 gtk_menu_shell_deactivate (GtkMenuShell *menu_shell)
508 {
509   g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
510
511   g_signal_emit (menu_shell, menu_shell_signals[DEACTIVATE], 0);
512 }
513
514 static void
515 gtk_menu_shell_realize (GtkWidget *widget)
516 {
517   GdkWindowAttr attributes;
518   gint attributes_mask;
519
520   gtk_widget_set_realized (widget, TRUE);
521
522   attributes.x = widget->allocation.x;
523   attributes.y = widget->allocation.y;
524   attributes.width = widget->allocation.width;
525   attributes.height = widget->allocation.height;
526   attributes.window_type = GDK_WINDOW_CHILD;
527   attributes.wclass = GDK_INPUT_OUTPUT;
528   attributes.visual = gtk_widget_get_visual (widget);
529   attributes.colormap = gtk_widget_get_colormap (widget);
530   attributes.event_mask = gtk_widget_get_events (widget);
531   attributes.event_mask |= (GDK_EXPOSURE_MASK |
532                             GDK_BUTTON_PRESS_MASK |
533                             GDK_BUTTON_RELEASE_MASK |
534                             GDK_KEY_PRESS_MASK |
535                             GDK_ENTER_NOTIFY_MASK |
536                             GDK_LEAVE_NOTIFY_MASK);
537
538   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
539   widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
540   gdk_window_set_user_data (widget->window, widget);
541
542   widget->style = gtk_style_attach (widget->style, widget->window);
543   gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
544 }
545
546 void
547 _gtk_menu_shell_activate (GtkMenuShell *menu_shell)
548 {
549   if (!menu_shell->active)
550     {
551       gtk_grab_add (GTK_WIDGET (menu_shell));
552       menu_shell->have_grab = TRUE;
553       menu_shell->active = TRUE;
554     }
555 }
556
557 static gint
558 gtk_menu_shell_button_press (GtkWidget      *widget,
559                              GdkEventButton *event)
560 {
561   GtkMenuShell *menu_shell;
562   GtkWidget *menu_item;
563
564   if (event->type != GDK_BUTTON_PRESS)
565     return FALSE;
566
567   menu_shell = GTK_MENU_SHELL (widget);
568
569   if (menu_shell->parent_menu_shell)
570     return gtk_widget_event (menu_shell->parent_menu_shell, (GdkEvent*) event);
571
572   menu_item = gtk_menu_shell_get_item (menu_shell, (GdkEvent *)event);
573
574   if (menu_item && _gtk_menu_item_is_selectable (menu_item) &&
575       menu_item != GTK_MENU_SHELL (menu_item->parent)->active_menu_item)
576     {
577       /*  select the menu item *before* activating the shell, so submenus
578        *  which might be open are closed the friendly way. If we activate
579        *  (and thus grab) this menu shell first, we might get grab_broken
580        *  events which will close the entire menu hierarchy. Selecting the
581        *  menu item also fixes up the state as if enter_notify() would
582        *  have run before (which normally selects the item).
583        */
584       if (GTK_MENU_SHELL_GET_CLASS (menu_item->parent)->submenu_placement != GTK_TOP_BOTTOM)
585         {
586           gtk_menu_shell_select_item (GTK_MENU_SHELL (menu_item->parent), menu_item);
587         }
588     }
589
590   if (!menu_shell->active || !menu_shell->button)
591     {
592       _gtk_menu_shell_activate (menu_shell);
593
594       menu_shell->button = event->button;
595
596       if (menu_item && _gtk_menu_item_is_selectable (menu_item) &&
597           menu_item->parent == widget &&
598           menu_item != menu_shell->active_menu_item)
599         {
600           if (GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement == GTK_TOP_BOTTOM)
601             {
602               menu_shell->activate_time = event->time;
603               gtk_menu_shell_select_item (menu_shell, menu_item);
604             }
605         }
606     }
607   else
608     {
609       widget = gtk_get_event_widget ((GdkEvent*) event);
610       if (widget == GTK_WIDGET (menu_shell))
611         {
612           gtk_menu_shell_deactivate (menu_shell);
613           g_signal_emit (menu_shell, menu_shell_signals[SELECTION_DONE], 0);
614         }
615     }
616
617   if (menu_item && _gtk_menu_item_is_selectable (menu_item) &&
618       GTK_MENU_ITEM (menu_item)->submenu != NULL &&
619       !gtk_widget_get_visible (GTK_MENU_ITEM (menu_item)->submenu))
620     {
621       GtkMenuShellPrivate *priv;
622
623       _gtk_menu_item_popup_submenu (menu_item, FALSE);
624
625       priv = GTK_MENU_SHELL_GET_PRIVATE (menu_item->parent);
626       priv->activated_submenu = TRUE;
627     }
628
629   return TRUE;
630 }
631
632 static gboolean
633 gtk_menu_shell_grab_broken (GtkWidget          *widget,
634                             GdkEventGrabBroken *event)
635 {
636   GtkMenuShell *menu_shell = GTK_MENU_SHELL (widget);
637
638   if (menu_shell->have_xgrab && event->grab_window == NULL)
639     {
640       /* Unset the active menu item so gtk_menu_popdown() doesn't see it.
641        */
642       
643       gtk_menu_shell_deselect (menu_shell);
644       
645       gtk_menu_shell_deactivate (menu_shell);
646       g_signal_emit (menu_shell, menu_shell_signals[SELECTION_DONE], 0);
647     }
648
649   return TRUE;
650 }
651
652 static gint
653 gtk_menu_shell_button_release (GtkWidget      *widget,
654                                GdkEventButton *event)
655 {
656   GtkMenuShell *menu_shell = GTK_MENU_SHELL (widget);
657   GtkMenuShellPrivate *priv = GTK_MENU_SHELL_GET_PRIVATE (widget);
658
659   if (menu_shell->active)
660     {
661       GtkWidget *menu_item;
662       gboolean   deactivate = TRUE;
663
664       if (menu_shell->button && (event->button != menu_shell->button))
665         {
666           menu_shell->button = 0;
667           if (menu_shell->parent_menu_shell)
668             return gtk_widget_event (menu_shell->parent_menu_shell, (GdkEvent*) event);
669         }
670
671       menu_shell->button = 0;
672       menu_item = gtk_menu_shell_get_item (menu_shell, (GdkEvent*) event);
673
674       if ((event->time - menu_shell->activate_time) > MENU_SHELL_TIMEOUT)
675         {
676           if (menu_item && (menu_shell->active_menu_item == menu_item) &&
677               _gtk_menu_item_is_selectable (menu_item))
678             {
679               GtkWidget *submenu = GTK_MENU_ITEM (menu_item)->submenu;
680
681               if (submenu == NULL)
682                 {
683                   gtk_menu_shell_activate_item (menu_shell, menu_item, TRUE);
684
685                   deactivate = FALSE;
686                 }
687               else if (GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement != GTK_TOP_BOTTOM ||
688                        priv->activated_submenu)
689                 {
690                   gint popdown_delay;
691                   GTimeVal *popup_time;
692                   gint64 usec_since_popup = 0;
693
694                   g_object_get (gtk_widget_get_settings (widget),
695                                 "gtk-menu-popdown-delay", &popdown_delay,
696                                 NULL);
697
698                   popup_time = g_object_get_data (G_OBJECT (submenu),
699                                                   "gtk-menu-exact-popup-time");
700
701                   if (popup_time)
702                     {
703                       GTimeVal current_time;
704
705                       g_get_current_time (&current_time);
706
707                       usec_since_popup = ((gint64) current_time.tv_sec * 1000 * 1000 +
708                                           (gint64) current_time.tv_usec -
709                                           (gint64) popup_time->tv_sec * 1000 * 1000 -
710                                           (gint64) popup_time->tv_usec);
711
712                       g_object_set_data (G_OBJECT (submenu),
713                                          "gtk-menu-exact-popup-time", NULL);
714                     }
715
716                   /*  only close the submenu on click if we opened the
717                    *  menu explicitely (usec_since_popup == 0) or
718                    *  enough time has passed since it was opened by
719                    *  GtkMenuItem's timeout (usec_since_popup > delay).
720                    */
721                   if (!priv->activated_submenu &&
722                       (usec_since_popup == 0 ||
723                        usec_since_popup > popdown_delay * 1000))
724                     {
725                       _gtk_menu_item_popdown_submenu (menu_item);
726                     }
727                   else
728                     {
729                       gtk_menu_item_select (GTK_MENU_ITEM (menu_item));
730                     }
731
732                   deactivate = FALSE;
733                 }
734             }
735           else if (menu_item &&
736                    !_gtk_menu_item_is_selectable (menu_item) &&
737                    GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement != GTK_TOP_BOTTOM)
738             {
739               deactivate = FALSE;
740             }
741           else if (menu_shell->parent_menu_shell)
742             {
743               menu_shell->active = TRUE;
744               gtk_widget_event (menu_shell->parent_menu_shell, (GdkEvent*) event);
745               deactivate = FALSE;
746             }
747
748           /* If we ended up on an item with a submenu, leave the menu up.
749            */
750           if (menu_item && (menu_shell->active_menu_item == menu_item) &&
751               GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement != GTK_TOP_BOTTOM)
752             {
753               deactivate = FALSE;
754             }
755         }
756       else /* a very fast press-release */
757         {
758           /* We only ever want to prevent deactivation on the first
759            * press/release. Setting the time to zero is a bit of a
760            * hack, since we could be being triggered in the first
761            * few fractions of a second after a server time wraparound.
762            * the chances of that happening are ~1/10^6, without
763            * serious harm if we lose.
764            */
765           menu_shell->activate_time = 0;
766           deactivate = FALSE;
767         }
768
769       if (deactivate)
770         {
771           gtk_menu_shell_deactivate (menu_shell);
772           g_signal_emit (menu_shell, menu_shell_signals[SELECTION_DONE], 0);
773         }
774
775       priv->activated_submenu = FALSE;
776     }
777
778   return TRUE;
779 }
780
781 void
782 _gtk_menu_shell_set_keyboard_mode (GtkMenuShell *menu_shell,
783                                    gboolean      keyboard_mode)
784 {
785   menu_shell->keyboard_mode = keyboard_mode;
786 }
787
788 gboolean
789 _gtk_menu_shell_get_keyboard_mode (GtkMenuShell *menu_shell)
790 {
791   return menu_shell->keyboard_mode;
792 }
793
794 void
795 _gtk_menu_shell_update_mnemonics (GtkMenuShell *menu_shell)
796 {
797   GtkMenuShell *target;
798   gboolean auto_mnemonics;
799   gboolean found;
800   gboolean mnemonics_visible;
801
802   g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu_shell)),
803                 "gtk-auto-mnemonics", &auto_mnemonics, NULL);
804
805   if (!auto_mnemonics)
806     return;
807
808   target = menu_shell;
809   found = FALSE;
810   while (target)
811     {
812       GtkMenuShellPrivate *priv = GTK_MENU_SHELL_GET_PRIVATE (target);
813       GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (target));
814
815       /* The idea with keyboard mode is that once you start using
816        * the keyboard to navigate the menus, we show mnemonics
817        * until the menu navigation is over. To that end, we spread
818        * the keyboard mode upwards in the menu hierarchy here.
819        * Also see gtk_menu_popup, where we inherit it downwards.
820        */
821       if (menu_shell->keyboard_mode)
822         target->keyboard_mode = TRUE;
823
824       /* While navigating menus, the first parent menu with an active
825        * item is the one where mnemonics are effective, as can be seen
826        * in gtk_menu_shell_key_press below.
827        * We also show mnemonics in context menus. The grab condition is
828        * necessary to ensure we remove underlines from menu bars when
829        * dismissing menus.
830        */
831       mnemonics_visible = target->keyboard_mode &&
832                           (((target->active_menu_item || priv->in_unselectable_item) && !found) ||
833                            (target == menu_shell &&
834                             !target->parent_menu_shell &&
835                             gtk_widget_has_grab (GTK_WIDGET (target))));
836
837       /* While menus are up, only show underlines inside the menubar,
838        * not in the entire window.
839        */
840       if (GTK_IS_MENU_BAR (target))
841         {
842           gtk_window_set_mnemonics_visible (GTK_WINDOW (toplevel), FALSE);
843           _gtk_label_mnemonics_visible_apply_recursively (GTK_WIDGET (target),
844                                                           mnemonics_visible);
845         }
846       else
847         gtk_window_set_mnemonics_visible (GTK_WINDOW (toplevel), mnemonics_visible);
848
849       if (target->active_menu_item || priv->in_unselectable_item)
850         found = TRUE;
851
852       target = GTK_MENU_SHELL (target->parent_menu_shell);
853     }
854 }
855
856 static gint
857 gtk_menu_shell_key_press (GtkWidget   *widget,
858                           GdkEventKey *event)
859 {
860   GtkMenuShell *menu_shell = GTK_MENU_SHELL (widget);
861   GtkMenuShellPrivate *priv = GTK_MENU_SHELL_GET_PRIVATE (menu_shell);
862   gboolean enable_mnemonics;
863
864   menu_shell->keyboard_mode = TRUE;
865
866   if (!(menu_shell->active_menu_item || priv->in_unselectable_item) && menu_shell->parent_menu_shell)
867     return gtk_widget_event (menu_shell->parent_menu_shell, (GdkEvent *)event);
868
869   if (gtk_bindings_activate_event (GTK_OBJECT (widget), event))
870     return TRUE;
871
872   g_object_get (gtk_widget_get_settings (widget),
873                 "gtk-enable-mnemonics", &enable_mnemonics,
874                 NULL);
875
876   if (enable_mnemonics)
877     return gtk_menu_shell_activate_mnemonic (menu_shell, event);
878
879   return FALSE;
880 }
881
882 static gint
883 gtk_menu_shell_enter_notify (GtkWidget        *widget,
884                              GdkEventCrossing *event)
885 {
886   GtkMenuShell *menu_shell = GTK_MENU_SHELL (widget);
887
888   if (event->mode == GDK_CROSSING_GTK_GRAB ||
889       event->mode == GDK_CROSSING_GTK_UNGRAB ||
890       event->mode == GDK_CROSSING_STATE_CHANGED)
891     return TRUE;
892
893   if (menu_shell->active)
894     {
895       GtkWidget *menu_item;
896
897       menu_item = gtk_get_event_widget ((GdkEvent*) event);
898
899       if (!menu_item)
900         return TRUE;
901
902       if (GTK_IS_MENU_ITEM (menu_item) &&
903           !_gtk_menu_item_is_selectable (menu_item))
904         {
905           GtkMenuShellPrivate *priv;
906
907           priv = GTK_MENU_SHELL_GET_PRIVATE (menu_shell);
908           priv->in_unselectable_item = TRUE;
909
910           return TRUE;
911         }
912
913       if (menu_item->parent == widget &&
914           GTK_IS_MENU_ITEM (menu_item))
915         {
916           if (menu_shell->ignore_enter)
917             return TRUE;
918
919           if (event->detail != GDK_NOTIFY_INFERIOR)
920             {
921               if (gtk_widget_get_state (menu_item) != GTK_STATE_PRELIGHT)
922                 gtk_menu_shell_select_item (menu_shell, menu_item);
923
924               /* If any mouse button is down, and there is a submenu
925                * that is not yet visible, activate it. It's sufficient
926                * to check for any button's mask (not only the one
927                * matching menu_shell->button), because there is no
928                * situation a mouse button could be pressed while
929                * entering a menu item where we wouldn't want to show
930                * its submenu.
931                */
932               if ((event->state & (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK | GDK_BUTTON3_MASK)) &&
933                   GTK_MENU_ITEM (menu_item)->submenu != NULL)
934                 {
935                   GtkMenuShellPrivate *priv;
936
937                   priv = GTK_MENU_SHELL_GET_PRIVATE (menu_item->parent);
938                   priv->activated_submenu = TRUE;
939
940                   if (!gtk_widget_get_visible (GTK_MENU_ITEM (menu_item)->submenu))
941                     {
942                       gboolean touchscreen_mode;
943
944                       g_object_get (gtk_widget_get_settings (widget),
945                                     "gtk-touchscreen-mode", &touchscreen_mode,
946                                     NULL);
947
948                       if (touchscreen_mode)
949                         _gtk_menu_item_popup_submenu (menu_item, TRUE);
950                     }
951                 }
952             }
953         }
954       else if (menu_shell->parent_menu_shell)
955         {
956           gtk_widget_event (menu_shell->parent_menu_shell, (GdkEvent*) event);
957         }
958     }
959
960   return TRUE;
961 }
962
963 static gint
964 gtk_menu_shell_leave_notify (GtkWidget        *widget,
965                              GdkEventCrossing *event)
966 {
967   if (event->mode == GDK_CROSSING_GTK_GRAB ||
968       event->mode == GDK_CROSSING_GTK_GRAB ||
969       event->mode == GDK_CROSSING_STATE_CHANGED)
970     return TRUE;
971
972   if (gtk_widget_get_visible (widget))
973     {
974       GtkMenuShell *menu_shell = GTK_MENU_SHELL (widget);
975       GtkWidget *event_widget = gtk_get_event_widget ((GdkEvent*) event);
976       GtkMenuItem *menu_item;
977
978       if (!event_widget || !GTK_IS_MENU_ITEM (event_widget))
979         return TRUE;
980
981       menu_item = GTK_MENU_ITEM (event_widget);
982
983       if (!_gtk_menu_item_is_selectable (event_widget))
984         {
985           GtkMenuShellPrivate *priv;
986
987           priv = GTK_MENU_SHELL_GET_PRIVATE (menu_shell);
988           priv->in_unselectable_item = TRUE;
989
990           return TRUE;
991         }
992
993       if ((menu_shell->active_menu_item == event_widget) &&
994           (menu_item->submenu == NULL))
995         {
996           if ((event->detail != GDK_NOTIFY_INFERIOR) &&
997               (gtk_widget_get_state (GTK_WIDGET (menu_item)) != GTK_STATE_NORMAL))
998             {
999               gtk_menu_shell_deselect (menu_shell);
1000             }
1001         }
1002       else if (menu_shell->parent_menu_shell)
1003         {
1004           gtk_widget_event (menu_shell->parent_menu_shell, (GdkEvent*) event);
1005         }
1006     }
1007
1008   return TRUE;
1009 }
1010
1011 static void
1012 gtk_menu_shell_screen_changed (GtkWidget *widget,
1013                                GdkScreen *previous_screen)
1014 {
1015   gtk_menu_shell_reset_key_hash (GTK_MENU_SHELL (widget));
1016 }
1017
1018 static void
1019 gtk_menu_shell_add (GtkContainer *container,
1020                     GtkWidget    *widget)
1021 {
1022   gtk_menu_shell_append (GTK_MENU_SHELL (container), widget);
1023 }
1024
1025 static void
1026 gtk_menu_shell_remove (GtkContainer *container,
1027                        GtkWidget    *widget)
1028 {
1029   GtkMenuShell *menu_shell = GTK_MENU_SHELL (container);
1030   gint was_visible;
1031
1032   was_visible = gtk_widget_get_visible (widget);
1033   menu_shell->children = g_list_remove (menu_shell->children, widget);
1034   
1035   if (widget == menu_shell->active_menu_item)
1036     {
1037       gtk_item_deselect (GTK_ITEM (menu_shell->active_menu_item));
1038       menu_shell->active_menu_item = NULL;
1039     }
1040
1041   gtk_widget_unparent (widget);
1042   
1043   /* queue resize regardless of gtk_widget_get_visible (container),
1044    * since that's what is needed by toplevels.
1045    */
1046   if (was_visible)
1047     gtk_widget_queue_resize (GTK_WIDGET (container));
1048 }
1049
1050 static void
1051 gtk_menu_shell_forall (GtkContainer *container,
1052                        gboolean      include_internals,
1053                        GtkCallback   callback,
1054                        gpointer      callback_data)
1055 {
1056   GtkMenuShell *menu_shell = GTK_MENU_SHELL (container);
1057   GtkWidget *child;
1058   GList *children;
1059
1060   children = menu_shell->children;
1061   while (children)
1062     {
1063       child = children->data;
1064       children = children->next;
1065
1066       (* callback) (child, callback_data);
1067     }
1068 }
1069
1070
1071 static void
1072 gtk_real_menu_shell_deactivate (GtkMenuShell *menu_shell)
1073 {
1074   if (menu_shell->active)
1075     {
1076       menu_shell->button = 0;
1077       menu_shell->active = FALSE;
1078       menu_shell->activate_time = 0;
1079
1080       if (menu_shell->active_menu_item)
1081         {
1082           gtk_menu_item_deselect (GTK_MENU_ITEM (menu_shell->active_menu_item));
1083           menu_shell->active_menu_item = NULL;
1084         }
1085
1086       if (menu_shell->have_grab)
1087         {
1088           menu_shell->have_grab = FALSE;
1089           gtk_grab_remove (GTK_WIDGET (menu_shell));
1090         }
1091       if (menu_shell->have_xgrab)
1092         {
1093           GdkDisplay *display = gtk_widget_get_display (GTK_WIDGET (menu_shell));
1094
1095           menu_shell->have_xgrab = FALSE;
1096           gdk_display_pointer_ungrab (display, GDK_CURRENT_TIME);
1097           gdk_display_keyboard_ungrab (display, GDK_CURRENT_TIME);
1098         }
1099
1100       menu_shell->keyboard_mode = FALSE;
1101
1102       _gtk_menu_shell_update_mnemonics (menu_shell);
1103     }
1104 }
1105
1106 static gint
1107 gtk_menu_shell_is_item (GtkMenuShell *menu_shell,
1108                         GtkWidget    *child)
1109 {
1110   GtkWidget *parent;
1111
1112   g_return_val_if_fail (GTK_IS_MENU_SHELL (menu_shell), FALSE);
1113   g_return_val_if_fail (child != NULL, FALSE);
1114
1115   parent = child->parent;
1116   while (GTK_IS_MENU_SHELL (parent))
1117     {
1118       if (parent == (GtkWidget*) menu_shell)
1119         return TRUE;
1120       parent = GTK_MENU_SHELL (parent)->parent_menu_shell;
1121     }
1122
1123   return FALSE;
1124 }
1125
1126 static GtkWidget*
1127 gtk_menu_shell_get_item (GtkMenuShell *menu_shell,
1128                          GdkEvent     *event)
1129 {
1130   GtkWidget *menu_item;
1131
1132   menu_item = gtk_get_event_widget ((GdkEvent*) event);
1133   
1134   while (menu_item && !GTK_IS_MENU_ITEM (menu_item))
1135     menu_item = menu_item->parent;
1136
1137   if (menu_item && gtk_menu_shell_is_item (menu_shell, menu_item))
1138     return menu_item;
1139   else
1140     return NULL;
1141 }
1142
1143 /* Handlers for action signals */
1144
1145 void
1146 gtk_menu_shell_select_item (GtkMenuShell *menu_shell,
1147                             GtkWidget    *menu_item)
1148 {
1149   GtkMenuShellClass *class;
1150
1151   g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
1152   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
1153
1154   class = GTK_MENU_SHELL_GET_CLASS (menu_shell);
1155
1156   if (class->select_item &&
1157       !(menu_shell->active &&
1158         menu_shell->active_menu_item == menu_item))
1159     class->select_item (menu_shell, menu_item);
1160 }
1161
1162 void _gtk_menu_item_set_placement (GtkMenuItem         *menu_item,
1163                                    GtkSubmenuPlacement  placement);
1164
1165 static void
1166 gtk_menu_shell_real_select_item (GtkMenuShell *menu_shell,
1167                                  GtkWidget    *menu_item)
1168 {
1169   GtkPackDirection pack_dir = PACK_DIRECTION (menu_shell);
1170
1171   gtk_menu_shell_deselect (menu_shell);
1172
1173   if (!_gtk_menu_item_is_selectable (menu_item))
1174     {
1175       GtkMenuShellPrivate *priv = GTK_MENU_SHELL_GET_PRIVATE (menu_shell);
1176
1177       priv->in_unselectable_item = TRUE;
1178       _gtk_menu_shell_update_mnemonics (menu_shell);
1179
1180       return;
1181     }
1182
1183   menu_shell->active_menu_item = menu_item;
1184   if (pack_dir == GTK_PACK_DIRECTION_TTB || pack_dir == GTK_PACK_DIRECTION_BTT)
1185     _gtk_menu_item_set_placement (GTK_MENU_ITEM (menu_shell->active_menu_item),
1186                                   GTK_LEFT_RIGHT);
1187   else
1188     _gtk_menu_item_set_placement (GTK_MENU_ITEM (menu_shell->active_menu_item),
1189                                   GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement);
1190   gtk_menu_item_select (GTK_MENU_ITEM (menu_shell->active_menu_item));
1191
1192   _gtk_menu_shell_update_mnemonics (menu_shell);
1193
1194   /* This allows the bizarre radio buttons-with-submenus-display-history
1195    * behavior
1196    */
1197   if (GTK_MENU_ITEM (menu_shell->active_menu_item)->submenu)
1198     gtk_widget_activate (menu_shell->active_menu_item);
1199 }
1200
1201 void
1202 gtk_menu_shell_deselect (GtkMenuShell *menu_shell)
1203 {
1204   g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
1205
1206   if (menu_shell->active_menu_item)
1207     {
1208       gtk_menu_item_deselect (GTK_MENU_ITEM (menu_shell->active_menu_item));
1209       menu_shell->active_menu_item = NULL;
1210       _gtk_menu_shell_update_mnemonics (menu_shell);
1211     }
1212 }
1213
1214 void
1215 gtk_menu_shell_activate_item (GtkMenuShell      *menu_shell,
1216                               GtkWidget         *menu_item,
1217                               gboolean           force_deactivate)
1218 {
1219   GSList *slist, *shells = NULL;
1220   gboolean deactivate = force_deactivate;
1221
1222   g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
1223   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
1224
1225   if (!deactivate)
1226     deactivate = GTK_MENU_ITEM_GET_CLASS (menu_item)->hide_on_activate;
1227
1228   g_object_ref (menu_shell);
1229   g_object_ref (menu_item);
1230
1231   if (deactivate)
1232     {
1233       GtkMenuShell *parent_menu_shell = menu_shell;
1234
1235       do
1236         {
1237           g_object_ref (parent_menu_shell);
1238           shells = g_slist_prepend (shells, parent_menu_shell);
1239           parent_menu_shell = (GtkMenuShell*) parent_menu_shell->parent_menu_shell;
1240         }
1241       while (parent_menu_shell);
1242       shells = g_slist_reverse (shells);
1243
1244       gtk_menu_shell_deactivate (menu_shell);
1245   
1246       /* flush the x-queue, so any grabs are removed and
1247        * the menu is actually taken down
1248        */
1249       gdk_display_sync (gtk_widget_get_display (menu_item));
1250     }
1251
1252   gtk_widget_activate (menu_item);
1253
1254   for (slist = shells; slist; slist = slist->next)
1255     {
1256       g_signal_emit (slist->data, menu_shell_signals[SELECTION_DONE], 0);
1257       g_object_unref (slist->data);
1258     }
1259   g_slist_free (shells);
1260
1261   g_object_unref (menu_shell);
1262   g_object_unref (menu_item);
1263 }
1264
1265 /* Distance should be +/- 1 */
1266 static gboolean
1267 gtk_menu_shell_real_move_selected (GtkMenuShell  *menu_shell, 
1268                                    gint           distance)
1269 {
1270   if (menu_shell->active_menu_item)
1271     {
1272       GList *node = g_list_find (menu_shell->children,
1273                                  menu_shell->active_menu_item);
1274       GList *start_node = node;
1275       gboolean wrap_around;
1276
1277       g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu_shell)),
1278                     "gtk-keynav-wrap-around", &wrap_around,
1279                     NULL);
1280
1281       if (distance > 0)
1282         {
1283           node = node->next;
1284           while (node != start_node && 
1285                  (!node || !_gtk_menu_item_is_selectable (node->data)))
1286             {
1287               if (node)
1288                 node = node->next;
1289               else if (wrap_around)
1290                 node = menu_shell->children;
1291               else
1292                 {
1293                   gtk_widget_error_bell (GTK_WIDGET (menu_shell));
1294                   break;
1295                 }
1296             }
1297         }
1298       else
1299         {
1300           node = node->prev;
1301           while (node != start_node &&
1302                  (!node || !_gtk_menu_item_is_selectable (node->data)))
1303             {
1304               if (node)
1305                 node = node->prev;
1306               else if (wrap_around)
1307                 node = g_list_last (menu_shell->children);
1308               else
1309                 {
1310                   gtk_widget_error_bell (GTK_WIDGET (menu_shell));
1311                   break;
1312                 }
1313             }
1314         }
1315       
1316       if (node)
1317         gtk_menu_shell_select_item (menu_shell, node->data);
1318     }
1319
1320   return TRUE;
1321 }
1322
1323 /* Distance should be +/- 1 */
1324 static void
1325 gtk_menu_shell_move_selected (GtkMenuShell  *menu_shell, 
1326                               gint           distance)
1327 {
1328   gboolean handled = FALSE;
1329
1330   g_signal_emit (menu_shell, menu_shell_signals[MOVE_SELECTED], 0,
1331                  distance, &handled);
1332 }
1333
1334 /**
1335  * gtk_menu_shell_select_first:
1336  * @menu_shell: a #GtkMenuShell
1337  * @search_sensitive: if %TRUE, search for the first selectable
1338  *                    menu item, otherwise select nothing if
1339  *                    the first item isn't sensitive. This
1340  *                    should be %FALSE if the menu is being
1341  *                    popped up initially.
1342  * 
1343  * Select the first visible or selectable child of the menu shell;
1344  * don't select tearoff items unless the only item is a tearoff
1345  * item.
1346  *
1347  * Since: 2.2
1348  **/
1349 void
1350 gtk_menu_shell_select_first (GtkMenuShell *menu_shell,
1351                              gboolean      search_sensitive)
1352 {
1353   GtkWidget *to_select = NULL;
1354   GList *tmp_list;
1355
1356   tmp_list = menu_shell->children;
1357   while (tmp_list)
1358     {
1359       GtkWidget *child = tmp_list->data;
1360       
1361       if ((!search_sensitive && gtk_widget_get_visible (child)) ||
1362           _gtk_menu_item_is_selectable (child))
1363         {
1364           to_select = child;
1365           if (!GTK_IS_TEAROFF_MENU_ITEM (child))
1366             break;
1367         }
1368       
1369       tmp_list = tmp_list->next;
1370     }
1371
1372   if (to_select)
1373     gtk_menu_shell_select_item (menu_shell, to_select);
1374 }
1375
1376 void
1377 _gtk_menu_shell_select_last (GtkMenuShell *menu_shell,
1378                              gboolean      search_sensitive)
1379 {
1380   GtkWidget *to_select = NULL;
1381   GList *tmp_list;
1382
1383   tmp_list = g_list_last (menu_shell->children);
1384   while (tmp_list)
1385     {
1386       GtkWidget *child = tmp_list->data;
1387       
1388       if ((!search_sensitive && gtk_widget_get_visible (child)) ||
1389           _gtk_menu_item_is_selectable (child))
1390         {
1391           to_select = child;
1392           if (!GTK_IS_TEAROFF_MENU_ITEM (child))
1393             break;
1394         }
1395       
1396       tmp_list = tmp_list->prev;
1397     }
1398
1399   if (to_select)
1400     gtk_menu_shell_select_item (menu_shell, to_select);
1401 }
1402
1403 static gboolean
1404 gtk_menu_shell_select_submenu_first (GtkMenuShell     *menu_shell)
1405 {
1406   GtkMenuItem *menu_item;
1407
1408   if (menu_shell->active_menu_item == NULL)
1409     return FALSE;
1410
1411   menu_item = GTK_MENU_ITEM (menu_shell->active_menu_item); 
1412   
1413   if (menu_item->submenu)
1414     {
1415       _gtk_menu_item_popup_submenu (GTK_WIDGET (menu_item), FALSE);
1416       gtk_menu_shell_select_first (GTK_MENU_SHELL (menu_item->submenu), TRUE);
1417       if (GTK_MENU_SHELL (menu_item->submenu)->active_menu_item)
1418         return TRUE;
1419     }
1420
1421   return FALSE;
1422 }
1423
1424 static void
1425 gtk_real_menu_shell_move_current (GtkMenuShell         *menu_shell,
1426                                   GtkMenuDirectionType  direction)
1427 {
1428   GtkMenuShellPrivate *priv = GTK_MENU_SHELL_GET_PRIVATE (menu_shell);
1429   GtkMenuShell *parent_menu_shell = NULL;
1430   gboolean had_selection;
1431   gboolean touchscreen_mode;
1432
1433   priv->in_unselectable_item = FALSE;
1434
1435   had_selection = menu_shell->active_menu_item != NULL;
1436
1437   g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu_shell)),
1438                 "gtk-touchscreen-mode", &touchscreen_mode,
1439                 NULL);
1440
1441   if (menu_shell->parent_menu_shell)
1442     parent_menu_shell = GTK_MENU_SHELL (menu_shell->parent_menu_shell);
1443
1444   switch (direction)
1445     {
1446     case GTK_MENU_DIR_PARENT:
1447       if (touchscreen_mode &&
1448           menu_shell->active_menu_item &&
1449           GTK_MENU_ITEM (menu_shell->active_menu_item)->submenu &&
1450           gtk_widget_get_visible (GTK_MENU_ITEM (menu_shell->active_menu_item)->submenu))
1451         {
1452           /* if we are on a menu item that has an open submenu but the
1453            * focus is not in that submenu (e.g. because it's empty or
1454            * has only insensitive items), close that submenu instead
1455            * of running into the code below which would close *this*
1456            * menu.
1457            */
1458           _gtk_menu_item_popdown_submenu (menu_shell->active_menu_item);
1459           _gtk_menu_shell_update_mnemonics (menu_shell);
1460         }
1461       else if (parent_menu_shell)
1462         {
1463           if (touchscreen_mode)
1464             {
1465               /* close menu when returning from submenu. */
1466               _gtk_menu_item_popdown_submenu (GTK_MENU (menu_shell)->parent_menu_item);
1467               _gtk_menu_shell_update_mnemonics (parent_menu_shell);
1468               break;
1469             }
1470
1471           if (GTK_MENU_SHELL_GET_CLASS (parent_menu_shell)->submenu_placement ==
1472               GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement)
1473             gtk_menu_shell_deselect (menu_shell);
1474           else
1475             {
1476               if (PACK_DIRECTION (parent_menu_shell) == GTK_PACK_DIRECTION_LTR)
1477                 gtk_menu_shell_move_selected (parent_menu_shell, -1);
1478               else
1479                 gtk_menu_shell_move_selected (parent_menu_shell, 1);
1480               gtk_menu_shell_select_submenu_first (parent_menu_shell);
1481             }
1482         }
1483       /* If there is no parent and the submenu is in the opposite direction
1484        * to the menu, then make the PARENT direction wrap around to
1485        * the bottom of the submenu.
1486        */
1487       else if (menu_shell->active_menu_item &&
1488                _gtk_menu_item_is_selectable (menu_shell->active_menu_item) &&
1489                GTK_MENU_ITEM (menu_shell->active_menu_item)->submenu)
1490         {
1491           GtkMenuShell *submenu = GTK_MENU_SHELL (GTK_MENU_ITEM (menu_shell->active_menu_item)->submenu);
1492
1493           if (GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement !=
1494               GTK_MENU_SHELL_GET_CLASS (submenu)->submenu_placement)
1495             _gtk_menu_shell_select_last (submenu, TRUE);
1496         }
1497       break;
1498
1499     case GTK_MENU_DIR_CHILD:
1500       if (menu_shell->active_menu_item &&
1501           _gtk_menu_item_is_selectable (menu_shell->active_menu_item) &&
1502           GTK_MENU_ITEM (menu_shell->active_menu_item)->submenu)
1503         {
1504           if (gtk_menu_shell_select_submenu_first (menu_shell))
1505             break;
1506         }
1507
1508       /* Try to find a menu running the opposite direction */
1509       while (parent_menu_shell &&
1510              (GTK_MENU_SHELL_GET_CLASS (parent_menu_shell)->submenu_placement ==
1511               GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement))
1512         {
1513           parent_menu_shell = GTK_MENU_SHELL (parent_menu_shell->parent_menu_shell);
1514         }
1515
1516       if (parent_menu_shell)
1517         {
1518           if (PACK_DIRECTION (parent_menu_shell) == GTK_PACK_DIRECTION_LTR)
1519             gtk_menu_shell_move_selected (parent_menu_shell, 1);
1520           else
1521             gtk_menu_shell_move_selected (parent_menu_shell, -1);
1522
1523           gtk_menu_shell_select_submenu_first (parent_menu_shell);
1524         }
1525       break;
1526
1527     case GTK_MENU_DIR_PREV:
1528       gtk_menu_shell_move_selected (menu_shell, -1);
1529       if (!had_selection &&
1530           !menu_shell->active_menu_item &&
1531           menu_shell->children)
1532         _gtk_menu_shell_select_last (menu_shell, TRUE);
1533       break;
1534
1535     case GTK_MENU_DIR_NEXT:
1536       gtk_menu_shell_move_selected (menu_shell, 1);
1537       if (!had_selection &&
1538           !menu_shell->active_menu_item &&
1539           menu_shell->children)
1540         gtk_menu_shell_select_first (menu_shell, TRUE);
1541       break;
1542     }
1543 }
1544
1545 static void
1546 gtk_real_menu_shell_activate_current (GtkMenuShell      *menu_shell,
1547                                       gboolean           force_hide)
1548 {
1549   if (menu_shell->active_menu_item &&
1550       _gtk_menu_item_is_selectable (menu_shell->active_menu_item))
1551   {
1552     if (GTK_MENU_ITEM (menu_shell->active_menu_item)->submenu == NULL)
1553       gtk_menu_shell_activate_item (menu_shell,
1554                                     menu_shell->active_menu_item,
1555                                     force_hide);
1556     else
1557       _gtk_menu_item_popup_submenu (menu_shell->active_menu_item, FALSE);
1558   }
1559 }
1560
1561 static void
1562 gtk_real_menu_shell_cancel (GtkMenuShell      *menu_shell)
1563 {
1564   /* Unset the active menu item so gtk_menu_popdown() doesn't see it.
1565    */
1566   gtk_menu_shell_deselect (menu_shell);
1567   
1568   gtk_menu_shell_deactivate (menu_shell);
1569   g_signal_emit (menu_shell, menu_shell_signals[SELECTION_DONE], 0);
1570 }
1571
1572 static void
1573 gtk_real_menu_shell_cycle_focus (GtkMenuShell      *menu_shell,
1574                                  GtkDirectionType   dir)
1575 {
1576   while (menu_shell && !GTK_IS_MENU_BAR (menu_shell))
1577     {
1578       if (menu_shell->parent_menu_shell)
1579         menu_shell = GTK_MENU_SHELL (menu_shell->parent_menu_shell);
1580       else
1581         menu_shell = NULL;
1582     }
1583
1584   if (menu_shell)
1585     _gtk_menu_bar_cycle_focus (GTK_MENU_BAR (menu_shell), dir);
1586 }
1587
1588 gint
1589 _gtk_menu_shell_get_popup_delay (GtkMenuShell *menu_shell)
1590 {
1591   GtkMenuShellClass *klass = GTK_MENU_SHELL_GET_CLASS (menu_shell);
1592   
1593   if (klass->get_popup_delay)
1594     {
1595       return klass->get_popup_delay (menu_shell);
1596     }
1597   else
1598     {
1599       gint popup_delay;
1600       GtkWidget *widget = GTK_WIDGET (menu_shell);
1601       
1602       g_object_get (gtk_widget_get_settings (widget),
1603                     "gtk-menu-popup-delay", &popup_delay,
1604                     NULL);
1605       
1606       return popup_delay;
1607     }
1608 }
1609
1610 /**
1611  * gtk_menu_shell_cancel:
1612  * @menu_shell: a #GtkMenuShell
1613  * 
1614  * Cancels the selection within the menu shell.  
1615  * 
1616  * Since: 2.4
1617  */
1618 void
1619 gtk_menu_shell_cancel (GtkMenuShell *menu_shell)
1620 {
1621   g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
1622
1623   g_signal_emit (menu_shell, menu_shell_signals[CANCEL], 0);
1624 }
1625
1626 static GtkMnemonicHash *
1627 gtk_menu_shell_get_mnemonic_hash (GtkMenuShell *menu_shell,
1628                                   gboolean      create)
1629 {
1630   GtkMenuShellPrivate *private = GTK_MENU_SHELL_GET_PRIVATE (menu_shell);
1631
1632   if (!private->mnemonic_hash && create)
1633     private->mnemonic_hash = _gtk_mnemonic_hash_new ();
1634   
1635   return private->mnemonic_hash;
1636 }
1637
1638 static void
1639 menu_shell_add_mnemonic_foreach (guint    keyval,
1640                                  GSList  *targets,
1641                                  gpointer data)
1642 {
1643   GtkKeyHash *key_hash = data;
1644
1645   _gtk_key_hash_add_entry (key_hash, keyval, 0, GUINT_TO_POINTER (keyval));
1646 }
1647
1648 static GtkKeyHash *
1649 gtk_menu_shell_get_key_hash (GtkMenuShell *menu_shell,
1650                              gboolean      create)
1651 {
1652   GtkMenuShellPrivate *private = GTK_MENU_SHELL_GET_PRIVATE (menu_shell);
1653   GtkWidget *widget = GTK_WIDGET (menu_shell);
1654
1655   if (!private->key_hash && create && gtk_widget_has_screen (widget))
1656     {
1657       GtkMnemonicHash *mnemonic_hash = gtk_menu_shell_get_mnemonic_hash (menu_shell, FALSE);
1658       GdkScreen *screen = gtk_widget_get_screen (widget);
1659       GdkKeymap *keymap = gdk_keymap_get_for_display (gdk_screen_get_display (screen));
1660
1661       if (!mnemonic_hash)
1662         return NULL;
1663       
1664       private->key_hash = _gtk_key_hash_new (keymap, NULL);
1665
1666       _gtk_mnemonic_hash_foreach (mnemonic_hash,
1667                                   menu_shell_add_mnemonic_foreach,
1668                                   private->key_hash);
1669     }
1670   
1671   return private->key_hash;
1672 }
1673
1674 static void
1675 gtk_menu_shell_reset_key_hash (GtkMenuShell *menu_shell)
1676 {
1677   GtkMenuShellPrivate *private = GTK_MENU_SHELL_GET_PRIVATE (menu_shell);
1678
1679   if (private->key_hash)
1680     {
1681       _gtk_key_hash_free (private->key_hash);
1682       private->key_hash = NULL;
1683     }
1684 }
1685
1686 static gboolean
1687 gtk_menu_shell_activate_mnemonic (GtkMenuShell *menu_shell,
1688                                   GdkEventKey  *event)
1689 {
1690   GtkMnemonicHash *mnemonic_hash;
1691   GtkKeyHash *key_hash;
1692   GSList *entries;
1693   gboolean result = FALSE;
1694
1695   mnemonic_hash = gtk_menu_shell_get_mnemonic_hash (menu_shell, FALSE);
1696   if (!mnemonic_hash)
1697     return FALSE;
1698
1699   key_hash = gtk_menu_shell_get_key_hash (menu_shell, TRUE);
1700   if (!key_hash)
1701     return FALSE;
1702   
1703   entries = _gtk_key_hash_lookup (key_hash,
1704                                   event->hardware_keycode,
1705                                   event->state,
1706                                   gtk_accelerator_get_default_mod_mask (),
1707                                   event->group);
1708
1709   if (entries)
1710     result = _gtk_mnemonic_hash_activate (mnemonic_hash,
1711                                           GPOINTER_TO_UINT (entries->data));
1712
1713   return result;
1714 }
1715
1716 void
1717 _gtk_menu_shell_add_mnemonic (GtkMenuShell *menu_shell,
1718                               guint      keyval,
1719                               GtkWidget *target)
1720 {
1721   g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
1722   g_return_if_fail (GTK_IS_WIDGET (target));
1723
1724   _gtk_mnemonic_hash_add (gtk_menu_shell_get_mnemonic_hash (menu_shell, TRUE),
1725                           keyval, target);
1726   gtk_menu_shell_reset_key_hash (menu_shell);
1727 }
1728
1729 void
1730 _gtk_menu_shell_remove_mnemonic (GtkMenuShell *menu_shell,
1731                                  guint      keyval,
1732                                  GtkWidget *target)
1733 {
1734   g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
1735   g_return_if_fail (GTK_IS_WIDGET (target));
1736   
1737   _gtk_mnemonic_hash_remove (gtk_menu_shell_get_mnemonic_hash (menu_shell, TRUE),
1738                              keyval, target);
1739   gtk_menu_shell_reset_key_hash (menu_shell);
1740 }
1741
1742 /**
1743  * gtk_menu_shell_get_take_focus:
1744  * @menu_shell: a #GtkMenuShell
1745  *
1746  * Returns %TRUE if the menu shell will take the keyboard focus on popup.
1747  *
1748  * Returns: %TRUE if the menu shell will take the keyboard focus on popup.
1749  *
1750  * Since: 2.8
1751  **/
1752 gboolean
1753 gtk_menu_shell_get_take_focus (GtkMenuShell *menu_shell)
1754 {
1755   GtkMenuShellPrivate *priv;
1756
1757   g_return_val_if_fail (GTK_IS_MENU_SHELL (menu_shell), FALSE);
1758
1759   priv = GTK_MENU_SHELL_GET_PRIVATE (menu_shell);
1760
1761   return priv->take_focus;
1762 }
1763
1764 /**
1765  * gtk_menu_shell_set_take_focus:
1766  * @menu_shell: a #GtkMenuShell
1767  * @take_focus: %TRUE if the menu shell should take the keyboard focus on popup.
1768  *
1769  * If @take_focus is %TRUE (the default) the menu shell will take the keyboard 
1770  * focus so that it will receive all keyboard events which is needed to enable
1771  * keyboard navigation in menus.
1772  *
1773  * Setting @take_focus to %FALSE is useful only for special applications
1774  * like virtual keyboard implementations which should not take keyboard
1775  * focus.
1776  *
1777  * The @take_focus state of a menu or menu bar is automatically propagated
1778  * to submenus whenever a submenu is popped up, so you don't have to worry
1779  * about recursively setting it for your entire menu hierarchy. Only when
1780  * programmatically picking a submenu and popping it up manually, the
1781  * @take_focus property of the submenu needs to be set explicitely.
1782  *
1783  * Note that setting it to %FALSE has side-effects:
1784  *
1785  * If the focus is in some other app, it keeps the focus and keynav in
1786  * the menu doesn't work. Consequently, keynav on the menu will only
1787  * work if the focus is on some toplevel owned by the onscreen keyboard.
1788  *
1789  * To avoid confusing the user, menus with @take_focus set to %FALSE
1790  * should not display mnemonics or accelerators, since it cannot be
1791  * guaranteed that they will work.
1792  *
1793  * See also gdk_keyboard_grab()
1794  *
1795  * Since: 2.8
1796  **/
1797 void
1798 gtk_menu_shell_set_take_focus (GtkMenuShell *menu_shell,
1799                                gboolean      take_focus)
1800 {
1801   GtkMenuShellPrivate *priv;
1802
1803   g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
1804
1805   priv = GTK_MENU_SHELL_GET_PRIVATE (menu_shell);
1806
1807   if (priv->take_focus != take_focus)
1808     {
1809       priv->take_focus = take_focus;
1810       g_object_notify (G_OBJECT (menu_shell), "take-focus");
1811     }
1812 }
1813
1814 #define __GTK_MENU_SHELL_C__
1815 #include "gtkaliasdef.c"