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