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