]> Pileus Git - ~andy/gtk/blob - gtk/gtkmenushell.c
stylecontext: Do invalidation on first resize container
[~andy/gtk] / gtk / gtkmenushell.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library. If not, see <http://www.gnu.org/licenses/>.
16  */
17
18 /*
19  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
20  * file for a list of people on the GTK+ Team.  See the ChangeLog
21  * files for a list of changes.  These files are distributed with
22  * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
23  */
24
25 /**
26  * SECTION:gtkmenushell
27  * @Title: GtkMenuShell
28  * @Short_description: A base class for menu objects
29  *
30  * A #GtkMenuShell is the abstract base class used to derive the
31  * #GtkMenu and #GtkMenuBar subclasses.
32  *
33  * A #GtkMenuShell is a container of #GtkMenuItem objects arranged
34  * in a list which can be navigated, selected, and activated by the
35  * user to perform application functions. A #GtkMenuItem can have a
36  * submenu associated with it, allowing for nested hierarchical menus.
37  */
38 #include "config.h"
39
40 #include "gtkbindings.h"
41 #include "gtkkeyhash.h"
42 #include "gtklabel.h"
43 #include "gtkmain.h"
44 #include "gtkmarshalers.h"
45 #include "gtkmenubar.h"
46 #include "gtkmenuitemprivate.h"
47 #include "gtkmenushellprivate.h"
48 #include "gtkmenuprivate.h"
49 #include "gtkmnemonichash.h"
50 #include "gtkwindow.h"
51 #include "gtkprivate.h"
52 #include "gtkmain.h"
53 #include "gtkintl.h"
54 #include "gtktypebuiltins.h"
55
56 #include "deprecated/gtktearoffmenuitem.h"
57
58 #include "a11y/gtkmenushellaccessible.h"
59
60
61 #define MENU_SHELL_TIMEOUT   500
62
63 #define PACK_DIRECTION(m)                                 \
64    (GTK_IS_MENU_BAR (m)                                   \
65      ? gtk_menu_bar_get_pack_direction (GTK_MENU_BAR (m)) \
66      : GTK_PACK_DIRECTION_LTR)
67
68 enum {
69   DEACTIVATE,
70   SELECTION_DONE,
71   MOVE_CURRENT,
72   ACTIVATE_CURRENT,
73   CANCEL,
74   CYCLE_FOCUS,
75   MOVE_SELECTED,
76   INSERT,
77   LAST_SIGNAL
78 };
79
80 enum {
81   PROP_0,
82   PROP_TAKE_FOCUS
83 };
84
85 /* Terminology:
86  * 
87  * A menu item can be "selected", this means that it is displayed
88  * in the prelight state, and if it has a submenu, that submenu
89  * will be popped up. 
90  * 
91  * A menu is "active" when it is visible onscreen and the user
92  * is selecting from it. A menubar is not active until the user
93  * clicks on one of its menuitems. When a menu is active,
94  * passing the mouse over a submenu will pop it up.
95  *
96  * menu_shell->active_menu_item, is however, not an "active"
97  * menu item (there is no such thing) but rather, the selected
98  * menu item in that MenuShell, if there is one.
99  *
100  * There is also is a concept of the current menu and a current
101  * menu item. The current menu item is the selected menu item
102  * that is furthest down in the hierarchy. (Every active menu_shell
103  * does not necessarily contain a selected menu item, but if
104  * it does, then menu_shell->parent_menu_shell must also contain
105  * a selected menu item. The current menu is the menu that 
106  * contains the current menu_item. It will always have a GTK
107  * grab and receive all key presses.
108  *
109  *
110  * Action signals:
111  *
112  *  ::move_current (GtkMenuDirection *dir)
113  *     Moves the current menu item in direction 'dir':
114  *
115  *       GTK_MENU_DIR_PARENT: To the parent menu shell
116  *       GTK_MENU_DIR_CHILD: To the child menu shell (if this item has
117  *          a submenu.
118  *       GTK_MENU_DIR_NEXT/PREV: To the next or previous item
119  *          in this menu.
120  * 
121  *     As a a bit of a hack to get movement between menus and
122  *     menubars working, if submenu_placement is different for
123  *     the menu and its MenuShell then the following apply:
124  * 
125  *       - For 'parent' the current menu is not just moved to
126  *         the parent, but moved to the previous entry in the parent
127  *       - For 'child', if there is no child, then current is
128  *         moved to the next item in the parent.
129  *
130  *    Note that the above explanation of ::move_current was written
131  *    before menus and menubars had support for RTL flipping and
132  *    different packing directions, and therefore only applies for
133  *    when text direction and packing direction are both left-to-right.
134  * 
135  *  ::activate_current (GBoolean *force_hide)
136  *     Activate the current item. If 'force_hide' is true, hide
137  *     the current menu item always. Otherwise, only hide
138  *     it if menu_item->klass->hide_on_activate is true.
139  *
140  *  ::cancel ()
141  *     Cancels the current selection
142  */
143
144
145 static void gtk_menu_shell_set_property      (GObject           *object,
146                                               guint              prop_id,
147                                               const GValue      *value,
148                                               GParamSpec        *pspec);
149 static void gtk_menu_shell_get_property      (GObject           *object,
150                                               guint              prop_id,
151                                               GValue            *value,
152                                               GParamSpec        *pspec);
153 static void gtk_menu_shell_realize           (GtkWidget         *widget);
154 static void gtk_menu_shell_finalize          (GObject           *object);
155 static void gtk_menu_shell_dispose           (GObject           *object);
156 static gint gtk_menu_shell_button_press      (GtkWidget         *widget,
157                                               GdkEventButton    *event);
158 static gint gtk_menu_shell_button_release    (GtkWidget         *widget,
159                                               GdkEventButton    *event);
160 static gint gtk_menu_shell_key_press         (GtkWidget         *widget,
161                                               GdkEventKey       *event);
162 static gint gtk_menu_shell_enter_notify      (GtkWidget         *widget,
163                                               GdkEventCrossing  *event);
164 static gint gtk_menu_shell_leave_notify      (GtkWidget         *widget,
165                                               GdkEventCrossing  *event);
166 static void gtk_menu_shell_screen_changed    (GtkWidget         *widget,
167                                               GdkScreen         *previous_screen);
168 static gboolean gtk_menu_shell_grab_broken       (GtkWidget         *widget,
169                                               GdkEventGrabBroken *event);
170 static void gtk_menu_shell_add               (GtkContainer      *container,
171                                               GtkWidget         *widget);
172 static void gtk_menu_shell_remove            (GtkContainer      *container,
173                                               GtkWidget         *widget);
174 static void gtk_menu_shell_forall            (GtkContainer      *container,
175                                               gboolean           include_internals,
176                                               GtkCallback        callback,
177                                               gpointer           callback_data);
178 static void gtk_menu_shell_real_insert       (GtkMenuShell *menu_shell,
179                                               GtkWidget    *child,
180                                               gint          position);
181 static void gtk_real_menu_shell_deactivate   (GtkMenuShell      *menu_shell);
182 static gint gtk_menu_shell_is_item           (GtkMenuShell      *menu_shell,
183                                               GtkWidget         *child);
184 static GtkWidget *gtk_menu_shell_get_item    (GtkMenuShell      *menu_shell,
185                                               GdkEvent          *event);
186 static GType    gtk_menu_shell_child_type  (GtkContainer      *container);
187 static void gtk_menu_shell_real_select_item  (GtkMenuShell      *menu_shell,
188                                               GtkWidget         *menu_item);
189 static gboolean gtk_menu_shell_select_submenu_first (GtkMenuShell   *menu_shell); 
190
191 static void gtk_real_menu_shell_move_current (GtkMenuShell      *menu_shell,
192                                               GtkMenuDirectionType direction);
193 static void gtk_real_menu_shell_activate_current (GtkMenuShell      *menu_shell,
194                                                   gboolean           force_hide);
195 static void gtk_real_menu_shell_cancel           (GtkMenuShell      *menu_shell);
196 static void gtk_real_menu_shell_cycle_focus      (GtkMenuShell      *menu_shell,
197                                                   GtkDirectionType   dir);
198
199 static void     gtk_menu_shell_reset_key_hash    (GtkMenuShell *menu_shell);
200 static gboolean gtk_menu_shell_activate_mnemonic (GtkMenuShell *menu_shell,
201                                                   GdkEventKey  *event);
202 static gboolean gtk_menu_shell_real_move_selected (GtkMenuShell  *menu_shell, 
203                                                    gint           distance);
204
205 static guint menu_shell_signals[LAST_SIGNAL] = { 0 };
206
207 G_DEFINE_ABSTRACT_TYPE (GtkMenuShell, gtk_menu_shell, GTK_TYPE_CONTAINER)
208
209 static void
210 gtk_menu_shell_class_init (GtkMenuShellClass *klass)
211 {
212   GObjectClass *object_class;
213   GtkWidgetClass *widget_class;
214   GtkContainerClass *container_class;
215
216   GtkBindingSet *binding_set;
217
218   object_class = (GObjectClass*) klass;
219   widget_class = (GtkWidgetClass*) klass;
220   container_class = (GtkContainerClass*) klass;
221
222   object_class->set_property = gtk_menu_shell_set_property;
223   object_class->get_property = gtk_menu_shell_get_property;
224   object_class->finalize = gtk_menu_shell_finalize;
225   object_class->dispose = gtk_menu_shell_dispose;
226
227   widget_class->realize = gtk_menu_shell_realize;
228   widget_class->button_press_event = gtk_menu_shell_button_press;
229   widget_class->button_release_event = gtk_menu_shell_button_release;
230   widget_class->grab_broken_event = gtk_menu_shell_grab_broken;
231   widget_class->key_press_event = gtk_menu_shell_key_press;
232   widget_class->enter_notify_event = gtk_menu_shell_enter_notify;
233   widget_class->leave_notify_event = gtk_menu_shell_leave_notify;
234   widget_class->screen_changed = gtk_menu_shell_screen_changed;
235
236   container_class->add = gtk_menu_shell_add;
237   container_class->remove = gtk_menu_shell_remove;
238   container_class->forall = gtk_menu_shell_forall;
239   container_class->child_type = gtk_menu_shell_child_type;
240
241   klass->submenu_placement = GTK_TOP_BOTTOM;
242   klass->deactivate = gtk_real_menu_shell_deactivate;
243   klass->selection_done = NULL;
244   klass->move_current = gtk_real_menu_shell_move_current;
245   klass->activate_current = gtk_real_menu_shell_activate_current;
246   klass->cancel = gtk_real_menu_shell_cancel;
247   klass->select_item = gtk_menu_shell_real_select_item;
248   klass->insert = gtk_menu_shell_real_insert;
249   klass->move_selected = gtk_menu_shell_real_move_selected;
250
251   /**
252    * GtkMenuShell::deactivate:
253    * @menushell: the object which received the signal
254    *
255    * This signal is emitted when a menu shell is deactivated.
256    */
257   menu_shell_signals[DEACTIVATE] =
258     g_signal_new (I_("deactivate"),
259                   G_OBJECT_CLASS_TYPE (object_class),
260                   G_SIGNAL_RUN_FIRST,
261                   G_STRUCT_OFFSET (GtkMenuShellClass, deactivate),
262                   NULL, NULL,
263                   _gtk_marshal_VOID__VOID,
264                   G_TYPE_NONE, 0);
265
266   /**
267    * GtkMenuShell::selection-done:
268    * @menushell: the object which received the signal
269    *
270    * This signal is emitted when a selection has been
271    * completed within a menu shell.
272    */
273   menu_shell_signals[SELECTION_DONE] =
274     g_signal_new (I_("selection-done"),
275                   G_OBJECT_CLASS_TYPE (object_class),
276                   G_SIGNAL_RUN_FIRST,
277                   G_STRUCT_OFFSET (GtkMenuShellClass, selection_done),
278                   NULL, NULL,
279                   _gtk_marshal_VOID__VOID,
280                   G_TYPE_NONE, 0);
281
282   /**
283    * GtkMenuShell::move-current:
284    * @menushell: the object which received the signal
285    * @direction: the direction to move
286    *
287    * An keybinding signal which moves the current menu item
288    * in the direction specified by @direction.
289    */
290   menu_shell_signals[MOVE_CURRENT] =
291     g_signal_new (I_("move-current"),
292                   G_OBJECT_CLASS_TYPE (object_class),
293                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
294                   G_STRUCT_OFFSET (GtkMenuShellClass, move_current),
295                   NULL, NULL,
296                   _gtk_marshal_VOID__ENUM,
297                   G_TYPE_NONE, 1,
298                   GTK_TYPE_MENU_DIRECTION_TYPE);
299
300   /**
301    * GtkMenuShell::activate-current:
302    * @menushell: the object which received the signal
303    * @force_hide: if %TRUE, hide the menu after activating the menu item
304    *
305    * An action signal that activates the current menu item within
306    * the menu shell.
307    */
308   menu_shell_signals[ACTIVATE_CURRENT] =
309     g_signal_new (I_("activate-current"),
310                   G_OBJECT_CLASS_TYPE (object_class),
311                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
312                   G_STRUCT_OFFSET (GtkMenuShellClass, activate_current),
313                   NULL, NULL,
314                   _gtk_marshal_VOID__BOOLEAN,
315                   G_TYPE_NONE, 1,
316                   G_TYPE_BOOLEAN);
317
318   /**
319    * GtkMenuShell::cancel:
320    * @menushell: the object which received the signal
321    *
322    * An action signal which cancels the selection within the menu shell.
323    * Causes the #GtkMenuShell::selection-done signal to be emitted.
324    */
325   menu_shell_signals[CANCEL] =
326     g_signal_new (I_("cancel"),
327                   G_OBJECT_CLASS_TYPE (object_class),
328                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
329                   G_STRUCT_OFFSET (GtkMenuShellClass, cancel),
330                   NULL, NULL,
331                   _gtk_marshal_VOID__VOID,
332                   G_TYPE_NONE, 0);
333
334   /**
335    * GtkMenuShell::cycle-focus:
336    * @menushell: the object which received the signal
337    * @direction: the direction to cycle in
338    *
339    * A keybinding signal which moves the focus in the
340    * given @direction.
341    */
342   menu_shell_signals[CYCLE_FOCUS] =
343     g_signal_new_class_handler (I_("cycle-focus"),
344                                 G_OBJECT_CLASS_TYPE (object_class),
345                                 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
346                                 G_CALLBACK (gtk_real_menu_shell_cycle_focus),
347                                 NULL, NULL,
348                                 _gtk_marshal_VOID__ENUM,
349                                 G_TYPE_NONE, 1,
350                                 GTK_TYPE_DIRECTION_TYPE);
351
352   /**
353    * GtkMenuShell::move-selected:
354    * @menu_shell: the object on which the signal is emitted
355    * @distance: +1 to move to the next item, -1 to move to the previous
356    *
357    * The ::move-selected signal is emitted to move the selection to
358    * another item.
359    *
360    * Returns: %TRUE to stop the signal emission, %FALSE to continue
361    *
362    * Since: 2.12
363    */
364   menu_shell_signals[MOVE_SELECTED] =
365     g_signal_new (I_("move-selected"),
366                   G_OBJECT_CLASS_TYPE (object_class),
367                   G_SIGNAL_RUN_LAST,
368                   G_STRUCT_OFFSET (GtkMenuShellClass, move_selected),
369                   _gtk_boolean_handled_accumulator, NULL,
370                   _gtk_marshal_BOOLEAN__INT,
371                   G_TYPE_BOOLEAN, 1,
372                   G_TYPE_INT);
373
374   /**
375    * GtkMenuShell::insert:
376    * @menu_shell: the object on which the signal is emitted
377    * @child: the #GtkMenuItem that is being inserted
378    * @position: the position at which the insert occurs
379    *
380    * The ::insert signal is emitted when a new #GtkMenuItem is added to
381    * a #GtkMenuShell.  A separate signal is used instead of
382    * GtkContainer::add because of the need for an additional position
383    * parameter.
384    *
385    * The inverse of this signal is the GtkContainer::removed signal.
386    *
387    * Since: 3.2
388    **/
389   menu_shell_signals[INSERT] =
390     g_signal_new (I_("insert"),
391                   G_OBJECT_CLASS_TYPE (object_class),
392                   G_SIGNAL_RUN_FIRST,
393                   G_STRUCT_OFFSET (GtkMenuShellClass, insert),
394                   NULL, NULL,
395                   _gtk_marshal_VOID__OBJECT_INT,
396                   G_TYPE_NONE, 2, GTK_TYPE_WIDGET, G_TYPE_INT);
397
398
399   binding_set = gtk_binding_set_by_class (klass);
400   gtk_binding_entry_add_signal (binding_set,
401                                 GDK_KEY_Escape, 0,
402                                 "cancel", 0);
403   gtk_binding_entry_add_signal (binding_set,
404                                 GDK_KEY_Return, 0,
405                                 "activate-current", 1,
406                                 G_TYPE_BOOLEAN,
407                                 TRUE);
408   gtk_binding_entry_add_signal (binding_set,
409                                 GDK_KEY_ISO_Enter, 0,
410                                 "activate-current", 1,
411                                 G_TYPE_BOOLEAN,
412                                 TRUE);
413   gtk_binding_entry_add_signal (binding_set,
414                                 GDK_KEY_KP_Enter, 0,
415                                 "activate-current", 1,
416                                 G_TYPE_BOOLEAN,
417                                 TRUE);
418   gtk_binding_entry_add_signal (binding_set,
419                                 GDK_KEY_space, 0,
420                                 "activate-current", 1,
421                                 G_TYPE_BOOLEAN,
422                                 FALSE);
423   gtk_binding_entry_add_signal (binding_set,
424                                 GDK_KEY_KP_Space, 0,
425                                 "activate-current", 1,
426                                 G_TYPE_BOOLEAN,
427                                 FALSE);
428   gtk_binding_entry_add_signal (binding_set,
429                                 GDK_KEY_F10, 0,
430                                 "cycle-focus", 1,
431                                 GTK_TYPE_DIRECTION_TYPE, GTK_DIR_TAB_FORWARD);
432   gtk_binding_entry_add_signal (binding_set,
433                                 GDK_KEY_F10, GDK_SHIFT_MASK,
434                                 "cycle-focus", 1,
435                                 GTK_TYPE_DIRECTION_TYPE, GTK_DIR_TAB_BACKWARD);
436
437   /**
438    * GtkMenuShell:take-focus:
439    *
440    * A boolean that determines whether the menu and its submenus grab the
441    * keyboard focus. See gtk_menu_shell_set_take_focus() and
442    * gtk_menu_shell_get_take_focus().
443    *
444    * Since: 2.8
445    **/
446   g_object_class_install_property (object_class,
447                                    PROP_TAKE_FOCUS,
448                                    g_param_spec_boolean ("take-focus",
449                                                          P_("Take Focus"),
450                                                          P_("A boolean that determines whether the menu grabs the keyboard focus"),
451                                                          TRUE,
452                                                          GTK_PARAM_READWRITE));
453
454   gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_MENU_SHELL_ACCESSIBLE);
455
456   g_type_class_add_private (object_class, sizeof (GtkMenuShellPrivate));
457 }
458
459 static GType
460 gtk_menu_shell_child_type (GtkContainer *container)
461 {
462   return GTK_TYPE_MENU_ITEM;
463 }
464
465 static void
466 gtk_menu_shell_init (GtkMenuShell *menu_shell)
467 {
468   GtkMenuShellPrivate *priv;
469
470   priv = G_TYPE_INSTANCE_GET_PRIVATE (menu_shell,
471                                       GTK_TYPE_MENU_SHELL,
472                                       GtkMenuShellPrivate);
473   menu_shell->priv = priv;
474   priv->take_focus = TRUE;
475 }
476
477 static void
478 gtk_menu_shell_set_property (GObject      *object,
479                              guint         prop_id,
480                              const GValue *value,
481                              GParamSpec   *pspec)
482 {
483   GtkMenuShell *menu_shell = GTK_MENU_SHELL (object);
484
485   switch (prop_id)
486     {
487     case PROP_TAKE_FOCUS:
488       gtk_menu_shell_set_take_focus (menu_shell, g_value_get_boolean (value));
489       break;
490     default:
491       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
492       break;
493     }
494 }
495
496 static void
497 gtk_menu_shell_get_property (GObject    *object,
498                              guint       prop_id,
499                              GValue     *value,
500                              GParamSpec *pspec)
501 {
502   GtkMenuShell *menu_shell = GTK_MENU_SHELL (object);
503
504   switch (prop_id)
505     {
506     case PROP_TAKE_FOCUS:
507       g_value_set_boolean (value, gtk_menu_shell_get_take_focus (menu_shell));
508       break;
509     default:
510       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
511       break;
512     }
513 }
514
515 static void
516 gtk_menu_shell_finalize (GObject *object)
517 {
518   GtkMenuShell *menu_shell = GTK_MENU_SHELL (object);
519   GtkMenuShellPrivate *priv = menu_shell->priv;
520
521   if (priv->mnemonic_hash)
522     _gtk_mnemonic_hash_free (priv->mnemonic_hash);
523   if (priv->key_hash)
524     _gtk_key_hash_free (priv->key_hash);
525
526   G_OBJECT_CLASS (gtk_menu_shell_parent_class)->finalize (object);
527 }
528
529
530 static void
531 gtk_menu_shell_dispose (GObject *object)
532 {
533   gtk_menu_shell_deactivate (GTK_MENU_SHELL (object));
534
535   G_OBJECT_CLASS (gtk_menu_shell_parent_class)->dispose (object);
536 }
537
538 /**
539  * gtk_menu_shell_append:
540  * @menu_shell: a #GtkMenuShell
541  * @child: The #GtkMenuItem to add
542  *
543  * Adds a new #GtkMenuItem to the end of the menu shell's
544  * item list.
545  */
546 void
547 gtk_menu_shell_append (GtkMenuShell *menu_shell,
548                        GtkWidget    *child)
549 {
550   gtk_menu_shell_insert (menu_shell, child, -1);
551 }
552
553 /**
554  * gtk_menu_shell_prepend:
555  * @menu_shell: a #GtkMenuShell
556  * @child: The #GtkMenuItem to add
557  *
558  * Adds a new #GtkMenuItem to the beginning of the menu shell's
559  * item list.
560  */
561 void
562 gtk_menu_shell_prepend (GtkMenuShell *menu_shell,
563                         GtkWidget    *child)
564 {
565   gtk_menu_shell_insert (menu_shell, child, 0);
566 }
567
568 /**
569  * gtk_menu_shell_insert:
570  * @menu_shell: a #GtkMenuShell
571  * @child: The #GtkMenuItem to add
572  * @position: The position in the item list where @child
573  *     is added. Positions are numbered from 0 to n-1
574  *
575  * Adds a new #GtkMenuItem to the menu shell's item list
576  * at the position indicated by @position.
577  */
578 void
579 gtk_menu_shell_insert (GtkMenuShell *menu_shell,
580                        GtkWidget    *child,
581                        gint          position)
582 {
583   g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
584   g_return_if_fail (GTK_IS_MENU_ITEM (child));
585
586   g_signal_emit (menu_shell, menu_shell_signals[INSERT], 0, child, position);
587 }
588
589 static void
590 gtk_menu_shell_real_insert (GtkMenuShell *menu_shell,
591                             GtkWidget    *child,
592                             gint          position)
593 {
594   GtkMenuShellPrivate *priv = menu_shell->priv;
595
596   priv->children = g_list_insert (priv->children, child, position);
597
598   gtk_widget_set_parent (child, GTK_WIDGET (menu_shell));
599 }
600
601 /**
602  * gtk_menu_shell_deactivate:
603  * @menu_shell: a #GtkMenuShell
604  *
605  * Deactivates the menu shell.
606  *
607  * Typically this results in the menu shell being erased
608  * from the screen.
609  */
610 void
611 gtk_menu_shell_deactivate (GtkMenuShell *menu_shell)
612 {
613   g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
614
615   if (menu_shell->priv->active)
616     g_signal_emit (menu_shell, menu_shell_signals[DEACTIVATE], 0);
617 }
618
619 static void
620 gtk_menu_shell_realize (GtkWidget *widget)
621 {
622   GtkAllocation allocation;
623   GdkWindow *window;
624   GdkWindowAttr attributes;
625   gint attributes_mask;
626   GtkStyleContext *context;
627
628   gtk_widget_set_realized (widget, TRUE);
629
630   gtk_widget_get_allocation (widget, &allocation);
631
632   attributes.x = allocation.x;
633   attributes.y = allocation.y;
634   attributes.width = allocation.width;
635   attributes.height = allocation.height;
636   attributes.window_type = GDK_WINDOW_CHILD;
637   attributes.wclass = GDK_INPUT_OUTPUT;
638   attributes.visual = gtk_widget_get_visual (widget);
639   attributes.event_mask = gtk_widget_get_events (widget);
640   attributes.event_mask |= (GDK_EXPOSURE_MASK |
641                             GDK_BUTTON_PRESS_MASK |
642                             GDK_BUTTON_RELEASE_MASK |
643                             GDK_KEY_PRESS_MASK |
644                             GDK_ENTER_NOTIFY_MASK |
645                             GDK_LEAVE_NOTIFY_MASK);
646
647   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
648
649   window = gdk_window_new (gtk_widget_get_parent_window (widget),
650                            &attributes, attributes_mask);
651   gtk_widget_set_window (widget, window);
652   gtk_widget_register_window (widget, window);
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                       GdkDevice *source_device;
1087
1088                       source_device = gdk_event_get_source_device ((GdkEvent *) event);
1089
1090                       if (gdk_device_get_source (source_device) == GDK_SOURCE_TOUCHSCREEN)
1091                         _gtk_menu_item_popup_submenu (menu_item, TRUE);
1092                     }
1093                 }
1094             }
1095         }
1096       else if (priv->parent_menu_shell)
1097         {
1098           gtk_widget_event (priv->parent_menu_shell, (GdkEvent*) event);
1099         }
1100     }
1101
1102   return TRUE;
1103 }
1104
1105 static gint
1106 gtk_menu_shell_leave_notify (GtkWidget        *widget,
1107                              GdkEventCrossing *event)
1108 {
1109   if (event->mode == GDK_CROSSING_GTK_GRAB ||
1110       event->mode == GDK_CROSSING_GTK_UNGRAB ||
1111       event->mode == GDK_CROSSING_STATE_CHANGED)
1112     return TRUE;
1113
1114   if (gtk_widget_get_visible (widget))
1115     {
1116       GtkMenuShell *menu_shell = GTK_MENU_SHELL (widget);
1117       GtkMenuShellPrivate *priv = menu_shell->priv;
1118       GtkWidget *event_widget = gtk_get_event_widget ((GdkEvent*) event);
1119       GtkMenuItem *menu_item;
1120
1121       if (!event_widget || !GTK_IS_MENU_ITEM (event_widget))
1122         return TRUE;
1123
1124       menu_item = GTK_MENU_ITEM (event_widget);
1125
1126       if (!_gtk_menu_item_is_selectable (event_widget))
1127         {
1128           priv->in_unselectable_item = TRUE;
1129           return TRUE;
1130         }
1131
1132       if ((priv->active_menu_item == event_widget) &&
1133           (menu_item->priv->submenu == NULL))
1134         {
1135           if ((event->detail != GDK_NOTIFY_INFERIOR) &&
1136               (gtk_widget_get_state_flags (GTK_WIDGET (menu_item)) & GTK_STATE_FLAG_PRELIGHT) != 0)
1137             {
1138               gtk_menu_shell_deselect (menu_shell);
1139             }
1140         }
1141       else if (priv->parent_menu_shell)
1142         {
1143           gtk_widget_event (priv->parent_menu_shell, (GdkEvent*) event);
1144         }
1145     }
1146
1147   return TRUE;
1148 }
1149
1150 static void
1151 gtk_menu_shell_screen_changed (GtkWidget *widget,
1152                                GdkScreen *previous_screen)
1153 {
1154   gtk_menu_shell_reset_key_hash (GTK_MENU_SHELL (widget));
1155 }
1156
1157 static void
1158 gtk_menu_shell_add (GtkContainer *container,
1159                     GtkWidget    *widget)
1160 {
1161   gtk_menu_shell_append (GTK_MENU_SHELL (container), widget);
1162 }
1163
1164 static void
1165 gtk_menu_shell_remove (GtkContainer *container,
1166                        GtkWidget    *widget)
1167 {
1168   GtkMenuShell *menu_shell = GTK_MENU_SHELL (container);
1169   GtkMenuShellPrivate *priv = menu_shell->priv;
1170   gint was_visible;
1171
1172   was_visible = gtk_widget_get_visible (widget);
1173   priv->children = g_list_remove (priv->children, widget);
1174
1175   if (widget == priv->active_menu_item)
1176     {
1177       g_signal_emit_by_name (priv->active_menu_item, "deselect");
1178       priv->active_menu_item = NULL;
1179     }
1180
1181   gtk_widget_unparent (widget);
1182
1183   /* Queue resize regardless of gtk_widget_get_visible (container),
1184    * since that's what is needed by toplevels.
1185    */
1186   if (was_visible)
1187     gtk_widget_queue_resize (GTK_WIDGET (container));
1188 }
1189
1190 static void
1191 gtk_menu_shell_forall (GtkContainer *container,
1192                        gboolean      include_internals,
1193                        GtkCallback   callback,
1194                        gpointer      callback_data)
1195 {
1196   GtkMenuShell *menu_shell = GTK_MENU_SHELL (container);
1197   GtkWidget *child;
1198   GList *children;
1199
1200   children = menu_shell->priv->children;
1201   while (children)
1202     {
1203       child = children->data;
1204       children = children->next;
1205
1206       (* callback) (child, callback_data);
1207     }
1208 }
1209
1210
1211 static void
1212 gtk_real_menu_shell_deactivate (GtkMenuShell *menu_shell)
1213 {
1214   GtkMenuShellPrivate *priv = menu_shell->priv;
1215
1216   if (priv->active)
1217     {
1218       priv->button = 0;
1219       priv->active = FALSE;
1220       priv->activate_time = 0;
1221
1222       if (priv->active_menu_item)
1223         {
1224           gtk_menu_item_deselect (GTK_MENU_ITEM (priv->active_menu_item));
1225           priv->active_menu_item = NULL;
1226         }
1227
1228       if (priv->have_grab)
1229         {
1230           priv->have_grab = FALSE;
1231           gtk_device_grab_remove (GTK_WIDGET (menu_shell), priv->grab_pointer);
1232         }
1233       if (priv->have_xgrab)
1234         {
1235           GdkDevice *keyboard;
1236
1237           gdk_device_ungrab (priv->grab_pointer, GDK_CURRENT_TIME);
1238           keyboard = gdk_device_get_associated_device (priv->grab_pointer);
1239
1240           if (keyboard)
1241             gdk_device_ungrab (keyboard, GDK_CURRENT_TIME);
1242
1243           priv->have_xgrab = FALSE;
1244         }
1245
1246       priv->keyboard_mode = FALSE;
1247       _gtk_menu_shell_set_grab_device (menu_shell, NULL);
1248
1249       _gtk_menu_shell_update_mnemonics (menu_shell);
1250     }
1251 }
1252
1253 static gint
1254 gtk_menu_shell_is_item (GtkMenuShell *menu_shell,
1255                         GtkWidget    *child)
1256 {
1257   GtkWidget *parent;
1258
1259   g_return_val_if_fail (GTK_IS_MENU_SHELL (menu_shell), FALSE);
1260   g_return_val_if_fail (child != NULL, FALSE);
1261
1262   parent = gtk_widget_get_parent (child);
1263   while (GTK_IS_MENU_SHELL (parent))
1264     {
1265       if (parent == (GtkWidget*) menu_shell)
1266         return TRUE;
1267       parent = GTK_MENU_SHELL (parent)->priv->parent_menu_shell;
1268     }
1269
1270   return FALSE;
1271 }
1272
1273 static GtkWidget*
1274 gtk_menu_shell_get_item (GtkMenuShell *menu_shell,
1275                          GdkEvent     *event)
1276 {
1277   GtkWidget *menu_item;
1278
1279   menu_item = gtk_get_event_widget ((GdkEvent*) event);
1280
1281   while (menu_item && !GTK_IS_MENU_ITEM (menu_item))
1282     menu_item = gtk_widget_get_parent (menu_item);
1283
1284   if (menu_item && gtk_menu_shell_is_item (menu_shell, menu_item))
1285     return menu_item;
1286   else
1287     return NULL;
1288 }
1289
1290 /* Handlers for action signals */
1291
1292 /**
1293  * gtk_menu_shell_select_item:
1294  * @menu_shell: a #GtkMenuShell
1295  * @menu_item: The #GtkMenuItem to select
1296  *
1297  * Selects the menu item from the menu shell.
1298  */
1299 void
1300 gtk_menu_shell_select_item (GtkMenuShell *menu_shell,
1301                             GtkWidget    *menu_item)
1302 {
1303   GtkMenuShellPrivate *priv = menu_shell->priv;
1304   GtkMenuShellClass *class;
1305
1306   g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
1307   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
1308
1309   class = GTK_MENU_SHELL_GET_CLASS (menu_shell);
1310
1311   if (class->select_item &&
1312       !(priv->active &&
1313         priv->active_menu_item == menu_item))
1314     class->select_item (menu_shell, menu_item);
1315 }
1316
1317 void _gtk_menu_item_set_placement (GtkMenuItem         *menu_item,
1318                                    GtkSubmenuPlacement  placement);
1319
1320 static void
1321 gtk_menu_shell_real_select_item (GtkMenuShell *menu_shell,
1322                                  GtkWidget    *menu_item)
1323 {
1324   GtkMenuShellPrivate *priv = menu_shell->priv;
1325   GtkPackDirection pack_dir = PACK_DIRECTION (menu_shell);
1326
1327   if (priv->active_menu_item)
1328     {
1329       gtk_menu_item_deselect (GTK_MENU_ITEM (priv->active_menu_item));
1330       priv->active_menu_item = NULL;
1331     }
1332
1333   if (!_gtk_menu_item_is_selectable (menu_item))
1334     {
1335       priv->in_unselectable_item = TRUE;
1336       _gtk_menu_shell_update_mnemonics (menu_shell);
1337       return;
1338     }
1339
1340   gtk_menu_shell_activate (menu_shell);
1341
1342   priv->active_menu_item = menu_item;
1343   if (pack_dir == GTK_PACK_DIRECTION_TTB || pack_dir == GTK_PACK_DIRECTION_BTT)
1344     _gtk_menu_item_set_placement (GTK_MENU_ITEM (priv->active_menu_item),
1345                                   GTK_LEFT_RIGHT);
1346   else
1347     _gtk_menu_item_set_placement (GTK_MENU_ITEM (priv->active_menu_item),
1348                                   GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement);
1349   gtk_menu_item_select (GTK_MENU_ITEM (priv->active_menu_item));
1350
1351   _gtk_menu_shell_update_mnemonics (menu_shell);
1352
1353   /* This allows the bizarre radio buttons-with-submenus-display-history
1354    * behavior
1355    */
1356   if (GTK_MENU_ITEM (priv->active_menu_item)->priv->submenu)
1357     gtk_widget_activate (priv->active_menu_item);
1358 }
1359
1360 /**
1361  * gtk_menu_shell_deselect:
1362  * @menu_shell: a #GtkMenuShell
1363  *
1364  * Deselects the currently selected item from the menu shell,
1365  * if any.
1366  */
1367 void
1368 gtk_menu_shell_deselect (GtkMenuShell *menu_shell)
1369 {
1370   GtkMenuShellPrivate *priv = menu_shell->priv;
1371
1372   g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
1373
1374   if (priv->active_menu_item)
1375     {
1376       gtk_menu_item_deselect (GTK_MENU_ITEM (priv->active_menu_item));
1377       priv->active_menu_item = NULL;
1378       _gtk_menu_shell_update_mnemonics (menu_shell);
1379     }
1380 }
1381
1382 /**
1383  * gtk_menu_shell_activate_item:
1384  * @menu_shell: a #GtkMenuShell
1385  * @menu_item: the #GtkMenuItem to activate
1386  * @force_deactivate: if %TRUE, force the deactivation of the
1387  *     menu shell after the menu item is activated
1388  *
1389  * Activates the menu item within the menu shell.
1390  */
1391 void
1392 gtk_menu_shell_activate_item (GtkMenuShell *menu_shell,
1393                               GtkWidget    *menu_item,
1394                               gboolean      force_deactivate)
1395 {
1396   GSList *slist, *shells = NULL;
1397   gboolean deactivate = force_deactivate;
1398
1399   g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
1400   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
1401
1402   if (!deactivate)
1403     deactivate = GTK_MENU_ITEM_GET_CLASS (menu_item)->hide_on_activate;
1404
1405   g_object_ref (menu_shell);
1406   g_object_ref (menu_item);
1407
1408   if (deactivate)
1409     {
1410       GtkMenuShell *parent_menu_shell = menu_shell;
1411
1412       do
1413         {
1414           g_object_ref (parent_menu_shell);
1415           shells = g_slist_prepend (shells, parent_menu_shell);
1416           parent_menu_shell = (GtkMenuShell*) parent_menu_shell->priv->parent_menu_shell;
1417         }
1418       while (parent_menu_shell);
1419       shells = g_slist_reverse (shells);
1420
1421       gtk_menu_shell_deactivate (menu_shell);
1422
1423       /* Flush the x-queue, so any grabs are removed and
1424        * the menu is actually taken down
1425        */
1426       gdk_display_sync (gtk_widget_get_display (menu_item));
1427     }
1428
1429   gtk_widget_activate (menu_item);
1430
1431   for (slist = shells; slist; slist = slist->next)
1432     {
1433       g_signal_emit (slist->data, menu_shell_signals[SELECTION_DONE], 0);
1434       g_object_unref (slist->data);
1435     }
1436   g_slist_free (shells);
1437
1438   g_object_unref (menu_shell);
1439   g_object_unref (menu_item);
1440 }
1441
1442 /* Distance should be +/- 1 */
1443 static gboolean
1444 gtk_menu_shell_real_move_selected (GtkMenuShell  *menu_shell,
1445                                    gint           distance)
1446 {
1447   GtkMenuShellPrivate *priv = menu_shell->priv;
1448
1449   if (priv->active_menu_item)
1450     {
1451       GList *node = g_list_find (priv->children, priv->active_menu_item);
1452       GList *start_node = node;
1453       gboolean wrap_around;
1454
1455       g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu_shell)),
1456                     "gtk-keynav-wrap-around", &wrap_around,
1457                     NULL);
1458
1459       if (distance > 0)
1460         {
1461           node = node->next;
1462           while (node != start_node &&
1463                  (!node || !_gtk_menu_item_is_selectable (node->data)))
1464             {
1465               if (node)
1466                 node = node->next;
1467               else if (wrap_around)
1468                 node = priv->children;
1469               else
1470                 {
1471                   gtk_widget_error_bell (GTK_WIDGET (menu_shell));
1472                   break;
1473                 }
1474             }
1475         }
1476       else
1477         {
1478           node = node->prev;
1479           while (node != start_node &&
1480                  (!node || !_gtk_menu_item_is_selectable (node->data)))
1481             {
1482               if (node)
1483                 node = node->prev;
1484               else if (wrap_around)
1485                 node = g_list_last (priv->children);
1486               else
1487                 {
1488                   gtk_widget_error_bell (GTK_WIDGET (menu_shell));
1489                   break;
1490                 }
1491             }
1492         }
1493       
1494       if (node)
1495         gtk_menu_shell_select_item (menu_shell, node->data);
1496     }
1497
1498   return TRUE;
1499 }
1500
1501 /* Distance should be +/- 1 */
1502 static void
1503 gtk_menu_shell_move_selected (GtkMenuShell  *menu_shell,
1504                               gint           distance)
1505 {
1506   gboolean handled = FALSE;
1507
1508   g_signal_emit (menu_shell, menu_shell_signals[MOVE_SELECTED], 0,
1509                  distance, &handled);
1510 }
1511
1512 /**
1513  * gtk_menu_shell_select_first:
1514  * @menu_shell: a #GtkMenuShell
1515  * @search_sensitive: if %TRUE, search for the first selectable
1516  *                    menu item, otherwise select nothing if
1517  *                    the first item isn't sensitive. This
1518  *                    should be %FALSE if the menu is being
1519  *                    popped up initially.
1520  *
1521  * Select the first visible or selectable child of the menu shell;
1522  * don't select tearoff items unless the only item is a tearoff
1523  * item.
1524  *
1525  * Since: 2.2
1526  */
1527 void
1528 gtk_menu_shell_select_first (GtkMenuShell *menu_shell,
1529                              gboolean      search_sensitive)
1530 {
1531   GtkMenuShellPrivate *priv = menu_shell->priv;
1532   GtkWidget *to_select = NULL;
1533   GList *tmp_list;
1534
1535   tmp_list = priv->children;
1536   while (tmp_list)
1537     {
1538       GtkWidget *child = tmp_list->data;
1539
1540       if ((!search_sensitive && gtk_widget_get_visible (child)) ||
1541           _gtk_menu_item_is_selectable (child))
1542         {
1543           to_select = child;
1544           if (!GTK_IS_TEAROFF_MENU_ITEM (child))
1545             break;
1546         }
1547
1548       tmp_list = tmp_list->next;
1549     }
1550
1551   if (to_select)
1552     gtk_menu_shell_select_item (menu_shell, to_select);
1553 }
1554
1555 void
1556 _gtk_menu_shell_select_last (GtkMenuShell *menu_shell,
1557                              gboolean      search_sensitive)
1558 {
1559   GtkMenuShellPrivate *priv = menu_shell->priv;
1560   GtkWidget *to_select = NULL;
1561   GList *tmp_list;
1562
1563   tmp_list = g_list_last (priv->children);
1564   while (tmp_list)
1565     {
1566       GtkWidget *child = tmp_list->data;
1567
1568       if ((!search_sensitive && gtk_widget_get_visible (child)) ||
1569           _gtk_menu_item_is_selectable (child))
1570         {
1571           to_select = child;
1572           if (!GTK_IS_TEAROFF_MENU_ITEM (child))
1573             break;
1574         }
1575
1576       tmp_list = tmp_list->prev;
1577     }
1578
1579   if (to_select)
1580     gtk_menu_shell_select_item (menu_shell, to_select);
1581 }
1582
1583 static gboolean
1584 gtk_menu_shell_select_submenu_first (GtkMenuShell *menu_shell)
1585 {
1586   GtkMenuShellPrivate *priv = menu_shell->priv;
1587   GtkMenuItem *menu_item;
1588
1589   if (priv->active_menu_item == NULL)
1590     return FALSE;
1591
1592   menu_item = GTK_MENU_ITEM (priv->active_menu_item);
1593
1594   if (menu_item->priv->submenu)
1595     {
1596       _gtk_menu_item_popup_submenu (GTK_WIDGET (menu_item), FALSE);
1597       gtk_menu_shell_select_first (GTK_MENU_SHELL (menu_item->priv->submenu), TRUE);
1598       if (GTK_MENU_SHELL (menu_item->priv->submenu)->priv->active_menu_item)
1599         return TRUE;
1600     }
1601
1602   return FALSE;
1603 }
1604
1605 static void
1606 gtk_real_menu_shell_move_current (GtkMenuShell         *menu_shell,
1607                                   GtkMenuDirectionType  direction)
1608 {
1609   GtkMenuShellPrivate *priv = menu_shell->priv;
1610   GtkMenuShell *parent_menu_shell = NULL;
1611   gboolean had_selection;
1612
1613   priv->in_unselectable_item = FALSE;
1614
1615   had_selection = priv->active_menu_item != NULL;
1616
1617   if (priv->parent_menu_shell)
1618     parent_menu_shell = GTK_MENU_SHELL (priv->parent_menu_shell);
1619
1620   switch (direction)
1621     {
1622     case GTK_MENU_DIR_PARENT:
1623       if (parent_menu_shell)
1624         {
1625           if (GTK_MENU_SHELL_GET_CLASS (parent_menu_shell)->submenu_placement ==
1626               GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement)
1627             gtk_menu_shell_deselect (menu_shell);
1628           else
1629             {
1630               if (PACK_DIRECTION (parent_menu_shell) == GTK_PACK_DIRECTION_LTR)
1631                 gtk_menu_shell_move_selected (parent_menu_shell, -1);
1632               else
1633                 gtk_menu_shell_move_selected (parent_menu_shell, 1);
1634               gtk_menu_shell_select_submenu_first (parent_menu_shell);
1635             }
1636         }
1637       /* If there is no parent and the submenu is in the opposite direction
1638        * to the menu, then make the PARENT direction wrap around to
1639        * the bottom of the submenu.
1640        */
1641       else if (priv->active_menu_item &&
1642                _gtk_menu_item_is_selectable (priv->active_menu_item) &&
1643                GTK_MENU_ITEM (priv->active_menu_item)->priv->submenu)
1644         {
1645           GtkMenuShell *submenu = GTK_MENU_SHELL (GTK_MENU_ITEM (priv->active_menu_item)->priv->submenu);
1646
1647           if (GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement !=
1648               GTK_MENU_SHELL_GET_CLASS (submenu)->submenu_placement)
1649             _gtk_menu_shell_select_last (submenu, TRUE);
1650         }
1651       break;
1652
1653     case GTK_MENU_DIR_CHILD:
1654       if (priv->active_menu_item &&
1655           _gtk_menu_item_is_selectable (priv->active_menu_item) &&
1656           GTK_MENU_ITEM (priv->active_menu_item)->priv->submenu)
1657         {
1658           if (gtk_menu_shell_select_submenu_first (menu_shell))
1659             break;
1660         }
1661
1662       /* Try to find a menu running the opposite direction */
1663       while (parent_menu_shell &&
1664              (GTK_MENU_SHELL_GET_CLASS (parent_menu_shell)->submenu_placement ==
1665               GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement))
1666         {
1667           parent_menu_shell = GTK_MENU_SHELL (parent_menu_shell->priv->parent_menu_shell);
1668         }
1669
1670       if (parent_menu_shell)
1671         {
1672           if (PACK_DIRECTION (parent_menu_shell) == GTK_PACK_DIRECTION_LTR)
1673             gtk_menu_shell_move_selected (parent_menu_shell, 1);
1674           else
1675             gtk_menu_shell_move_selected (parent_menu_shell, -1);
1676
1677           gtk_menu_shell_select_submenu_first (parent_menu_shell);
1678         }
1679       break;
1680
1681     case GTK_MENU_DIR_PREV:
1682       gtk_menu_shell_move_selected (menu_shell, -1);
1683       if (!had_selection && !priv->active_menu_item && priv->children)
1684         _gtk_menu_shell_select_last (menu_shell, TRUE);
1685       break;
1686
1687     case GTK_MENU_DIR_NEXT:
1688       gtk_menu_shell_move_selected (menu_shell, 1);
1689       if (!had_selection && !priv->active_menu_item && priv->children)
1690         gtk_menu_shell_select_first (menu_shell, TRUE);
1691       break;
1692     }
1693 }
1694
1695 static void
1696 gtk_real_menu_shell_activate_current (GtkMenuShell *menu_shell,
1697                                       gboolean      force_hide)
1698 {
1699   GtkMenuShellPrivate *priv = menu_shell->priv;
1700
1701   if (priv->active_menu_item &&
1702       _gtk_menu_item_is_selectable (priv->active_menu_item))
1703   {
1704     if (GTK_MENU_ITEM (priv->active_menu_item)->priv->submenu == NULL)
1705       gtk_menu_shell_activate_item (menu_shell,
1706                                     priv->active_menu_item,
1707                                     force_hide);
1708     else
1709       _gtk_menu_item_popup_submenu (priv->active_menu_item, FALSE);
1710   }
1711 }
1712
1713 static void
1714 gtk_real_menu_shell_cancel (GtkMenuShell *menu_shell)
1715 {
1716   /* Unset the active menu item so gtk_menu_popdown() doesn't see it. */
1717   gtk_menu_shell_deselect (menu_shell);
1718   gtk_menu_shell_deactivate (menu_shell);
1719   g_signal_emit (menu_shell, menu_shell_signals[SELECTION_DONE], 0);
1720 }
1721
1722 static void
1723 gtk_real_menu_shell_cycle_focus (GtkMenuShell     *menu_shell,
1724                                  GtkDirectionType  dir)
1725 {
1726   while (menu_shell && !GTK_IS_MENU_BAR (menu_shell))
1727     {
1728       if (menu_shell->priv->parent_menu_shell)
1729         menu_shell = GTK_MENU_SHELL (menu_shell->priv->parent_menu_shell);
1730       else
1731         menu_shell = NULL;
1732     }
1733
1734   if (menu_shell)
1735     _gtk_menu_bar_cycle_focus (GTK_MENU_BAR (menu_shell), dir);
1736 }
1737
1738 gint
1739 _gtk_menu_shell_get_popup_delay (GtkMenuShell *menu_shell)
1740 {
1741   GtkMenuShellClass *klass = GTK_MENU_SHELL_GET_CLASS (menu_shell);
1742   
1743   if (klass->get_popup_delay)
1744     {
1745       return klass->get_popup_delay (menu_shell);
1746     }
1747   else
1748     {
1749       gint popup_delay;
1750       GtkWidget *widget = GTK_WIDGET (menu_shell);
1751
1752       g_object_get (gtk_widget_get_settings (widget),
1753                     "gtk-menu-popup-delay", &popup_delay,
1754                     NULL);
1755
1756       return popup_delay;
1757     }
1758 }
1759
1760 /**
1761  * gtk_menu_shell_cancel:
1762  * @menu_shell: a #GtkMenuShell
1763  *
1764  * Cancels the selection within the menu shell.
1765  *
1766  * Since: 2.4
1767  */
1768 void
1769 gtk_menu_shell_cancel (GtkMenuShell *menu_shell)
1770 {
1771   g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
1772
1773   g_signal_emit (menu_shell, menu_shell_signals[CANCEL], 0);
1774 }
1775
1776 static GtkMnemonicHash *
1777 gtk_menu_shell_get_mnemonic_hash (GtkMenuShell *menu_shell,
1778                                   gboolean      create)
1779 {
1780   GtkMenuShellPrivate *priv = menu_shell->priv;
1781
1782   if (!priv->mnemonic_hash && create)
1783     priv->mnemonic_hash = _gtk_mnemonic_hash_new ();
1784   
1785   return priv->mnemonic_hash;
1786 }
1787
1788 static void
1789 menu_shell_add_mnemonic_foreach (guint     keyval,
1790                                  GSList   *targets,
1791                                  gpointer  data)
1792 {
1793   GtkKeyHash *key_hash = data;
1794
1795   _gtk_key_hash_add_entry (key_hash, keyval, 0, GUINT_TO_POINTER (keyval));
1796 }
1797
1798 static GtkKeyHash *
1799 gtk_menu_shell_get_key_hash (GtkMenuShell *menu_shell,
1800                              gboolean      create)
1801 {
1802   GtkMenuShellPrivate *priv = menu_shell->priv;
1803   GtkWidget *widget = GTK_WIDGET (menu_shell);
1804
1805   if (!priv->key_hash && create && gtk_widget_has_screen (widget))
1806     {
1807       GtkMnemonicHash *mnemonic_hash = gtk_menu_shell_get_mnemonic_hash (menu_shell, FALSE);
1808       GdkScreen *screen = gtk_widget_get_screen (widget);
1809       GdkKeymap *keymap = gdk_keymap_get_for_display (gdk_screen_get_display (screen));
1810
1811       if (!mnemonic_hash)
1812         return NULL;
1813
1814       priv->key_hash = _gtk_key_hash_new (keymap, NULL);
1815
1816       _gtk_mnemonic_hash_foreach (mnemonic_hash,
1817                                   menu_shell_add_mnemonic_foreach,
1818                                   priv->key_hash);
1819     }
1820
1821   return priv->key_hash;
1822 }
1823
1824 static void
1825 gtk_menu_shell_reset_key_hash (GtkMenuShell *menu_shell)
1826 {
1827   GtkMenuShellPrivate *priv = menu_shell->priv;
1828
1829   if (priv->key_hash)
1830     {
1831       _gtk_key_hash_free (priv->key_hash);
1832       priv->key_hash = NULL;
1833     }
1834 }
1835
1836 static gboolean
1837 gtk_menu_shell_activate_mnemonic (GtkMenuShell *menu_shell,
1838                                   GdkEventKey  *event)
1839 {
1840   GtkMnemonicHash *mnemonic_hash;
1841   GtkKeyHash *key_hash;
1842   GSList *entries;
1843   gboolean result = FALSE;
1844
1845   mnemonic_hash = gtk_menu_shell_get_mnemonic_hash (menu_shell, FALSE);
1846   if (!mnemonic_hash)
1847     return FALSE;
1848
1849   key_hash = gtk_menu_shell_get_key_hash (menu_shell, TRUE);
1850   if (!key_hash)
1851     return FALSE;
1852
1853   entries = _gtk_key_hash_lookup (key_hash,
1854                                   event->hardware_keycode,
1855                                   event->state,
1856                                   gtk_accelerator_get_default_mod_mask (),
1857                                   event->group);
1858
1859   if (entries)
1860     {
1861       result = _gtk_mnemonic_hash_activate (mnemonic_hash,
1862                                             GPOINTER_TO_UINT (entries->data));
1863       g_slist_free (entries);
1864     }
1865
1866   return result;
1867 }
1868
1869 void
1870 _gtk_menu_shell_add_mnemonic (GtkMenuShell *menu_shell,
1871                               guint         keyval,
1872                               GtkWidget    *target)
1873 {
1874   g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
1875   g_return_if_fail (GTK_IS_WIDGET (target));
1876
1877   _gtk_mnemonic_hash_add (gtk_menu_shell_get_mnemonic_hash (menu_shell, TRUE),
1878                           keyval, target);
1879   gtk_menu_shell_reset_key_hash (menu_shell);
1880 }
1881
1882 void
1883 _gtk_menu_shell_remove_mnemonic (GtkMenuShell *menu_shell,
1884                                  guint         keyval,
1885                                  GtkWidget    *target)
1886 {
1887   g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
1888   g_return_if_fail (GTK_IS_WIDGET (target));
1889
1890   _gtk_mnemonic_hash_remove (gtk_menu_shell_get_mnemonic_hash (menu_shell, TRUE),
1891                              keyval, target);
1892   gtk_menu_shell_reset_key_hash (menu_shell);
1893 }
1894
1895 void
1896 _gtk_menu_shell_set_grab_device (GtkMenuShell *menu_shell,
1897                                  GdkDevice    *device)
1898 {
1899   GtkMenuShellPrivate *priv = menu_shell->priv;
1900
1901   g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
1902   g_return_if_fail (device == NULL || GDK_IS_DEVICE (device));
1903
1904   if (!device)
1905     priv->grab_pointer = NULL;
1906   else if (gdk_device_get_source (device) == GDK_SOURCE_KEYBOARD)
1907     priv->grab_pointer = gdk_device_get_associated_device (device);
1908   else
1909     priv->grab_pointer = device;
1910 }
1911
1912 GdkDevice *
1913 _gtk_menu_shell_get_grab_device (GtkMenuShell *menu_shell)
1914 {
1915   g_return_val_if_fail (GTK_IS_MENU_SHELL (menu_shell), NULL);
1916
1917   return menu_shell->priv->grab_pointer;
1918 }
1919
1920 /**
1921  * gtk_menu_shell_get_take_focus:
1922  * @menu_shell: a #GtkMenuShell
1923  *
1924  * Returns %TRUE if the menu shell will take the keyboard focus on popup.
1925  *
1926  * Returns: %TRUE if the menu shell will take the keyboard focus on popup.
1927  *
1928  * Since: 2.8
1929  */
1930 gboolean
1931 gtk_menu_shell_get_take_focus (GtkMenuShell *menu_shell)
1932 {
1933   g_return_val_if_fail (GTK_IS_MENU_SHELL (menu_shell), FALSE);
1934
1935   return menu_shell->priv->take_focus;
1936 }
1937
1938 /**
1939  * gtk_menu_shell_set_take_focus:
1940  * @menu_shell: a #GtkMenuShell
1941  * @take_focus: %TRUE if the menu shell should take the keyboard
1942  *     focus on popup
1943  *
1944  * If @take_focus is %TRUE (the default) the menu shell will take
1945  * the keyboard focus so that it will receive all keyboard events
1946  * which is needed to enable keyboard navigation in menus.
1947  *
1948  * Setting @take_focus to %FALSE is useful only for special applications
1949  * like virtual keyboard implementations which should not take keyboard
1950  * focus.
1951  *
1952  * The @take_focus state of a menu or menu bar is automatically
1953  * propagated to submenus whenever a submenu is popped up, so you
1954  * don't have to worry about recursively setting it for your entire
1955  * menu hierarchy. Only when programmatically picking a submenu and
1956  * popping it up manually, the @take_focus property of the submenu
1957  * needs to be set explicitely.
1958  *
1959  * Note that setting it to %FALSE has side-effects:
1960  *
1961  * If the focus is in some other app, it keeps the focus and keynav in
1962  * the menu doesn't work. Consequently, keynav on the menu will only
1963  * work if the focus is on some toplevel owned by the onscreen keyboard.
1964  *
1965  * To avoid confusing the user, menus with @take_focus set to %FALSE
1966  * should not display mnemonics or accelerators, since it cannot be
1967  * guaranteed that they will work.
1968  *
1969  * See also gdk_keyboard_grab()
1970  *
1971  * Since: 2.8
1972  */
1973 void
1974 gtk_menu_shell_set_take_focus (GtkMenuShell *menu_shell,
1975                                gboolean      take_focus)
1976 {
1977   GtkMenuShellPrivate *priv = menu_shell->priv;
1978
1979   g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
1980
1981   if (priv->take_focus != take_focus)
1982     {
1983       priv->take_focus = take_focus;
1984       g_object_notify (G_OBJECT (menu_shell), "take-focus");
1985     }
1986 }
1987
1988 /**
1989  * gtk_menu_shell_get_selected_item:
1990  * @menu_shell: a #GtkMenuShell
1991  *
1992  * Gets the currently selected item.
1993  *
1994  * Returns: (transfer none): the currently selected item
1995  *
1996  * Since: 3.0
1997  */
1998 GtkWidget *
1999 gtk_menu_shell_get_selected_item (GtkMenuShell *menu_shell)
2000 {
2001   g_return_val_if_fail (GTK_IS_MENU_SHELL (menu_shell), NULL);
2002
2003   return menu_shell->priv->active_menu_item;
2004 }
2005
2006 /**
2007  * gtk_menu_shell_get_parent_shell:
2008  * @menu_shell: a #GtkMenuShell
2009  *
2010  * Gets the parent menu shell.
2011  *
2012  * The parent menu shell of a submenu is the #GtkMenu or #GtkMenuBar
2013  * from which it was opened up.
2014  *
2015  * Returns: (transfer none): the parent #GtkMenuShell
2016  *
2017  * Since: 3.0
2018  */
2019 GtkWidget *
2020 gtk_menu_shell_get_parent_shell (GtkMenuShell *menu_shell)
2021 {
2022   g_return_val_if_fail (GTK_IS_MENU_SHELL (menu_shell), NULL);
2023
2024   return menu_shell->priv->parent_menu_shell;
2025 }