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