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