1 /* GTK - The GIMP Toolkit
2 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
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.
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.
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.
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/.
28 * SECTION:gtkmenushell
29 * @Title: GtkMenuShell
30 * @Short_description: A base class for menu objects
32 * A #GtkMenuShell is the abstract base class used to derive the
33 * #GtkMenu and #GtkMenuBar subclasses.
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.
42 #include "gtkbindings.h"
43 #include "gtkkeyhash.h"
46 #include "gtkmarshalers.h"
47 #include "gtkmenubar.h"
48 #include "gtkmenuitemprivate.h"
49 #include "gtkmenushellprivate.h"
50 #include "gtkmenuprivate.h"
51 #include "gtkmnemonichash.h"
52 #include "gtkwindow.h"
53 #include "gtkprivate.h"
56 #include "gtktypebuiltins.h"
58 #include "deprecated/gtktearoffmenuitem.h"
60 #include "a11y/gtkmenushellaccessible.h"
63 #define MENU_SHELL_TIMEOUT 500
65 #define PACK_DIRECTION(m) \
66 (GTK_IS_MENU_BAR (m) \
67 ? gtk_menu_bar_get_pack_direction (GTK_MENU_BAR (m)) \
68 : GTK_PACK_DIRECTION_LTR)
89 * A menu item can be "selected", this means that it is displayed
90 * in the prelight state, and if it has a submenu, that submenu
93 * A menu is "active" when it is visible onscreen and the user
94 * is selecting from it. A menubar is not active until the user
95 * clicks on one of its menuitems. When a menu is active,
96 * passing the mouse over a submenu will pop it up.
98 * menu_shell->active_menu_item, is however, not an "active"
99 * menu item (there is no such thing) but rather, the selected
100 * menu item in that MenuShell, if there is one.
102 * There is also is a concept of the current menu and a current
103 * menu item. The current menu item is the selected menu item
104 * that is furthest down in the hierarchy. (Every active menu_shell
105 * does not necessarily contain a selected menu item, but if
106 * it does, then menu_shell->parent_menu_shell must also contain
107 * a selected menu item. The current menu is the menu that
108 * contains the current menu_item. It will always have a GTK
109 * grab and receive all key presses.
114 * ::move_current (GtkMenuDirection *dir)
115 * Moves the current menu item in direction 'dir':
117 * GTK_MENU_DIR_PARENT: To the parent menu shell
118 * GTK_MENU_DIR_CHILD: To the child menu shell (if this item has
120 * GTK_MENU_DIR_NEXT/PREV: To the next or previous item
123 * As a a bit of a hack to get movement between menus and
124 * menubars working, if submenu_placement is different for
125 * the menu and its MenuShell then the following apply:
127 * - For 'parent' the current menu is not just moved to
128 * the parent, but moved to the previous entry in the parent
129 * - For 'child', if there is no child, then current is
130 * moved to the next item in the parent.
132 * Note that the above explanation of ::move_current was written
133 * before menus and menubars had support for RTL flipping and
134 * different packing directions, and therefore only applies for
135 * when text direction and packing direction are both left-to-right.
137 * ::activate_current (GBoolean *force_hide)
138 * Activate the current item. If 'force_hide' is true, hide
139 * the current menu item always. Otherwise, only hide
140 * it if menu_item->klass->hide_on_activate is true.
143 * Cancels the current selection
147 static void gtk_menu_shell_set_property (GObject *object,
151 static void gtk_menu_shell_get_property (GObject *object,
155 static void gtk_menu_shell_realize (GtkWidget *widget);
156 static void gtk_menu_shell_finalize (GObject *object);
157 static void gtk_menu_shell_dispose (GObject *object);
158 static gint gtk_menu_shell_button_press (GtkWidget *widget,
159 GdkEventButton *event);
160 static gint gtk_menu_shell_button_release (GtkWidget *widget,
161 GdkEventButton *event);
162 static gint gtk_menu_shell_key_press (GtkWidget *widget,
164 static gint gtk_menu_shell_enter_notify (GtkWidget *widget,
165 GdkEventCrossing *event);
166 static gint gtk_menu_shell_leave_notify (GtkWidget *widget,
167 GdkEventCrossing *event);
168 static void gtk_menu_shell_screen_changed (GtkWidget *widget,
169 GdkScreen *previous_screen);
170 static gboolean gtk_menu_shell_grab_broken (GtkWidget *widget,
171 GdkEventGrabBroken *event);
172 static void gtk_menu_shell_add (GtkContainer *container,
174 static void gtk_menu_shell_remove (GtkContainer *container,
176 static void gtk_menu_shell_forall (GtkContainer *container,
177 gboolean include_internals,
178 GtkCallback callback,
179 gpointer callback_data);
180 static void gtk_menu_shell_real_insert (GtkMenuShell *menu_shell,
183 static void gtk_real_menu_shell_deactivate (GtkMenuShell *menu_shell);
184 static gint gtk_menu_shell_is_item (GtkMenuShell *menu_shell,
186 static GtkWidget *gtk_menu_shell_get_item (GtkMenuShell *menu_shell,
188 static GType gtk_menu_shell_child_type (GtkContainer *container);
189 static void gtk_menu_shell_real_select_item (GtkMenuShell *menu_shell,
190 GtkWidget *menu_item);
191 static gboolean gtk_menu_shell_select_submenu_first (GtkMenuShell *menu_shell);
193 static void gtk_real_menu_shell_move_current (GtkMenuShell *menu_shell,
194 GtkMenuDirectionType direction);
195 static void gtk_real_menu_shell_activate_current (GtkMenuShell *menu_shell,
196 gboolean force_hide);
197 static void gtk_real_menu_shell_cancel (GtkMenuShell *menu_shell);
198 static void gtk_real_menu_shell_cycle_focus (GtkMenuShell *menu_shell,
199 GtkDirectionType dir);
201 static void gtk_menu_shell_reset_key_hash (GtkMenuShell *menu_shell);
202 static gboolean gtk_menu_shell_activate_mnemonic (GtkMenuShell *menu_shell,
204 static gboolean gtk_menu_shell_real_move_selected (GtkMenuShell *menu_shell,
207 static guint menu_shell_signals[LAST_SIGNAL] = { 0 };
209 G_DEFINE_ABSTRACT_TYPE (GtkMenuShell, gtk_menu_shell, GTK_TYPE_CONTAINER)
212 gtk_menu_shell_class_init (GtkMenuShellClass *klass)
214 GObjectClass *object_class;
215 GtkWidgetClass *widget_class;
216 GtkContainerClass *container_class;
218 GtkBindingSet *binding_set;
220 object_class = (GObjectClass*) klass;
221 widget_class = (GtkWidgetClass*) klass;
222 container_class = (GtkContainerClass*) klass;
224 object_class->set_property = gtk_menu_shell_set_property;
225 object_class->get_property = gtk_menu_shell_get_property;
226 object_class->finalize = gtk_menu_shell_finalize;
227 object_class->dispose = gtk_menu_shell_dispose;
229 widget_class->realize = gtk_menu_shell_realize;
230 widget_class->button_press_event = gtk_menu_shell_button_press;
231 widget_class->button_release_event = gtk_menu_shell_button_release;
232 widget_class->grab_broken_event = gtk_menu_shell_grab_broken;
233 widget_class->key_press_event = gtk_menu_shell_key_press;
234 widget_class->enter_notify_event = gtk_menu_shell_enter_notify;
235 widget_class->leave_notify_event = gtk_menu_shell_leave_notify;
236 widget_class->screen_changed = gtk_menu_shell_screen_changed;
238 container_class->add = gtk_menu_shell_add;
239 container_class->remove = gtk_menu_shell_remove;
240 container_class->forall = gtk_menu_shell_forall;
241 container_class->child_type = gtk_menu_shell_child_type;
243 klass->submenu_placement = GTK_TOP_BOTTOM;
244 klass->deactivate = gtk_real_menu_shell_deactivate;
245 klass->selection_done = NULL;
246 klass->move_current = gtk_real_menu_shell_move_current;
247 klass->activate_current = gtk_real_menu_shell_activate_current;
248 klass->cancel = gtk_real_menu_shell_cancel;
249 klass->select_item = gtk_menu_shell_real_select_item;
250 klass->insert = gtk_menu_shell_real_insert;
251 klass->move_selected = gtk_menu_shell_real_move_selected;
254 * GtkMenuShell::deactivate:
255 * @menushell: the object which received the signal
257 * This signal is emitted when a menu shell is deactivated.
259 menu_shell_signals[DEACTIVATE] =
260 g_signal_new (I_("deactivate"),
261 G_OBJECT_CLASS_TYPE (object_class),
263 G_STRUCT_OFFSET (GtkMenuShellClass, deactivate),
265 _gtk_marshal_VOID__VOID,
269 * GtkMenuShell::selection-done:
270 * @menushell: the object which received the signal
272 * This signal is emitted when a selection has been
273 * completed within a menu shell.
275 menu_shell_signals[SELECTION_DONE] =
276 g_signal_new (I_("selection-done"),
277 G_OBJECT_CLASS_TYPE (object_class),
279 G_STRUCT_OFFSET (GtkMenuShellClass, selection_done),
281 _gtk_marshal_VOID__VOID,
285 * GtkMenuShell::move-current:
286 * @menushell: the object which received the signal
287 * @direction: the direction to move
289 * An keybinding signal which moves the current menu item
290 * in the direction specified by @direction.
292 menu_shell_signals[MOVE_CURRENT] =
293 g_signal_new (I_("move-current"),
294 G_OBJECT_CLASS_TYPE (object_class),
295 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
296 G_STRUCT_OFFSET (GtkMenuShellClass, move_current),
298 _gtk_marshal_VOID__ENUM,
300 GTK_TYPE_MENU_DIRECTION_TYPE);
303 * GtkMenuShell::activate-current:
304 * @menushell: the object which received the signal
305 * @force_hide: if %TRUE, hide the menu after activating the menu item
307 * An action signal that activates the current menu item within
310 menu_shell_signals[ACTIVATE_CURRENT] =
311 g_signal_new (I_("activate-current"),
312 G_OBJECT_CLASS_TYPE (object_class),
313 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
314 G_STRUCT_OFFSET (GtkMenuShellClass, activate_current),
316 _gtk_marshal_VOID__BOOLEAN,
321 * GtkMenuShell::cancel:
322 * @menushell: the object which received the signal
324 * An action signal which cancels the selection within the menu shell.
325 * Causes the #GtkMenuShell::selection-done signal to be emitted.
327 menu_shell_signals[CANCEL] =
328 g_signal_new (I_("cancel"),
329 G_OBJECT_CLASS_TYPE (object_class),
330 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
331 G_STRUCT_OFFSET (GtkMenuShellClass, cancel),
333 _gtk_marshal_VOID__VOID,
337 * GtkMenuShell::cycle-focus:
338 * @menushell: the object which received the signal
339 * @direction: the direction to cycle in
341 * A keybinding signal which moves the focus in the
344 menu_shell_signals[CYCLE_FOCUS] =
345 g_signal_new_class_handler (I_("cycle-focus"),
346 G_OBJECT_CLASS_TYPE (object_class),
347 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
348 G_CALLBACK (gtk_real_menu_shell_cycle_focus),
350 _gtk_marshal_VOID__ENUM,
352 GTK_TYPE_DIRECTION_TYPE);
355 * GtkMenuShell::move-selected:
356 * @menu_shell: the object on which the signal is emitted
357 * @distance: +1 to move to the next item, -1 to move to the previous
359 * The ::move-selected signal is emitted to move the selection to
362 * Returns: %TRUE to stop the signal emission, %FALSE to continue
366 menu_shell_signals[MOVE_SELECTED] =
367 g_signal_new (I_("move-selected"),
368 G_OBJECT_CLASS_TYPE (object_class),
370 G_STRUCT_OFFSET (GtkMenuShellClass, move_selected),
371 _gtk_boolean_handled_accumulator, NULL,
372 _gtk_marshal_BOOLEAN__INT,
377 * GtkMenuShell::insert:
378 * @menu_shell: the object on which the signal is emitted
379 * @child: the #GtkMenuItem that is being inserted
380 * @position: the position at which the insert occurs
382 * The ::insert signal is emitted when a new #GtkMenuItem is added to
383 * a #GtkMenuShell. A separate signal is used instead of
384 * GtkContainer::add because of the need for an additional position
387 * The inverse of this signal is the GtkContainer::removed signal.
391 menu_shell_signals[INSERT] =
392 g_signal_new (I_("insert"),
393 G_OBJECT_CLASS_TYPE (object_class),
395 G_STRUCT_OFFSET (GtkMenuShellClass, insert),
397 _gtk_marshal_VOID__OBJECT_INT,
398 G_TYPE_NONE, 2, GTK_TYPE_WIDGET, G_TYPE_INT);
401 binding_set = gtk_binding_set_by_class (klass);
402 gtk_binding_entry_add_signal (binding_set,
405 gtk_binding_entry_add_signal (binding_set,
407 "activate-current", 1,
410 gtk_binding_entry_add_signal (binding_set,
411 GDK_KEY_ISO_Enter, 0,
412 "activate-current", 1,
415 gtk_binding_entry_add_signal (binding_set,
417 "activate-current", 1,
420 gtk_binding_entry_add_signal (binding_set,
422 "activate-current", 1,
425 gtk_binding_entry_add_signal (binding_set,
427 "activate-current", 1,
430 gtk_binding_entry_add_signal (binding_set,
433 GTK_TYPE_DIRECTION_TYPE, GTK_DIR_TAB_FORWARD);
434 gtk_binding_entry_add_signal (binding_set,
435 GDK_KEY_F10, GDK_SHIFT_MASK,
437 GTK_TYPE_DIRECTION_TYPE, GTK_DIR_TAB_BACKWARD);
440 * GtkMenuShell:take-focus:
442 * A boolean that determines whether the menu and its submenus grab the
443 * keyboard focus. See gtk_menu_shell_set_take_focus() and
444 * gtk_menu_shell_get_take_focus().
448 g_object_class_install_property (object_class,
450 g_param_spec_boolean ("take-focus",
452 P_("A boolean that determines whether the menu grabs the keyboard focus"),
454 GTK_PARAM_READWRITE));
456 gtk_widget_class_set_accessible_type (widget_class, GTK_TYPE_MENU_SHELL_ACCESSIBLE);
458 g_type_class_add_private (object_class, sizeof (GtkMenuShellPrivate));
462 gtk_menu_shell_child_type (GtkContainer *container)
464 return GTK_TYPE_MENU_ITEM;
468 gtk_menu_shell_init (GtkMenuShell *menu_shell)
470 GtkMenuShellPrivate *priv;
472 priv = G_TYPE_INSTANCE_GET_PRIVATE (menu_shell,
474 GtkMenuShellPrivate);
475 menu_shell->priv = priv;
476 priv->take_focus = TRUE;
480 gtk_menu_shell_set_property (GObject *object,
485 GtkMenuShell *menu_shell = GTK_MENU_SHELL (object);
489 case PROP_TAKE_FOCUS:
490 gtk_menu_shell_set_take_focus (menu_shell, g_value_get_boolean (value));
493 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
499 gtk_menu_shell_get_property (GObject *object,
504 GtkMenuShell *menu_shell = GTK_MENU_SHELL (object);
508 case PROP_TAKE_FOCUS:
509 g_value_set_boolean (value, gtk_menu_shell_get_take_focus (menu_shell));
512 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
518 gtk_menu_shell_finalize (GObject *object)
520 GtkMenuShell *menu_shell = GTK_MENU_SHELL (object);
521 GtkMenuShellPrivate *priv = menu_shell->priv;
523 if (priv->mnemonic_hash)
524 _gtk_mnemonic_hash_free (priv->mnemonic_hash);
526 _gtk_key_hash_free (priv->key_hash);
528 G_OBJECT_CLASS (gtk_menu_shell_parent_class)->finalize (object);
533 gtk_menu_shell_dispose (GObject *object)
535 gtk_menu_shell_deactivate (GTK_MENU_SHELL (object));
537 G_OBJECT_CLASS (gtk_menu_shell_parent_class)->dispose (object);
541 * gtk_menu_shell_append:
542 * @menu_shell: a #GtkMenuShell
543 * @child: The #GtkMenuItem to add
545 * Adds a new #GtkMenuItem to the end of the menu shell's
549 gtk_menu_shell_append (GtkMenuShell *menu_shell,
552 gtk_menu_shell_insert (menu_shell, child, -1);
556 * gtk_menu_shell_prepend:
557 * @menu_shell: a #GtkMenuShell
558 * @child: The #GtkMenuItem to add
560 * Adds a new #GtkMenuItem to the beginning of the menu shell's
564 gtk_menu_shell_prepend (GtkMenuShell *menu_shell,
567 gtk_menu_shell_insert (menu_shell, child, 0);
571 * gtk_menu_shell_insert:
572 * @menu_shell: a #GtkMenuShell
573 * @child: The #GtkMenuItem to add
574 * @position: The position in the item list where @child
575 * is added. Positions are numbered from 0 to n-1
577 * Adds a new #GtkMenuItem to the menu shell's item list
578 * at the position indicated by @position.
581 gtk_menu_shell_insert (GtkMenuShell *menu_shell,
585 g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
586 g_return_if_fail (GTK_IS_MENU_ITEM (child));
588 g_signal_emit (menu_shell, menu_shell_signals[INSERT], 0, child, position);
592 gtk_menu_shell_real_insert (GtkMenuShell *menu_shell,
596 GtkMenuShellPrivate *priv = menu_shell->priv;
598 priv->children = g_list_insert (priv->children, child, position);
600 gtk_widget_set_parent (child, GTK_WIDGET (menu_shell));
604 * gtk_menu_shell_deactivate:
605 * @menu_shell: a #GtkMenuShell
607 * Deactivates the menu shell.
609 * Typically this results in the menu shell being erased
613 gtk_menu_shell_deactivate (GtkMenuShell *menu_shell)
615 g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
617 g_signal_emit (menu_shell, menu_shell_signals[DEACTIVATE], 0);
621 gtk_menu_shell_realize (GtkWidget *widget)
623 GtkAllocation allocation;
625 GdkWindowAttr attributes;
626 gint attributes_mask;
627 GtkStyleContext *context;
629 gtk_widget_set_realized (widget, TRUE);
631 gtk_widget_get_allocation (widget, &allocation);
633 attributes.x = allocation.x;
634 attributes.y = allocation.y;
635 attributes.width = allocation.width;
636 attributes.height = allocation.height;
637 attributes.window_type = GDK_WINDOW_CHILD;
638 attributes.wclass = GDK_INPUT_OUTPUT;
639 attributes.visual = gtk_widget_get_visual (widget);
640 attributes.event_mask = gtk_widget_get_events (widget);
641 attributes.event_mask |= (GDK_EXPOSURE_MASK |
642 GDK_BUTTON_PRESS_MASK |
643 GDK_BUTTON_RELEASE_MASK |
645 GDK_ENTER_NOTIFY_MASK |
646 GDK_LEAVE_NOTIFY_MASK);
648 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
650 window = gdk_window_new (gtk_widget_get_parent_window (widget),
651 &attributes, attributes_mask);
652 gtk_widget_set_window (widget, window);
653 gdk_window_set_user_data (window, widget);
655 context = gtk_widget_get_style_context (widget);
656 gtk_style_context_set_background (context, window);
660 gtk_menu_shell_activate (GtkMenuShell *menu_shell)
662 GtkMenuShellPrivate *priv = menu_shell->priv;
668 device = gtk_get_current_event_device ();
670 _gtk_menu_shell_set_grab_device (menu_shell, device);
671 gtk_device_grab_add (GTK_WIDGET (menu_shell), device, TRUE);
673 priv->have_grab = TRUE;
679 gtk_menu_shell_button_press (GtkWidget *widget,
680 GdkEventButton *event)
682 GtkMenuShell *menu_shell;
683 GtkMenuShellPrivate *priv;
684 GtkWidget *menu_item;
687 if (event->type != GDK_BUTTON_PRESS)
690 menu_shell = GTK_MENU_SHELL (widget);
691 priv = menu_shell->priv;
693 if (priv->parent_menu_shell)
694 return gtk_widget_event (priv->parent_menu_shell, (GdkEvent*) event);
696 menu_item = gtk_menu_shell_get_item (menu_shell, (GdkEvent *)event);
698 if (menu_item && _gtk_menu_item_is_selectable (menu_item))
700 parent = gtk_widget_get_parent (menu_item);
702 if (menu_item != GTK_MENU_SHELL (parent)->priv->active_menu_item)
704 /* select the menu item *before* activating the shell, so submenus
705 * which might be open are closed the friendly way. If we activate
706 * (and thus grab) this menu shell first, we might get grab_broken
707 * events which will close the entire menu hierarchy. Selecting the
708 * menu item also fixes up the state as if enter_notify() would
709 * have run before (which normally selects the item).
711 if (GTK_MENU_SHELL_GET_CLASS (parent)->submenu_placement != GTK_TOP_BOTTOM)
712 gtk_menu_shell_select_item (GTK_MENU_SHELL (parent), menu_item);
716 if (!priv->active || !priv->button)
718 gboolean initially_active = priv->active;
720 priv->button = event->button;
724 if (_gtk_menu_item_is_selectable (menu_item) &&
725 gtk_widget_get_parent (menu_item) == widget &&
726 menu_item != priv->active_menu_item)
728 gtk_menu_shell_activate (menu_shell);
729 priv->button = event->button;
731 if (GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement == GTK_TOP_BOTTOM)
733 priv->activate_time = event->time;
734 gtk_menu_shell_select_item (menu_shell, menu_item);
740 if (!initially_active)
742 gboolean window_drag = FALSE;
744 gtk_widget_style_get (widget,
745 "window-dragging", &window_drag,
750 gtk_menu_shell_deactivate (menu_shell);
751 gtk_window_begin_move_drag (GTK_WINDOW (gtk_widget_get_toplevel (widget)),
762 widget = gtk_get_event_widget ((GdkEvent*) event);
763 if (widget == GTK_WIDGET (menu_shell))
765 gtk_menu_shell_deactivate (menu_shell);
766 g_signal_emit (menu_shell, menu_shell_signals[SELECTION_DONE], 0);
771 _gtk_menu_item_is_selectable (menu_item) &&
772 GTK_MENU_ITEM (menu_item)->priv->submenu != NULL &&
773 !gtk_widget_get_visible (GTK_MENU_ITEM (menu_item)->priv->submenu))
775 _gtk_menu_item_popup_submenu (menu_item, FALSE);
776 priv->activated_submenu = TRUE;
783 gtk_menu_shell_grab_broken (GtkWidget *widget,
784 GdkEventGrabBroken *event)
786 GtkMenuShell *menu_shell = GTK_MENU_SHELL (widget);
787 GtkMenuShellPrivate *priv = menu_shell->priv;
789 if (priv->have_xgrab && event->grab_window == NULL)
791 /* Unset the active menu item so gtk_menu_popdown() doesn't see it. */
792 gtk_menu_shell_deselect (menu_shell);
793 gtk_menu_shell_deactivate (menu_shell);
794 g_signal_emit (menu_shell, menu_shell_signals[SELECTION_DONE], 0);
801 gtk_menu_shell_button_release (GtkWidget *widget,
802 GdkEventButton *event)
804 GtkMenuShell *menu_shell = GTK_MENU_SHELL (widget);
805 GtkMenuShellPrivate *priv = menu_shell->priv;
809 GtkWidget *menu_item;
810 gboolean deactivate = TRUE;
812 if (priv->button && (event->button != priv->button))
815 if (priv->parent_menu_shell)
816 return gtk_widget_event (priv->parent_menu_shell, (GdkEvent*) event);
820 menu_item = gtk_menu_shell_get_item (menu_shell, (GdkEvent*) event);
822 if ((event->time - priv->activate_time) > MENU_SHELL_TIMEOUT)
824 if (menu_item && (priv->active_menu_item == menu_item) &&
825 _gtk_menu_item_is_selectable (menu_item))
827 GtkWidget *submenu = GTK_MENU_ITEM (menu_item)->priv->submenu;
831 gtk_menu_shell_activate_item (menu_shell, menu_item, TRUE);
834 else if (GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement != GTK_TOP_BOTTOM ||
835 priv->activated_submenu)
838 GTimeVal *popup_time;
839 gint64 usec_since_popup = 0;
841 g_object_get (gtk_widget_get_settings (widget),
842 "gtk-menu-popdown-delay", &popdown_delay,
845 popup_time = g_object_get_data (G_OBJECT (submenu),
846 "gtk-menu-exact-popup-time");
850 GTimeVal current_time;
852 g_get_current_time (¤t_time);
854 usec_since_popup = ((gint64) current_time.tv_sec * 1000 * 1000 +
855 (gint64) current_time.tv_usec -
856 (gint64) popup_time->tv_sec * 1000 * 1000 -
857 (gint64) popup_time->tv_usec);
859 g_object_set_data (G_OBJECT (submenu),
860 "gtk-menu-exact-popup-time", NULL);
863 /* Only close the submenu on click if we opened the
864 * menu explicitely (usec_since_popup == 0) or
865 * enough time has passed since it was opened by
866 * GtkMenuItem's timeout (usec_since_popup > delay).
868 if (!priv->activated_submenu &&
869 (usec_since_popup == 0 ||
870 usec_since_popup > popdown_delay * 1000))
872 _gtk_menu_item_popdown_submenu (menu_item);
876 gtk_menu_item_select (GTK_MENU_ITEM (menu_item));
882 else if (menu_item &&
883 !_gtk_menu_item_is_selectable (menu_item) &&
884 GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement != GTK_TOP_BOTTOM)
888 else if (priv->parent_menu_shell)
891 gtk_widget_event (priv->parent_menu_shell, (GdkEvent*) event);
895 /* If we ended up on an item with a submenu, leave the menu up. */
897 (priv->active_menu_item == menu_item) &&
898 GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement != GTK_TOP_BOTTOM)
903 else /* a very fast press-release */
905 /* We only ever want to prevent deactivation on the first
906 * press/release. Setting the time to zero is a bit of a
907 * hack, since we could be being triggered in the first
908 * few fractions of a second after a server time wraparound.
909 * the chances of that happening are ~1/10^6, without
910 * serious harm if we lose.
912 priv->activate_time = 0;
918 gtk_menu_shell_deactivate (menu_shell);
919 g_signal_emit (menu_shell, menu_shell_signals[SELECTION_DONE], 0);
922 priv->activated_submenu = FALSE;
929 _gtk_menu_shell_set_keyboard_mode (GtkMenuShell *menu_shell,
930 gboolean keyboard_mode)
932 menu_shell->priv->keyboard_mode = keyboard_mode;
936 _gtk_menu_shell_get_keyboard_mode (GtkMenuShell *menu_shell)
938 return menu_shell->priv->keyboard_mode;
942 _gtk_menu_shell_update_mnemonics (GtkMenuShell *menu_shell)
944 GtkMenuShell *target;
945 gboolean auto_mnemonics;
947 gboolean mnemonics_visible;
949 g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu_shell)),
950 "gtk-auto-mnemonics", &auto_mnemonics,
960 GtkMenuShellPrivate *priv = target->priv;
961 GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (target));
963 /* The idea with keyboard mode is that once you start using
964 * the keyboard to navigate the menus, we show mnemonics
965 * until the menu navigation is over. To that end, we spread
966 * the keyboard mode upwards in the menu hierarchy here.
967 * Also see gtk_menu_popup, where we inherit it downwards.
969 if (menu_shell->priv->keyboard_mode)
970 target->priv->keyboard_mode = TRUE;
972 /* While navigating menus, the first parent menu with an active
973 * item is the one where mnemonics are effective, as can be seen
974 * in gtk_menu_shell_key_press below.
975 * We also show mnemonics in context menus. The grab condition is
976 * necessary to ensure we remove underlines from menu bars when
979 mnemonics_visible = target->priv->keyboard_mode &&
980 (((target->priv->active_menu_item || priv->in_unselectable_item) && !found) ||
981 (target == menu_shell &&
982 !target->priv->parent_menu_shell &&
983 gtk_widget_has_grab (GTK_WIDGET (target))));
985 /* While menus are up, only show underlines inside the menubar,
986 * not in the entire window.
988 if (GTK_IS_MENU_BAR (target))
990 gtk_window_set_mnemonics_visible (GTK_WINDOW (toplevel), FALSE);
991 _gtk_label_mnemonics_visible_apply_recursively (GTK_WIDGET (target),
995 gtk_window_set_mnemonics_visible (GTK_WINDOW (toplevel), mnemonics_visible);
997 if (target->priv->active_menu_item || priv->in_unselectable_item)
1000 target = GTK_MENU_SHELL (target->priv->parent_menu_shell);
1005 gtk_menu_shell_key_press (GtkWidget *widget,
1008 GtkMenuShell *menu_shell = GTK_MENU_SHELL (widget);
1009 GtkMenuShellPrivate *priv = menu_shell->priv;
1010 gboolean enable_mnemonics;
1012 priv->keyboard_mode = TRUE;
1014 if (!(priv->active_menu_item || priv->in_unselectable_item) &&
1015 priv->parent_menu_shell)
1016 return gtk_widget_event (priv->parent_menu_shell, (GdkEvent *)event);
1018 if (gtk_bindings_activate_event (G_OBJECT (widget), event))
1021 g_object_get (gtk_widget_get_settings (widget),
1022 "gtk-enable-mnemonics", &enable_mnemonics,
1025 if (enable_mnemonics)
1026 return gtk_menu_shell_activate_mnemonic (menu_shell, event);
1032 gtk_menu_shell_enter_notify (GtkWidget *widget,
1033 GdkEventCrossing *event)
1035 GtkMenuShell *menu_shell = GTK_MENU_SHELL (widget);
1036 GtkMenuShellPrivate *priv = menu_shell->priv;
1038 if (event->mode == GDK_CROSSING_GTK_GRAB ||
1039 event->mode == GDK_CROSSING_GTK_UNGRAB ||
1040 event->mode == GDK_CROSSING_STATE_CHANGED)
1045 GtkWidget *menu_item;
1048 menu_item = gtk_get_event_widget ((GdkEvent*) event);
1053 if (GTK_IS_MENU_ITEM (menu_item) &&
1054 !_gtk_menu_item_is_selectable (menu_item))
1056 priv->in_unselectable_item = TRUE;
1060 parent = gtk_widget_get_parent (menu_item);
1061 if (parent == widget &&
1062 GTK_IS_MENU_ITEM (menu_item))
1064 if (priv->ignore_enter)
1067 if (event->detail != GDK_NOTIFY_INFERIOR)
1069 if ((gtk_widget_get_state_flags (menu_item) & GTK_STATE_FLAG_PRELIGHT) == 0)
1070 gtk_menu_shell_select_item (menu_shell, menu_item);
1072 /* If any mouse button is down, and there is a submenu
1073 * that is not yet visible, activate it. It's sufficient
1074 * to check for any button's mask (not only the one
1075 * matching menu_shell->button), because there is no
1076 * situation a mouse button could be pressed while
1077 * entering a menu item where we wouldn't want to show
1080 if ((event->state & (GDK_BUTTON1_MASK|GDK_BUTTON2_MASK|GDK_BUTTON3_MASK)) &&
1081 GTK_MENU_ITEM (menu_item)->priv->submenu != NULL)
1083 GTK_MENU_SHELL (parent)->priv->activated_submenu = TRUE;
1085 if (!gtk_widget_get_visible (GTK_MENU_ITEM (menu_item)->priv->submenu))
1087 gboolean touchscreen_mode;
1089 g_object_get (gtk_widget_get_settings (widget),
1090 "gtk-touchscreen-mode", &touchscreen_mode,
1093 if (touchscreen_mode)
1094 _gtk_menu_item_popup_submenu (menu_item, TRUE);
1099 else if (priv->parent_menu_shell)
1101 gtk_widget_event (priv->parent_menu_shell, (GdkEvent*) event);
1109 gtk_menu_shell_leave_notify (GtkWidget *widget,
1110 GdkEventCrossing *event)
1112 if (event->mode == GDK_CROSSING_GTK_GRAB ||
1113 event->mode == GDK_CROSSING_GTK_GRAB ||
1114 event->mode == GDK_CROSSING_STATE_CHANGED)
1117 if (gtk_widget_get_visible (widget))
1119 GtkMenuShell *menu_shell = GTK_MENU_SHELL (widget);
1120 GtkMenuShellPrivate *priv = menu_shell->priv;
1121 GtkWidget *event_widget = gtk_get_event_widget ((GdkEvent*) event);
1122 GtkMenuItem *menu_item;
1124 if (!event_widget || !GTK_IS_MENU_ITEM (event_widget))
1127 menu_item = GTK_MENU_ITEM (event_widget);
1129 if (!_gtk_menu_item_is_selectable (event_widget))
1131 priv->in_unselectable_item = TRUE;
1135 if ((priv->active_menu_item == event_widget) &&
1136 (menu_item->priv->submenu == NULL))
1138 if ((event->detail != GDK_NOTIFY_INFERIOR) &&
1139 (gtk_widget_get_state_flags (GTK_WIDGET (menu_item)) & GTK_STATE_FLAG_PRELIGHT) != 0)
1141 gtk_menu_shell_deselect (menu_shell);
1144 else if (priv->parent_menu_shell)
1146 gtk_widget_event (priv->parent_menu_shell, (GdkEvent*) event);
1154 gtk_menu_shell_screen_changed (GtkWidget *widget,
1155 GdkScreen *previous_screen)
1157 gtk_menu_shell_reset_key_hash (GTK_MENU_SHELL (widget));
1161 gtk_menu_shell_add (GtkContainer *container,
1164 gtk_menu_shell_append (GTK_MENU_SHELL (container), widget);
1168 gtk_menu_shell_remove (GtkContainer *container,
1171 GtkMenuShell *menu_shell = GTK_MENU_SHELL (container);
1172 GtkMenuShellPrivate *priv = menu_shell->priv;
1175 was_visible = gtk_widget_get_visible (widget);
1176 priv->children = g_list_remove (priv->children, widget);
1178 if (widget == priv->active_menu_item)
1180 g_signal_emit_by_name (priv->active_menu_item, "deselect");
1181 priv->active_menu_item = NULL;
1184 gtk_widget_unparent (widget);
1186 /* Queue resize regardless of gtk_widget_get_visible (container),
1187 * since that's what is needed by toplevels.
1190 gtk_widget_queue_resize (GTK_WIDGET (container));
1194 gtk_menu_shell_forall (GtkContainer *container,
1195 gboolean include_internals,
1196 GtkCallback callback,
1197 gpointer callback_data)
1199 GtkMenuShell *menu_shell = GTK_MENU_SHELL (container);
1203 children = menu_shell->priv->children;
1206 child = children->data;
1207 children = children->next;
1209 (* callback) (child, callback_data);
1215 gtk_real_menu_shell_deactivate (GtkMenuShell *menu_shell)
1217 GtkMenuShellPrivate *priv = menu_shell->priv;
1222 priv->active = FALSE;
1223 priv->activate_time = 0;
1225 if (priv->active_menu_item)
1227 gtk_menu_item_deselect (GTK_MENU_ITEM (priv->active_menu_item));
1228 priv->active_menu_item = NULL;
1231 if (priv->have_grab)
1233 priv->have_grab = FALSE;
1234 gtk_device_grab_remove (GTK_WIDGET (menu_shell), priv->grab_pointer);
1236 if (priv->have_xgrab)
1238 GdkDevice *keyboard;
1240 gdk_device_ungrab (priv->grab_pointer, GDK_CURRENT_TIME);
1241 keyboard = gdk_device_get_associated_device (priv->grab_pointer);
1244 gdk_device_ungrab (keyboard, GDK_CURRENT_TIME);
1246 priv->have_xgrab = FALSE;
1249 priv->keyboard_mode = FALSE;
1250 _gtk_menu_shell_set_grab_device (menu_shell, NULL);
1252 _gtk_menu_shell_update_mnemonics (menu_shell);
1257 gtk_menu_shell_is_item (GtkMenuShell *menu_shell,
1262 g_return_val_if_fail (GTK_IS_MENU_SHELL (menu_shell), FALSE);
1263 g_return_val_if_fail (child != NULL, FALSE);
1265 parent = gtk_widget_get_parent (child);
1266 while (GTK_IS_MENU_SHELL (parent))
1268 if (parent == (GtkWidget*) menu_shell)
1270 parent = GTK_MENU_SHELL (parent)->priv->parent_menu_shell;
1277 gtk_menu_shell_get_item (GtkMenuShell *menu_shell,
1280 GtkWidget *menu_item;
1282 menu_item = gtk_get_event_widget ((GdkEvent*) event);
1284 while (menu_item && !GTK_IS_MENU_ITEM (menu_item))
1285 menu_item = gtk_widget_get_parent (menu_item);
1287 if (menu_item && gtk_menu_shell_is_item (menu_shell, menu_item))
1293 /* Handlers for action signals */
1296 * gtk_menu_shell_select_item:
1297 * @menu_shell: a #GtkMenuShell
1298 * @menu_item: The #GtkMenuItem to select
1300 * Selects the menu item from the menu shell.
1303 gtk_menu_shell_select_item (GtkMenuShell *menu_shell,
1304 GtkWidget *menu_item)
1306 GtkMenuShellPrivate *priv = menu_shell->priv;
1307 GtkMenuShellClass *class;
1309 g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
1310 g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
1312 class = GTK_MENU_SHELL_GET_CLASS (menu_shell);
1314 if (class->select_item &&
1316 priv->active_menu_item == menu_item))
1317 class->select_item (menu_shell, menu_item);
1320 void _gtk_menu_item_set_placement (GtkMenuItem *menu_item,
1321 GtkSubmenuPlacement placement);
1324 gtk_menu_shell_real_select_item (GtkMenuShell *menu_shell,
1325 GtkWidget *menu_item)
1327 GtkMenuShellPrivate *priv = menu_shell->priv;
1328 GtkPackDirection pack_dir = PACK_DIRECTION (menu_shell);
1330 if (priv->active_menu_item)
1332 gtk_menu_item_deselect (GTK_MENU_ITEM (priv->active_menu_item));
1333 priv->active_menu_item = NULL;
1336 if (!_gtk_menu_item_is_selectable (menu_item))
1338 priv->in_unselectable_item = TRUE;
1339 _gtk_menu_shell_update_mnemonics (menu_shell);
1343 gtk_menu_shell_activate (menu_shell);
1345 priv->active_menu_item = menu_item;
1346 if (pack_dir == GTK_PACK_DIRECTION_TTB || pack_dir == GTK_PACK_DIRECTION_BTT)
1347 _gtk_menu_item_set_placement (GTK_MENU_ITEM (priv->active_menu_item),
1350 _gtk_menu_item_set_placement (GTK_MENU_ITEM (priv->active_menu_item),
1351 GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement);
1352 gtk_menu_item_select (GTK_MENU_ITEM (priv->active_menu_item));
1354 _gtk_menu_shell_update_mnemonics (menu_shell);
1356 /* This allows the bizarre radio buttons-with-submenus-display-history
1359 if (GTK_MENU_ITEM (priv->active_menu_item)->priv->submenu)
1360 gtk_widget_activate (priv->active_menu_item);
1364 * gtk_menu_shell_deselect:
1365 * @menu_shell: a #GtkMenuShell
1367 * Deselects the currently selected item from the menu shell,
1371 gtk_menu_shell_deselect (GtkMenuShell *menu_shell)
1373 GtkMenuShellPrivate *priv = menu_shell->priv;
1375 g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
1377 if (priv->active_menu_item)
1379 gtk_menu_item_deselect (GTK_MENU_ITEM (priv->active_menu_item));
1380 priv->active_menu_item = NULL;
1381 _gtk_menu_shell_update_mnemonics (menu_shell);
1386 * gtk_menu_shell_activate_item:
1387 * @menu_shell: a #GtkMenuShell
1388 * @menu_item: the #GtkMenuItem to activate
1389 * @force_deactivate: if %TRUE, force the deactivation of the
1390 * menu shell after the menu item is activated
1392 * Activates the menu item within the menu shell.
1395 gtk_menu_shell_activate_item (GtkMenuShell *menu_shell,
1396 GtkWidget *menu_item,
1397 gboolean force_deactivate)
1399 GSList *slist, *shells = NULL;
1400 gboolean deactivate = force_deactivate;
1402 g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
1403 g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
1406 deactivate = GTK_MENU_ITEM_GET_CLASS (menu_item)->hide_on_activate;
1408 g_object_ref (menu_shell);
1409 g_object_ref (menu_item);
1413 GtkMenuShell *parent_menu_shell = menu_shell;
1417 g_object_ref (parent_menu_shell);
1418 shells = g_slist_prepend (shells, parent_menu_shell);
1419 parent_menu_shell = (GtkMenuShell*) parent_menu_shell->priv->parent_menu_shell;
1421 while (parent_menu_shell);
1422 shells = g_slist_reverse (shells);
1424 gtk_menu_shell_deactivate (menu_shell);
1426 /* Flush the x-queue, so any grabs are removed and
1427 * the menu is actually taken down
1429 gdk_display_sync (gtk_widget_get_display (menu_item));
1432 gtk_widget_activate (menu_item);
1434 for (slist = shells; slist; slist = slist->next)
1436 g_signal_emit (slist->data, menu_shell_signals[SELECTION_DONE], 0);
1437 g_object_unref (slist->data);
1439 g_slist_free (shells);
1441 g_object_unref (menu_shell);
1442 g_object_unref (menu_item);
1445 /* Distance should be +/- 1 */
1447 gtk_menu_shell_real_move_selected (GtkMenuShell *menu_shell,
1450 GtkMenuShellPrivate *priv = menu_shell->priv;
1452 if (priv->active_menu_item)
1454 GList *node = g_list_find (priv->children, priv->active_menu_item);
1455 GList *start_node = node;
1456 gboolean wrap_around;
1458 g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu_shell)),
1459 "gtk-keynav-wrap-around", &wrap_around,
1465 while (node != start_node &&
1466 (!node || !_gtk_menu_item_is_selectable (node->data)))
1470 else if (wrap_around)
1471 node = priv->children;
1474 gtk_widget_error_bell (GTK_WIDGET (menu_shell));
1482 while (node != start_node &&
1483 (!node || !_gtk_menu_item_is_selectable (node->data)))
1487 else if (wrap_around)
1488 node = g_list_last (priv->children);
1491 gtk_widget_error_bell (GTK_WIDGET (menu_shell));
1498 gtk_menu_shell_select_item (menu_shell, node->data);
1504 /* Distance should be +/- 1 */
1506 gtk_menu_shell_move_selected (GtkMenuShell *menu_shell,
1509 gboolean handled = FALSE;
1511 g_signal_emit (menu_shell, menu_shell_signals[MOVE_SELECTED], 0,
1512 distance, &handled);
1516 * gtk_menu_shell_select_first:
1517 * @menu_shell: a #GtkMenuShell
1518 * @search_sensitive: if %TRUE, search for the first selectable
1519 * menu item, otherwise select nothing if
1520 * the first item isn't sensitive. This
1521 * should be %FALSE if the menu is being
1522 * popped up initially.
1524 * Select the first visible or selectable child of the menu shell;
1525 * don't select tearoff items unless the only item is a tearoff
1531 gtk_menu_shell_select_first (GtkMenuShell *menu_shell,
1532 gboolean search_sensitive)
1534 GtkMenuShellPrivate *priv = menu_shell->priv;
1535 GtkWidget *to_select = NULL;
1538 tmp_list = priv->children;
1541 GtkWidget *child = tmp_list->data;
1543 if ((!search_sensitive && gtk_widget_get_visible (child)) ||
1544 _gtk_menu_item_is_selectable (child))
1547 if (!GTK_IS_TEAROFF_MENU_ITEM (child))
1551 tmp_list = tmp_list->next;
1555 gtk_menu_shell_select_item (menu_shell, to_select);
1559 _gtk_menu_shell_select_last (GtkMenuShell *menu_shell,
1560 gboolean search_sensitive)
1562 GtkMenuShellPrivate *priv = menu_shell->priv;
1563 GtkWidget *to_select = NULL;
1566 tmp_list = g_list_last (priv->children);
1569 GtkWidget *child = tmp_list->data;
1571 if ((!search_sensitive && gtk_widget_get_visible (child)) ||
1572 _gtk_menu_item_is_selectable (child))
1575 if (!GTK_IS_TEAROFF_MENU_ITEM (child))
1579 tmp_list = tmp_list->prev;
1583 gtk_menu_shell_select_item (menu_shell, to_select);
1587 gtk_menu_shell_select_submenu_first (GtkMenuShell *menu_shell)
1589 GtkMenuShellPrivate *priv = menu_shell->priv;
1590 GtkMenuItem *menu_item;
1592 if (priv->active_menu_item == NULL)
1595 menu_item = GTK_MENU_ITEM (priv->active_menu_item);
1597 if (menu_item->priv->submenu)
1599 _gtk_menu_item_popup_submenu (GTK_WIDGET (menu_item), FALSE);
1600 gtk_menu_shell_select_first (GTK_MENU_SHELL (menu_item->priv->submenu), TRUE);
1601 if (GTK_MENU_SHELL (menu_item->priv->submenu)->priv->active_menu_item)
1609 gtk_real_menu_shell_move_current (GtkMenuShell *menu_shell,
1610 GtkMenuDirectionType direction)
1612 GtkMenuShellPrivate *priv = menu_shell->priv;
1613 GtkMenuShell *parent_menu_shell = NULL;
1614 gboolean had_selection;
1615 gboolean touchscreen_mode;
1617 priv->in_unselectable_item = FALSE;
1619 had_selection = priv->active_menu_item != NULL;
1621 g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu_shell)),
1622 "gtk-touchscreen-mode", &touchscreen_mode,
1625 if (priv->parent_menu_shell)
1626 parent_menu_shell = GTK_MENU_SHELL (priv->parent_menu_shell);
1630 case GTK_MENU_DIR_PARENT:
1631 if (touchscreen_mode &&
1632 priv->active_menu_item &&
1633 GTK_MENU_ITEM (priv->active_menu_item)->priv->submenu &&
1634 gtk_widget_get_visible (GTK_MENU_ITEM (priv->active_menu_item)->priv->submenu))
1636 /* if we are on a menu item that has an open submenu but the
1637 * focus is not in that submenu (e.g. because it's empty or
1638 * has only insensitive items), close that submenu instead of
1639 * running into the code below which would close *this* menu.
1641 _gtk_menu_item_popdown_submenu (priv->active_menu_item);
1642 _gtk_menu_shell_update_mnemonics (menu_shell);
1644 else if (parent_menu_shell)
1646 if (touchscreen_mode)
1648 /* close menu when returning from submenu. */
1649 _gtk_menu_item_popdown_submenu (GTK_MENU (menu_shell)->priv->parent_menu_item);
1650 _gtk_menu_shell_update_mnemonics (parent_menu_shell);
1654 if (GTK_MENU_SHELL_GET_CLASS (parent_menu_shell)->submenu_placement ==
1655 GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement)
1656 gtk_menu_shell_deselect (menu_shell);
1659 if (PACK_DIRECTION (parent_menu_shell) == GTK_PACK_DIRECTION_LTR)
1660 gtk_menu_shell_move_selected (parent_menu_shell, -1);
1662 gtk_menu_shell_move_selected (parent_menu_shell, 1);
1663 gtk_menu_shell_select_submenu_first (parent_menu_shell);
1666 /* If there is no parent and the submenu is in the opposite direction
1667 * to the menu, then make the PARENT direction wrap around to
1668 * the bottom of the submenu.
1670 else if (priv->active_menu_item &&
1671 _gtk_menu_item_is_selectable (priv->active_menu_item) &&
1672 GTK_MENU_ITEM (priv->active_menu_item)->priv->submenu)
1674 GtkMenuShell *submenu = GTK_MENU_SHELL (GTK_MENU_ITEM (priv->active_menu_item)->priv->submenu);
1676 if (GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement !=
1677 GTK_MENU_SHELL_GET_CLASS (submenu)->submenu_placement)
1678 _gtk_menu_shell_select_last (submenu, TRUE);
1682 case GTK_MENU_DIR_CHILD:
1683 if (priv->active_menu_item &&
1684 _gtk_menu_item_is_selectable (priv->active_menu_item) &&
1685 GTK_MENU_ITEM (priv->active_menu_item)->priv->submenu)
1687 if (gtk_menu_shell_select_submenu_first (menu_shell))
1691 /* Try to find a menu running the opposite direction */
1692 while (parent_menu_shell &&
1693 (GTK_MENU_SHELL_GET_CLASS (parent_menu_shell)->submenu_placement ==
1694 GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement))
1696 parent_menu_shell = GTK_MENU_SHELL (parent_menu_shell->priv->parent_menu_shell);
1699 if (parent_menu_shell)
1701 if (PACK_DIRECTION (parent_menu_shell) == GTK_PACK_DIRECTION_LTR)
1702 gtk_menu_shell_move_selected (parent_menu_shell, 1);
1704 gtk_menu_shell_move_selected (parent_menu_shell, -1);
1706 gtk_menu_shell_select_submenu_first (parent_menu_shell);
1710 case GTK_MENU_DIR_PREV:
1711 gtk_menu_shell_move_selected (menu_shell, -1);
1712 if (!had_selection && !priv->active_menu_item && priv->children)
1713 _gtk_menu_shell_select_last (menu_shell, TRUE);
1716 case GTK_MENU_DIR_NEXT:
1717 gtk_menu_shell_move_selected (menu_shell, 1);
1718 if (!had_selection && !priv->active_menu_item && priv->children)
1719 gtk_menu_shell_select_first (menu_shell, TRUE);
1725 gtk_real_menu_shell_activate_current (GtkMenuShell *menu_shell,
1726 gboolean force_hide)
1728 GtkMenuShellPrivate *priv = menu_shell->priv;
1730 if (priv->active_menu_item &&
1731 _gtk_menu_item_is_selectable (priv->active_menu_item))
1733 if (GTK_MENU_ITEM (priv->active_menu_item)->priv->submenu == NULL)
1734 gtk_menu_shell_activate_item (menu_shell,
1735 priv->active_menu_item,
1738 _gtk_menu_item_popup_submenu (priv->active_menu_item, FALSE);
1743 gtk_real_menu_shell_cancel (GtkMenuShell *menu_shell)
1745 /* Unset the active menu item so gtk_menu_popdown() doesn't see it. */
1746 gtk_menu_shell_deselect (menu_shell);
1747 gtk_menu_shell_deactivate (menu_shell);
1748 g_signal_emit (menu_shell, menu_shell_signals[SELECTION_DONE], 0);
1752 gtk_real_menu_shell_cycle_focus (GtkMenuShell *menu_shell,
1753 GtkDirectionType dir)
1755 GtkMenuShellPrivate *priv = menu_shell->priv;
1757 while (menu_shell && !GTK_IS_MENU_BAR (menu_shell))
1759 if (priv->parent_menu_shell)
1760 menu_shell = GTK_MENU_SHELL (priv->parent_menu_shell);
1766 _gtk_menu_bar_cycle_focus (GTK_MENU_BAR (menu_shell), dir);
1770 _gtk_menu_shell_get_popup_delay (GtkMenuShell *menu_shell)
1772 GtkMenuShellClass *klass = GTK_MENU_SHELL_GET_CLASS (menu_shell);
1774 if (klass->get_popup_delay)
1776 return klass->get_popup_delay (menu_shell);
1781 GtkWidget *widget = GTK_WIDGET (menu_shell);
1783 g_object_get (gtk_widget_get_settings (widget),
1784 "gtk-menu-popup-delay", &popup_delay,
1792 * gtk_menu_shell_cancel:
1793 * @menu_shell: a #GtkMenuShell
1795 * Cancels the selection within the menu shell.
1800 gtk_menu_shell_cancel (GtkMenuShell *menu_shell)
1802 g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
1804 g_signal_emit (menu_shell, menu_shell_signals[CANCEL], 0);
1807 static GtkMnemonicHash *
1808 gtk_menu_shell_get_mnemonic_hash (GtkMenuShell *menu_shell,
1811 GtkMenuShellPrivate *priv = menu_shell->priv;
1813 if (!priv->mnemonic_hash && create)
1814 priv->mnemonic_hash = _gtk_mnemonic_hash_new ();
1816 return priv->mnemonic_hash;
1820 menu_shell_add_mnemonic_foreach (guint keyval,
1824 GtkKeyHash *key_hash = data;
1826 _gtk_key_hash_add_entry (key_hash, keyval, 0, GUINT_TO_POINTER (keyval));
1830 gtk_menu_shell_get_key_hash (GtkMenuShell *menu_shell,
1833 GtkMenuShellPrivate *priv = menu_shell->priv;
1834 GtkWidget *widget = GTK_WIDGET (menu_shell);
1836 if (!priv->key_hash && create && gtk_widget_has_screen (widget))
1838 GtkMnemonicHash *mnemonic_hash = gtk_menu_shell_get_mnemonic_hash (menu_shell, FALSE);
1839 GdkScreen *screen = gtk_widget_get_screen (widget);
1840 GdkKeymap *keymap = gdk_keymap_get_for_display (gdk_screen_get_display (screen));
1845 priv->key_hash = _gtk_key_hash_new (keymap, NULL);
1847 _gtk_mnemonic_hash_foreach (mnemonic_hash,
1848 menu_shell_add_mnemonic_foreach,
1852 return priv->key_hash;
1856 gtk_menu_shell_reset_key_hash (GtkMenuShell *menu_shell)
1858 GtkMenuShellPrivate *priv = menu_shell->priv;
1862 _gtk_key_hash_free (priv->key_hash);
1863 priv->key_hash = NULL;
1868 gtk_menu_shell_activate_mnemonic (GtkMenuShell *menu_shell,
1871 GtkMnemonicHash *mnemonic_hash;
1872 GtkKeyHash *key_hash;
1874 gboolean result = FALSE;
1876 mnemonic_hash = gtk_menu_shell_get_mnemonic_hash (menu_shell, FALSE);
1880 key_hash = gtk_menu_shell_get_key_hash (menu_shell, TRUE);
1884 entries = _gtk_key_hash_lookup (key_hash,
1885 event->hardware_keycode,
1887 gtk_accelerator_get_default_mod_mask (),
1892 result = _gtk_mnemonic_hash_activate (mnemonic_hash,
1893 GPOINTER_TO_UINT (entries->data));
1894 g_slist_free (entries);
1901 _gtk_menu_shell_add_mnemonic (GtkMenuShell *menu_shell,
1905 g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
1906 g_return_if_fail (GTK_IS_WIDGET (target));
1908 _gtk_mnemonic_hash_add (gtk_menu_shell_get_mnemonic_hash (menu_shell, TRUE),
1910 gtk_menu_shell_reset_key_hash (menu_shell);
1914 _gtk_menu_shell_remove_mnemonic (GtkMenuShell *menu_shell,
1918 g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
1919 g_return_if_fail (GTK_IS_WIDGET (target));
1921 _gtk_mnemonic_hash_remove (gtk_menu_shell_get_mnemonic_hash (menu_shell, TRUE),
1923 gtk_menu_shell_reset_key_hash (menu_shell);
1927 _gtk_menu_shell_set_grab_device (GtkMenuShell *menu_shell,
1930 GtkMenuShellPrivate *priv = menu_shell->priv;
1932 g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
1933 g_return_if_fail (device == NULL || GDK_IS_DEVICE (device));
1936 priv->grab_pointer = NULL;
1937 else if (gdk_device_get_source (device) == GDK_SOURCE_KEYBOARD)
1938 priv->grab_pointer = gdk_device_get_associated_device (device);
1940 priv->grab_pointer = device;
1944 _gtk_menu_shell_get_grab_device (GtkMenuShell *menu_shell)
1946 g_return_val_if_fail (GTK_IS_MENU_SHELL (menu_shell), NULL);
1948 return menu_shell->priv->grab_pointer;
1952 * gtk_menu_shell_get_take_focus:
1953 * @menu_shell: a #GtkMenuShell
1955 * Returns %TRUE if the menu shell will take the keyboard focus on popup.
1957 * Returns: %TRUE if the menu shell will take the keyboard focus on popup.
1962 gtk_menu_shell_get_take_focus (GtkMenuShell *menu_shell)
1964 g_return_val_if_fail (GTK_IS_MENU_SHELL (menu_shell), FALSE);
1966 return menu_shell->priv->take_focus;
1970 * gtk_menu_shell_set_take_focus:
1971 * @menu_shell: a #GtkMenuShell
1972 * @take_focus: %TRUE if the menu shell should take the keyboard
1975 * If @take_focus is %TRUE (the default) the menu shell will take
1976 * the keyboard focus so that it will receive all keyboard events
1977 * which is needed to enable keyboard navigation in menus.
1979 * Setting @take_focus to %FALSE is useful only for special applications
1980 * like virtual keyboard implementations which should not take keyboard
1983 * The @take_focus state of a menu or menu bar is automatically
1984 * propagated to submenus whenever a submenu is popped up, so you
1985 * don't have to worry about recursively setting it for your entire
1986 * menu hierarchy. Only when programmatically picking a submenu and
1987 * popping it up manually, the @take_focus property of the submenu
1988 * needs to be set explicitely.
1990 * Note that setting it to %FALSE has side-effects:
1992 * If the focus is in some other app, it keeps the focus and keynav in
1993 * the menu doesn't work. Consequently, keynav on the menu will only
1994 * work if the focus is on some toplevel owned by the onscreen keyboard.
1996 * To avoid confusing the user, menus with @take_focus set to %FALSE
1997 * should not display mnemonics or accelerators, since it cannot be
1998 * guaranteed that they will work.
2000 * See also gdk_keyboard_grab()
2005 gtk_menu_shell_set_take_focus (GtkMenuShell *menu_shell,
2006 gboolean take_focus)
2008 GtkMenuShellPrivate *priv = menu_shell->priv;
2010 g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
2012 if (priv->take_focus != take_focus)
2014 priv->take_focus = take_focus;
2015 g_object_notify (G_OBJECT (menu_shell), "take-focus");
2020 * gtk_menu_shell_get_selected_item:
2021 * @menu_shell: a #GtkMenuShell
2023 * Gets the currently selected item.
2025 * Returns: (transfer none): the currently selected item
2030 gtk_menu_shell_get_selected_item (GtkMenuShell *menu_shell)
2032 g_return_val_if_fail (GTK_IS_MENU_SHELL (menu_shell), NULL);
2034 return menu_shell->priv->active_menu_item;
2038 * gtk_menu_shell_get_parent_shell:
2039 * @menu_shell: a #GtkMenuShell
2041 * Gets the parent menu shell.
2043 * The parent menu shell of a submenu is the #GtkMenu or #GtkMenuBar
2044 * from which it was opened up.
2046 * Returns: (transfer none): the parent #GtkMenuShell
2051 gtk_menu_shell_get_parent_shell (GtkMenuShell *menu_shell)
2053 g_return_val_if_fail (GTK_IS_MENU_SHELL (menu_shell), NULL);
2055 return menu_shell->priv->parent_menu_shell;