]> Pileus Git - ~andy/gtk/blob - gtk/gtkmenushell.c
Fix some implicit declaration warnings
[~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 "gtkmain.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 "gtkwindow.h"
53 #include "gtkprivate.h"
54 #include "gtkintl.h"
55 #include "gtktypebuiltins.h"
56
57 #include "deprecated/gtktearoffmenuitem.h"
58
59 #include "a11y/gtkmenushellaccessible.h"
60
61
62 #define MENU_SHELL_TIMEOUT   500
63
64 #define PACK_DIRECTION(m)                                 \
65    (GTK_IS_MENU_BAR (m)                                   \
66      ? gtk_menu_bar_get_pack_direction (GTK_MENU_BAR (m)) \
67      : GTK_PACK_DIRECTION_LTR)
68
69 enum {
70   DEACTIVATE,
71   SELECTION_DONE,
72   MOVE_CURRENT,
73   ACTIVATE_CURRENT,
74   CANCEL,
75   CYCLE_FOCUS,
76   MOVE_SELECTED,
77   INSERT,
78   LAST_SIGNAL
79 };
80
81 enum {
82   PROP_0,
83   PROP_TAKE_FOCUS
84 };
85
86 /* Terminology:
87  * 
88  * A menu item can be "selected", this means that it is displayed
89  * in the prelight state, and if it has a submenu, that submenu
90  * will be popped up. 
91  * 
92  * A menu is "active" when it is visible onscreen and the user
93  * is selecting from it. A menubar is not active until the user
94  * clicks on one of its menuitems. When a menu is active,
95  * passing the mouse over a submenu will pop it up.
96  *
97  * menu_shell->active_menu_item, is however, not an "active"
98  * menu item (there is no such thing) but rather, the selected
99  * menu item in that MenuShell, if there is one.
100  *
101  * There is also is a concept of the current menu and a current
102  * menu item. The current menu item is the selected menu item
103  * that is furthest down in the hierarchy. (Every active menu_shell
104  * does not necessarily contain a selected menu item, but if
105  * it does, then menu_shell->parent_menu_shell must also contain
106  * a selected menu item. The current menu is the menu that 
107  * contains the current menu_item. It will always have a GTK
108  * grab and receive all key presses.
109  *
110  *
111  * Action signals:
112  *
113  *  ::move_current (GtkMenuDirection *dir)
114  *     Moves the current menu item in direction 'dir':
115  *
116  *       GTK_MENU_DIR_PARENT: To the parent menu shell
117  *       GTK_MENU_DIR_CHILD: To the child menu shell (if this item has
118  *          a submenu.
119  *       GTK_MENU_DIR_NEXT/PREV: To the next or previous item
120  *          in this menu.
121  * 
122  *     As a a bit of a hack to get movement between menus and
123  *     menubars working, if submenu_placement is different for
124  *     the menu and its MenuShell then the following apply:
125  * 
126  *       - For 'parent' the current menu is not just moved to
127  *         the parent, but moved to the previous entry in the parent
128  *       - For 'child', if there is no child, then current is
129  *         moved to the next item in the parent.
130  *
131  *    Note that the above explanation of ::move_current was written
132  *    before menus and menubars had support for RTL flipping and
133  *    different packing directions, and therefore only applies for
134  *    when text direction and packing direction are both left-to-right.
135  * 
136  *  ::activate_current (GBoolean *force_hide)
137  *     Activate the current item. If 'force_hide' is true, hide
138  *     the current menu item always. Otherwise, only hide
139  *     it if menu_item->klass->hide_on_activate is true.
140  *
141  *  ::cancel ()
142  *     Cancels the current selection
143  */
144
145
146 static void gtk_menu_shell_set_property      (GObject           *object,
147                                               guint              prop_id,
148                                               const GValue      *value,
149                                               GParamSpec        *pspec);
150 static void gtk_menu_shell_get_property      (GObject           *object,
151                                               guint              prop_id,
152                                               GValue            *value,
153                                               GParamSpec        *pspec);
154 static void gtk_menu_shell_realize           (GtkWidget         *widget);
155 static void gtk_menu_shell_finalize          (GObject           *object);
156 static void gtk_menu_shell_dispose           (GObject           *object);
157 static gint gtk_menu_shell_button_press      (GtkWidget         *widget,
158                                               GdkEventButton    *event);
159 static gint gtk_menu_shell_button_release    (GtkWidget         *widget,
160                                               GdkEventButton    *event);
161 static gint gtk_menu_shell_key_press         (GtkWidget         *widget,
162                                               GdkEventKey       *event);
163 static gint gtk_menu_shell_enter_notify      (GtkWidget         *widget,
164                                               GdkEventCrossing  *event);
165 static gint gtk_menu_shell_leave_notify      (GtkWidget         *widget,
166                                               GdkEventCrossing  *event);
167 static void gtk_menu_shell_screen_changed    (GtkWidget         *widget,
168                                               GdkScreen         *previous_screen);
169 static gboolean gtk_menu_shell_grab_broken       (GtkWidget         *widget,
170                                               GdkEventGrabBroken *event);
171 static void gtk_menu_shell_add               (GtkContainer      *container,
172                                               GtkWidget         *widget);
173 static void gtk_menu_shell_remove            (GtkContainer      *container,
174                                               GtkWidget         *widget);
175 static void gtk_menu_shell_forall            (GtkContainer      *container,
176                                               gboolean           include_internals,
177                                               GtkCallback        callback,
178                                               gpointer           callback_data);
179 static void gtk_menu_shell_real_insert       (GtkMenuShell *menu_shell,
180                                               GtkWidget    *child,
181                                               gint          position);
182 static void gtk_real_menu_shell_deactivate   (GtkMenuShell      *menu_shell);
183 static gint gtk_menu_shell_is_item           (GtkMenuShell      *menu_shell,
184                                               GtkWidget         *child);
185 static GtkWidget *gtk_menu_shell_get_item    (GtkMenuShell      *menu_shell,
186                                               GdkEvent          *event);
187 static GType    gtk_menu_shell_child_type  (GtkContainer      *container);
188 static void gtk_menu_shell_real_select_item  (GtkMenuShell      *menu_shell,
189                                               GtkWidget         *menu_item);
190 static gboolean gtk_menu_shell_select_submenu_first (GtkMenuShell   *menu_shell); 
191
192 static void gtk_real_menu_shell_move_current (GtkMenuShell      *menu_shell,
193                                               GtkMenuDirectionType direction);
194 static void gtk_real_menu_shell_activate_current (GtkMenuShell      *menu_shell,
195                                                   gboolean           force_hide);
196 static void gtk_real_menu_shell_cancel           (GtkMenuShell      *menu_shell);
197 static void gtk_real_menu_shell_cycle_focus      (GtkMenuShell      *menu_shell,
198                                                   GtkDirectionType   dir);
199
200 static void     gtk_menu_shell_reset_key_hash    (GtkMenuShell *menu_shell);
201 static gboolean gtk_menu_shell_activate_mnemonic (GtkMenuShell *menu_shell,
202                                                   GdkEventKey  *event);
203 static gboolean gtk_menu_shell_real_move_selected (GtkMenuShell  *menu_shell, 
204                                                    gint           distance);
205
206 static guint menu_shell_signals[LAST_SIGNAL] = { 0 };
207
208 G_DEFINE_ABSTRACT_TYPE (GtkMenuShell, gtk_menu_shell, GTK_TYPE_CONTAINER)
209
210 static void
211 gtk_menu_shell_class_init (GtkMenuShellClass *klass)
212 {
213   GObjectClass *object_class;
214   GtkWidgetClass *widget_class;
215   GtkContainerClass *container_class;
216
217   GtkBindingSet *binding_set;
218
219   object_class = (GObjectClass*) klass;
220   widget_class = (GtkWidgetClass*) klass;
221   container_class = (GtkContainerClass*) klass;
222
223   object_class->set_property = gtk_menu_shell_set_property;
224   object_class->get_property = gtk_menu_shell_get_property;
225   object_class->finalize = gtk_menu_shell_finalize;
226   object_class->dispose = gtk_menu_shell_dispose;
227
228   widget_class->realize = gtk_menu_shell_realize;
229   widget_class->button_press_event = gtk_menu_shell_button_press;
230   widget_class->button_release_event = gtk_menu_shell_button_release;
231   widget_class->grab_broken_event = gtk_menu_shell_grab_broken;
232   widget_class->key_press_event = gtk_menu_shell_key_press;
233   widget_class->enter_notify_event = gtk_menu_shell_enter_notify;
234   widget_class->leave_notify_event = gtk_menu_shell_leave_notify;
235   widget_class->screen_changed = gtk_menu_shell_screen_changed;
236
237   container_class->add = gtk_menu_shell_add;
238   container_class->remove = gtk_menu_shell_remove;
239   container_class->forall = gtk_menu_shell_forall;
240   container_class->child_type = gtk_menu_shell_child_type;
241
242   klass->submenu_placement = GTK_TOP_BOTTOM;
243   klass->deactivate = gtk_real_menu_shell_deactivate;
244   klass->selection_done = NULL;
245   klass->move_current = gtk_real_menu_shell_move_current;
246   klass->activate_current = gtk_real_menu_shell_activate_current;
247   klass->cancel = gtk_real_menu_shell_cancel;
248   klass->select_item = gtk_menu_shell_real_select_item;
249   klass->insert = gtk_menu_shell_real_insert;
250   klass->move_selected = gtk_menu_shell_real_move_selected;
251
252   /**
253    * GtkMenuShell::deactivate:
254    * @menushell: the object which received the signal
255    *
256    * This signal is emitted when a menu shell is deactivated.
257    */
258   menu_shell_signals[DEACTIVATE] =
259     g_signal_new (I_("deactivate"),
260                   G_OBJECT_CLASS_TYPE (object_class),
261                   G_SIGNAL_RUN_FIRST,
262                   G_STRUCT_OFFSET (GtkMenuShellClass, deactivate),
263                   NULL, NULL,
264                   _gtk_marshal_VOID__VOID,
265                   G_TYPE_NONE, 0);
266
267   /**
268    * GtkMenuShell::selection-done:
269    * @menushell: the object which received the signal
270    *
271    * This signal is emitted when a selection has been
272    * completed within a menu shell.
273    */
274   menu_shell_signals[SELECTION_DONE] =
275     g_signal_new (I_("selection-done"),
276                   G_OBJECT_CLASS_TYPE (object_class),
277                   G_SIGNAL_RUN_FIRST,
278                   G_STRUCT_OFFSET (GtkMenuShellClass, selection_done),
279                   NULL, NULL,
280                   _gtk_marshal_VOID__VOID,
281                   G_TYPE_NONE, 0);
282
283   /**
284    * GtkMenuShell::move-current:
285    * @menushell: the object which received the signal
286    * @direction: the direction to move
287    *
288    * An keybinding signal which moves the current menu item
289    * in the direction specified by @direction.
290    */
291   menu_shell_signals[MOVE_CURRENT] =
292     g_signal_new (I_("move-current"),
293                   G_OBJECT_CLASS_TYPE (object_class),
294                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
295                   G_STRUCT_OFFSET (GtkMenuShellClass, move_current),
296                   NULL, NULL,
297                   _gtk_marshal_VOID__ENUM,
298                   G_TYPE_NONE, 1,
299                   GTK_TYPE_MENU_DIRECTION_TYPE);
300
301   /**
302    * GtkMenuShell::activate-current:
303    * @menushell: the object which received the signal
304    * @force_hide: if %TRUE, hide the menu after activating the menu item
305    *
306    * An action signal that activates the current menu item within
307    * the menu shell.
308    */
309   menu_shell_signals[ACTIVATE_CURRENT] =
310     g_signal_new (I_("activate-current"),
311                   G_OBJECT_CLASS_TYPE (object_class),
312                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
313                   G_STRUCT_OFFSET (GtkMenuShellClass, activate_current),
314                   NULL, NULL,
315                   _gtk_marshal_VOID__BOOLEAN,
316                   G_TYPE_NONE, 1,
317                   G_TYPE_BOOLEAN);
318
319   /**
320    * GtkMenuShell::cancel:
321    * @menushell: the object which received the signal
322    *
323    * An action signal which cancels the selection within the menu shell.
324    * Causes the #GtkMenuShell::selection-done signal to be emitted.
325    */
326   menu_shell_signals[CANCEL] =
327     g_signal_new (I_("cancel"),
328                   G_OBJECT_CLASS_TYPE (object_class),
329                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
330                   G_STRUCT_OFFSET (GtkMenuShellClass, cancel),
331                   NULL, NULL,
332                   _gtk_marshal_VOID__VOID,
333                   G_TYPE_NONE, 0);
334
335   /**
336    * GtkMenuShell::cycle-focus:
337    * @menushell: the object which received the signal
338    * @direction: the direction to cycle in
339    *
340    * A keybinding signal which moves the focus in the
341    * given @direction.
342    */
343   menu_shell_signals[CYCLE_FOCUS] =
344     g_signal_new_class_handler (I_("cycle-focus"),
345                                 G_OBJECT_CLASS_TYPE (object_class),
346                                 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
347                                 G_CALLBACK (gtk_real_menu_shell_cycle_focus),
348                                 NULL, NULL,
349                                 _gtk_marshal_VOID__ENUM,
350                                 G_TYPE_NONE, 1,
351                                 GTK_TYPE_DIRECTION_TYPE);
352
353   /**
354    * GtkMenuShell::move-selected:
355    * @menu_shell: the object on which the signal is emitted
356    * @distance: +1 to move to the next item, -1 to move to the previous
357    *
358    * The ::move-selected signal is emitted to move the selection to
359    * another item.
360    *
361    * Returns: %TRUE to stop the signal emission, %FALSE to continue
362    *
363    * Since: 2.12
364    */
365   menu_shell_signals[MOVE_SELECTED] =
366     g_signal_new (I_("move-selected"),
367                   G_OBJECT_CLASS_TYPE (object_class),
368                   G_SIGNAL_RUN_LAST,
369                   G_STRUCT_OFFSET (GtkMenuShellClass, move_selected),
370                   _gtk_boolean_handled_accumulator, NULL,
371                   _gtk_marshal_BOOLEAN__INT,
372                   G_TYPE_BOOLEAN, 1,
373                   G_TYPE_INT);
374
375   /**
376    * GtkMenuShell::insert:
377    * @menu_shell: the object on which the signal is emitted
378    * @child: the #GtkMenuItem that is being inserted
379    * @position: the position at which the insert occurs
380    *
381    * The ::insert signal is emitted when a new #GtkMenuItem is added to
382    * a #GtkMenuShell.  A separate signal is used instead of
383    * GtkContainer::add because of the need for an additional position
384    * parameter.
385    *
386    * The inverse of this signal is the GtkContainer::removed signal.
387    *
388    * Since: 3.2
389    **/
390   menu_shell_signals[INSERT] =
391     g_signal_new (I_("insert"),
392                   G_OBJECT_CLASS_TYPE (object_class),
393                   G_SIGNAL_RUN_FIRST,
394                   G_STRUCT_OFFSET (GtkMenuShellClass, insert),
395                   NULL, NULL,
396                   _gtk_marshal_VOID__OBJECT_INT,
397                   G_TYPE_NONE, 2, GTK_TYPE_WIDGET, G_TYPE_INT);
398
399
400   binding_set = gtk_binding_set_by_class (klass);
401   gtk_binding_entry_add_signal (binding_set,
402                                 GDK_KEY_Escape, 0,
403                                 "cancel", 0);
404   gtk_binding_entry_add_signal (binding_set,
405                                 GDK_KEY_Return, 0,
406                                 "activate-current", 1,
407                                 G_TYPE_BOOLEAN,
408                                 TRUE);
409   gtk_binding_entry_add_signal (binding_set,
410                                 GDK_KEY_ISO_Enter, 0,
411                                 "activate-current", 1,
412                                 G_TYPE_BOOLEAN,
413                                 TRUE);
414   gtk_binding_entry_add_signal (binding_set,
415                                 GDK_KEY_KP_Enter, 0,
416                                 "activate-current", 1,
417                                 G_TYPE_BOOLEAN,
418                                 TRUE);
419   gtk_binding_entry_add_signal (binding_set,
420                                 GDK_KEY_space, 0,
421                                 "activate-current", 1,
422                                 G_TYPE_BOOLEAN,
423                                 FALSE);
424   gtk_binding_entry_add_signal (binding_set,
425                                 GDK_KEY_KP_Space, 0,
426                                 "activate-current", 1,
427                                 G_TYPE_BOOLEAN,
428                                 FALSE);
429   gtk_binding_entry_add_signal (binding_set,
430                                 GDK_KEY_F10, 0,
431                                 "cycle-focus", 1,
432                                 GTK_TYPE_DIRECTION_TYPE, GTK_DIR_TAB_FORWARD);
433   gtk_binding_entry_add_signal (binding_set,
434                                 GDK_KEY_F10, GDK_SHIFT_MASK,
435                                 "cycle-focus", 1,
436                                 GTK_TYPE_DIRECTION_TYPE, GTK_DIR_TAB_BACKWARD);
437
438   /**
439    * GtkMenuShell:take-focus:
440    *
441    * A boolean that determines whether the menu and its submenus grab the
442    * keyboard focus. See gtk_menu_shell_set_take_focus() and
443    * gtk_menu_shell_get_take_focus().
444    *
445    * Since: 2.8
446    **/
447   g_object_class_install_property (object_class,
448                                    PROP_TAKE_FOCUS,
449                                    g_param_spec_boolean ("take-focus",
450                                                          P_("Take Focus"),
451                                                          P_("A boolean that determines whether the menu grabs the keyboard focus"),
452                                                          TRUE,
453                                                          GTK_PARAM_READWRITE));
454
455   gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_MENU_SHELL_ACCESSIBLE);
456
457   g_type_class_add_private (object_class, sizeof (GtkMenuShellPrivate));
458 }
459
460 static GType
461 gtk_menu_shell_child_type (GtkContainer *container)
462 {
463   return GTK_TYPE_MENU_ITEM;
464 }
465
466 static void
467 gtk_menu_shell_init (GtkMenuShell *menu_shell)
468 {
469   GtkMenuShellPrivate *priv;
470
471   priv = G_TYPE_INSTANCE_GET_PRIVATE (menu_shell,
472                                       GTK_TYPE_MENU_SHELL,
473                                       GtkMenuShellPrivate);
474   menu_shell->priv = priv;
475   priv->take_focus = TRUE;
476 }
477
478 static void
479 gtk_menu_shell_set_property (GObject      *object,
480                              guint         prop_id,
481                              const GValue *value,
482                              GParamSpec   *pspec)
483 {
484   GtkMenuShell *menu_shell = GTK_MENU_SHELL (object);
485
486   switch (prop_id)
487     {
488     case PROP_TAKE_FOCUS:
489       gtk_menu_shell_set_take_focus (menu_shell, g_value_get_boolean (value));
490       break;
491     default:
492       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
493       break;
494     }
495 }
496
497 static void
498 gtk_menu_shell_get_property (GObject    *object,
499                              guint       prop_id,
500                              GValue     *value,
501                              GParamSpec *pspec)
502 {
503   GtkMenuShell *menu_shell = GTK_MENU_SHELL (object);
504
505   switch (prop_id)
506     {
507     case PROP_TAKE_FOCUS:
508       g_value_set_boolean (value, gtk_menu_shell_get_take_focus (menu_shell));
509       break;
510     default:
511       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
512       break;
513     }
514 }
515
516 static void
517 gtk_menu_shell_finalize (GObject *object)
518 {
519   GtkMenuShell *menu_shell = GTK_MENU_SHELL (object);
520   GtkMenuShellPrivate *priv = menu_shell->priv;
521
522   if (priv->mnemonic_hash)
523     _gtk_mnemonic_hash_free (priv->mnemonic_hash);
524   if (priv->key_hash)
525     _gtk_key_hash_free (priv->key_hash);
526
527   G_OBJECT_CLASS (gtk_menu_shell_parent_class)->finalize (object);
528 }
529
530
531 static void
532 gtk_menu_shell_dispose (GObject *object)
533 {
534   gtk_menu_shell_deactivate (GTK_MENU_SHELL (object));
535
536   G_OBJECT_CLASS (gtk_menu_shell_parent_class)->dispose (object);
537 }
538
539 /**
540  * gtk_menu_shell_append:
541  * @menu_shell: a #GtkMenuShell
542  * @child: The #GtkMenuItem to add
543  *
544  * Adds a new #GtkMenuItem to the end of the menu shell's
545  * item list.
546  */
547 void
548 gtk_menu_shell_append (GtkMenuShell *menu_shell,
549                        GtkWidget    *child)
550 {
551   gtk_menu_shell_insert (menu_shell, child, -1);
552 }
553
554 /**
555  * gtk_menu_shell_prepend:
556  * @menu_shell: a #GtkMenuShell
557  * @child: The #GtkMenuItem to add
558  *
559  * Adds a new #GtkMenuItem to the beginning of the menu shell's
560  * item list.
561  */
562 void
563 gtk_menu_shell_prepend (GtkMenuShell *menu_shell,
564                         GtkWidget    *child)
565 {
566   gtk_menu_shell_insert (menu_shell, child, 0);
567 }
568
569 /**
570  * gtk_menu_shell_insert:
571  * @menu_shell: a #GtkMenuShell
572  * @child: The #GtkMenuItem to add
573  * @position: The position in the item list where @child
574  *     is added. Positions are numbered from 0 to n-1
575  *
576  * Adds a new #GtkMenuItem to the menu shell's item list
577  * at the position indicated by @position.
578  */
579 void
580 gtk_menu_shell_insert (GtkMenuShell *menu_shell,
581                        GtkWidget    *child,
582                        gint          position)
583 {
584   g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
585   g_return_if_fail (GTK_IS_MENU_ITEM (child));
586
587   g_signal_emit (menu_shell, menu_shell_signals[INSERT], 0, child, position);
588 }
589
590 static void
591 gtk_menu_shell_real_insert (GtkMenuShell *menu_shell,
592                             GtkWidget    *child,
593                             gint          position)
594 {
595   GtkMenuShellPrivate *priv = menu_shell->priv;
596
597   priv->children = g_list_insert (priv->children, child, position);
598
599   gtk_widget_set_parent (child, GTK_WIDGET (menu_shell));
600 }
601
602 /**
603  * gtk_menu_shell_deactivate:
604  * @menu_shell: a #GtkMenuShell
605  *
606  * Deactivates the menu shell.
607  *
608  * Typically this results in the menu shell being erased
609  * from the screen.
610  */
611 void
612 gtk_menu_shell_deactivate (GtkMenuShell *menu_shell)
613 {
614   g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
615
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 }