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/.
27 #define GTK_MENU_INTERNALS
29 #include "gdk/gdkkeysyms.h"
30 #include "gtkbindings.h"
32 #include "gtkmarshalers.h"
33 #include "gtkmenubar.h"
34 #include "gtkmenuitem.h"
35 #include "gtkmenushell.h"
36 #include "gtktearoffmenuitem.h"
37 #include "gtkwindow.h"
39 #define MENU_SHELL_TIMEOUT 500
51 typedef void (*GtkMenuShellSignal1) (GtkObject *object,
52 GtkMenuDirectionType arg1,
54 typedef void (*GtkMenuShellSignal2) (GtkObject *object,
60 * A menu item can be "selected", this means that it is displayed
61 * in the prelight state, and if it has a submenu, that submenu
64 * A menu is "active" when it is visible onscreen and the user
65 * is selecting from it. A menubar is not active until the user
66 * clicks on one of its menuitems. When a menu is active,
67 * passing the mouse over a submenu will pop it up.
69 * menu_shell->active_menu_item, is however, not an "active"
70 * menu item (there is no such thing) but rather, the selected
71 * menu item in that MenuShell, if there is one.
73 * There is also is a concept of the current menu and a current
74 * menu item. The current menu item is the selected menu item
75 * that is furthest down in the heirarchy. (Every active menu_shell
76 * does not necessarily contain a selected menu item, but if
77 * it does, then menu_shell->parent_menu_shell must also contain
78 * a selected menu item. The current menu is the menu that
79 * contains the current menu_item. It will always have a GTK
80 * grab and receive all key presses.
85 * ::move_current (GtkMenuDirection *dir)
86 * Moves the current menu item in direction 'dir':
88 * GTK_MENU_DIR_PARENT: To the parent menu shell
89 * GTK_MENU_DIR_CHILD: To the child menu shell (if this item has
91 * GTK_MENU_DIR_NEXT/PREV: To the next or previous item
94 * As a a bit of a hack to get movement between menus and
95 * menubars working, if submenu_placement is different for
96 * the menu and its MenuShell then the following apply:
98 * - For 'parent' the current menu is not just moved to
99 * the parent, but moved to the previous entry in the parent
100 * - For 'child', if there is no child, then current is
101 * moved to the next item in the parent.
104 * ::activate_current (GBoolean *force_hide)
105 * Activate the current item. If 'force_hide' is true, hide
106 * the current menu item always. Otherwise, only hide
107 * it if menu_item->klass->hide_on_activate is true.
110 * Cancels the current selection
113 static void gtk_menu_shell_class_init (GtkMenuShellClass *klass);
114 static void gtk_menu_shell_init (GtkMenuShell *menu_shell);
115 static void gtk_menu_shell_realize (GtkWidget *widget);
116 static gint gtk_menu_shell_button_press (GtkWidget *widget,
117 GdkEventButton *event);
118 static gint gtk_menu_shell_button_release (GtkWidget *widget,
119 GdkEventButton *event);
120 static gint gtk_menu_shell_key_press (GtkWidget *widget,
122 static gint gtk_menu_shell_enter_notify (GtkWidget *widget,
123 GdkEventCrossing *event);
124 static gint gtk_menu_shell_leave_notify (GtkWidget *widget,
125 GdkEventCrossing *event);
126 static void gtk_menu_shell_add (GtkContainer *container,
128 static void gtk_menu_shell_remove (GtkContainer *container,
130 static void gtk_menu_shell_forall (GtkContainer *container,
131 gboolean include_internals,
132 GtkCallback callback,
133 gpointer callback_data);
134 static void gtk_menu_shell_real_insert (GtkMenuShell *menu_shell,
137 static void gtk_real_menu_shell_deactivate (GtkMenuShell *menu_shell);
138 static gint gtk_menu_shell_is_item (GtkMenuShell *menu_shell,
140 static GtkWidget *gtk_menu_shell_get_item (GtkMenuShell *menu_shell,
142 static GType gtk_menu_shell_child_type (GtkContainer *container);
143 static void gtk_menu_shell_real_select_item (GtkMenuShell *menu_shell,
144 GtkWidget *menu_item);
145 static void gtk_menu_shell_select_submenu_first (GtkMenuShell *menu_shell);
147 static void gtk_real_menu_shell_move_current (GtkMenuShell *menu_shell,
148 GtkMenuDirectionType direction);
149 static void gtk_real_menu_shell_activate_current (GtkMenuShell *menu_shell,
150 gboolean force_hide);
151 static void gtk_real_menu_shell_cancel (GtkMenuShell *menu_shell);
152 static void gtk_real_menu_shell_cycle_focus (GtkMenuShell *menu_shell,
153 GtkDirectionType dir);
155 static GtkContainerClass *parent_class = NULL;
156 static guint menu_shell_signals[LAST_SIGNAL] = { 0 };
160 gtk_menu_shell_get_type (void)
162 static GType menu_shell_type = 0;
164 if (!menu_shell_type)
166 static const GTypeInfo menu_shell_info =
168 sizeof (GtkMenuShellClass),
169 NULL, /* base_init */
170 NULL, /* base_finalize */
171 (GClassInitFunc) gtk_menu_shell_class_init,
172 NULL, /* class_finalize */
173 NULL, /* class_data */
174 sizeof (GtkMenuShell),
176 (GInstanceInitFunc) gtk_menu_shell_init,
177 NULL, /* value_table */
181 g_type_register_static (GTK_TYPE_CONTAINER, "GtkMenuShell",
182 &menu_shell_info, G_TYPE_FLAG_ABSTRACT);
185 return menu_shell_type;
189 binding_signal_new (const gchar *signal_name,
191 GSignalFlags signal_flags,
193 GSignalAccumulator accumulator,
195 GSignalCMarshaller c_marshaller,
203 g_return_val_if_fail (signal_name != NULL, 0);
205 va_start (args, n_params);
207 signal_id = g_signal_new_valist (signal_name, itype, signal_flags,
208 g_cclosure_new (handler, NULL, NULL),
209 accumulator, accu_data, c_marshaller,
210 return_type, n_params, args);
218 gtk_menu_shell_class_init (GtkMenuShellClass *klass)
220 GObjectClass *object_class;
221 GtkWidgetClass *widget_class;
222 GtkContainerClass *container_class;
224 GtkBindingSet *binding_set;
226 object_class = (GObjectClass*) klass;
227 widget_class = (GtkWidgetClass*) klass;
228 container_class = (GtkContainerClass*) klass;
230 parent_class = g_type_class_peek_parent (klass);
232 widget_class->realize = gtk_menu_shell_realize;
233 widget_class->button_press_event = gtk_menu_shell_button_press;
234 widget_class->button_release_event = gtk_menu_shell_button_release;
235 widget_class->key_press_event = gtk_menu_shell_key_press;
236 widget_class->enter_notify_event = gtk_menu_shell_enter_notify;
237 widget_class->leave_notify_event = gtk_menu_shell_leave_notify;
239 container_class->add = gtk_menu_shell_add;
240 container_class->remove = gtk_menu_shell_remove;
241 container_class->forall = gtk_menu_shell_forall;
242 container_class->child_type = gtk_menu_shell_child_type;
244 klass->submenu_placement = GTK_TOP_BOTTOM;
245 klass->deactivate = gtk_real_menu_shell_deactivate;
246 klass->selection_done = NULL;
247 klass->move_current = gtk_real_menu_shell_move_current;
248 klass->activate_current = gtk_real_menu_shell_activate_current;
249 klass->cancel = gtk_real_menu_shell_cancel;
250 klass->select_item = gtk_menu_shell_real_select_item;
251 klass->insert = gtk_menu_shell_real_insert;
253 menu_shell_signals[DEACTIVATE] =
254 g_signal_new ("deactivate",
255 G_OBJECT_CLASS_TYPE (object_class),
257 G_STRUCT_OFFSET (GtkMenuShellClass, deactivate),
259 _gtk_marshal_VOID__VOID,
261 menu_shell_signals[SELECTION_DONE] =
262 g_signal_new ("selection-done",
263 G_OBJECT_CLASS_TYPE (object_class),
265 G_STRUCT_OFFSET (GtkMenuShellClass, selection_done),
267 _gtk_marshal_VOID__VOID,
269 menu_shell_signals[MOVE_CURRENT] =
270 g_signal_new ("move_current",
271 G_OBJECT_CLASS_TYPE (object_class),
272 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
273 G_STRUCT_OFFSET (GtkMenuShellClass, move_current),
275 _gtk_marshal_VOID__ENUM,
277 GTK_TYPE_MENU_DIRECTION_TYPE);
278 menu_shell_signals[ACTIVATE_CURRENT] =
279 g_signal_new ("activate_current",
280 G_OBJECT_CLASS_TYPE (object_class),
281 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
282 G_STRUCT_OFFSET (GtkMenuShellClass, activate_current),
284 _gtk_marshal_VOID__BOOLEAN,
287 menu_shell_signals[CANCEL] =
288 g_signal_new ("cancel",
289 G_OBJECT_CLASS_TYPE (object_class),
290 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
291 G_STRUCT_OFFSET (GtkMenuShellClass, cancel),
293 _gtk_marshal_VOID__VOID,
295 menu_shell_signals[CYCLE_FOCUS] =
296 binding_signal_new ("cycle_focus",
297 G_OBJECT_CLASS_TYPE (object_class),
298 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
299 G_CALLBACK (gtk_real_menu_shell_cycle_focus),
301 _gtk_marshal_VOID__ENUM,
303 GTK_TYPE_DIRECTION_TYPE);
306 binding_set = gtk_binding_set_by_class (klass);
307 gtk_binding_entry_add_signal (binding_set,
310 gtk_binding_entry_add_signal (binding_set,
312 "activate_current", 1,
315 gtk_binding_entry_add_signal (binding_set,
317 "activate_current", 1,
320 gtk_binding_entry_add_signal (binding_set,
322 "activate_current", 1,
325 gtk_binding_entry_add_signal (binding_set,
327 "activate_current", 1,
330 gtk_binding_entry_add_signal (binding_set,
333 GTK_TYPE_DIRECTION_TYPE, GTK_DIR_TAB_FORWARD);
334 gtk_binding_entry_add_signal (binding_set,
335 GDK_F10, GDK_SHIFT_MASK,
337 GTK_TYPE_DIRECTION_TYPE, GTK_DIR_TAB_BACKWARD);
341 gtk_menu_shell_child_type (GtkContainer *container)
343 return GTK_TYPE_MENU_ITEM;
347 gtk_menu_shell_init (GtkMenuShell *menu_shell)
349 menu_shell->children = NULL;
350 menu_shell->active_menu_item = NULL;
351 menu_shell->parent_menu_shell = NULL;
352 menu_shell->active = FALSE;
353 menu_shell->have_grab = FALSE;
354 menu_shell->have_xgrab = FALSE;
355 menu_shell->ignore_leave = FALSE;
356 menu_shell->button = 0;
357 menu_shell->menu_flag = 0;
358 menu_shell->activate_time = 0;
362 gtk_menu_shell_append (GtkMenuShell *menu_shell,
365 gtk_menu_shell_insert (menu_shell, child, -1);
369 gtk_menu_shell_prepend (GtkMenuShell *menu_shell,
372 gtk_menu_shell_insert (menu_shell, child, 0);
376 gtk_menu_shell_insert (GtkMenuShell *menu_shell,
380 GtkMenuShellClass *class;
382 g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
383 g_return_if_fail (GTK_IS_MENU_ITEM (child));
385 class = GTK_MENU_SHELL_GET_CLASS (menu_shell);
388 class->insert (menu_shell, child, position);
392 gtk_menu_shell_real_insert (GtkMenuShell *menu_shell,
396 menu_shell->children = g_list_insert (menu_shell->children, child, position);
398 gtk_widget_set_parent (child, GTK_WIDGET (menu_shell));
402 gtk_menu_shell_deactivate (GtkMenuShell *menu_shell)
404 g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
406 g_signal_emit (menu_shell, menu_shell_signals[DEACTIVATE], 0);
410 gtk_menu_shell_realize (GtkWidget *widget)
412 GdkWindowAttr attributes;
413 gint attributes_mask;
415 g_return_if_fail (GTK_IS_MENU_SHELL (widget));
417 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
419 attributes.x = widget->allocation.x;
420 attributes.y = widget->allocation.y;
421 attributes.width = widget->allocation.width;
422 attributes.height = widget->allocation.height;
423 attributes.window_type = GDK_WINDOW_CHILD;
424 attributes.wclass = GDK_INPUT_OUTPUT;
425 attributes.visual = gtk_widget_get_visual (widget);
426 attributes.colormap = gtk_widget_get_colormap (widget);
427 attributes.event_mask = gtk_widget_get_events (widget);
428 attributes.event_mask |= (GDK_EXPOSURE_MASK |
429 GDK_BUTTON_PRESS_MASK |
430 GDK_BUTTON_RELEASE_MASK |
432 GDK_ENTER_NOTIFY_MASK |
433 GDK_LEAVE_NOTIFY_MASK);
435 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
436 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
437 gdk_window_set_user_data (widget->window, widget);
439 widget->style = gtk_style_attach (widget->style, widget->window);
440 gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
444 _gtk_menu_shell_activate (GtkMenuShell *menu_shell)
446 if (!menu_shell->active)
448 gtk_grab_add (GTK_WIDGET (menu_shell));
449 menu_shell->have_grab = TRUE;
450 menu_shell->active = TRUE;
455 gtk_menu_shell_button_press (GtkWidget *widget,
456 GdkEventButton *event)
458 GtkMenuShell *menu_shell;
459 GtkWidget *menu_item;
461 g_return_val_if_fail (GTK_IS_MENU_SHELL (widget), FALSE);
462 g_return_val_if_fail (event != NULL, FALSE);
464 if (event->type != GDK_BUTTON_PRESS)
467 menu_shell = GTK_MENU_SHELL (widget);
469 if (menu_shell->parent_menu_shell)
471 return gtk_widget_event (menu_shell->parent_menu_shell, (GdkEvent*) event);
473 else if (!menu_shell->active || !menu_shell->button)
475 _gtk_menu_shell_activate (menu_shell);
477 menu_shell->button = event->button;
479 menu_item = gtk_menu_shell_get_item (menu_shell, (GdkEvent *)event);
481 if (menu_item && _gtk_menu_item_is_selectable (menu_item))
483 if ((menu_item->parent == widget) &&
484 (menu_item != menu_shell->active_menu_item))
486 if (GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement == GTK_TOP_BOTTOM)
487 g_object_set_data (G_OBJECT (menu_shell),
488 "gtk-menushell-just-activated",
489 GUINT_TO_POINTER (1));
490 gtk_menu_shell_select_item (menu_shell, menu_item);
496 widget = gtk_get_event_widget ((GdkEvent*) event);
497 if (widget == GTK_WIDGET (menu_shell))
499 gtk_menu_shell_deactivate (menu_shell);
500 g_signal_emit (menu_shell, menu_shell_signals[SELECTION_DONE], 0);
508 gtk_menu_shell_button_release (GtkWidget *widget,
509 GdkEventButton *event)
511 GtkMenuShell *menu_shell;
512 GtkWidget *menu_item;
515 g_return_val_if_fail (GTK_IS_MENU_SHELL (widget), FALSE);
516 g_return_val_if_fail (event != NULL, FALSE);
518 menu_shell = GTK_MENU_SHELL (widget);
519 if (menu_shell->active)
521 gboolean deactivate_immediately = FALSE;
523 if (menu_shell->button && (event->button != menu_shell->button))
525 menu_shell->button = 0;
526 if (menu_shell->parent_menu_shell)
527 return gtk_widget_event (menu_shell->parent_menu_shell, (GdkEvent*) event);
530 menu_shell->button = 0;
531 menu_item = gtk_menu_shell_get_item (menu_shell, (GdkEvent*) event);
536 && GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement == GTK_TOP_BOTTOM)
538 if (g_object_get_data (G_OBJECT (menu_shell), "gtk-menushell-just-activated"))
539 g_object_set_data (G_OBJECT (menu_shell), "gtk-menushell-just-activated", NULL);
541 deactivate_immediately = TRUE;
544 if ((event->time - menu_shell->activate_time) > MENU_SHELL_TIMEOUT)
546 if (deactivate_immediately)
548 gtk_menu_shell_deactivate (menu_shell);
552 if (menu_item && (menu_shell->active_menu_item == menu_item) &&
553 _gtk_menu_item_is_selectable (menu_item))
555 if (GTK_MENU_ITEM (menu_item)->submenu == NULL)
557 gtk_menu_shell_activate_item (menu_shell, menu_item, TRUE);
561 else if (menu_item && !_gtk_menu_item_is_selectable (menu_item))
563 else if (menu_shell->parent_menu_shell)
565 menu_shell->active = TRUE;
566 gtk_widget_event (menu_shell->parent_menu_shell, (GdkEvent*) event);
572 /* We only ever want to prevent deactivation on the first
573 * press/release. Setting the time to zero is a bit of a
574 * hack, since we could be being triggered in the first
575 * few fractions of a second after a server time wraparound.
576 * the chances of that happening are ~1/10^6, without
577 * serious harm if we lose.
579 menu_shell->activate_time = 0;
583 /* If the button click was very fast, or we ended up on a submenu,
587 (menu_item && (menu_shell->active_menu_item == menu_item)))
590 menu_shell->ignore_leave = TRUE;
597 gtk_menu_shell_deactivate (menu_shell);
598 g_signal_emit (menu_shell, menu_shell_signals[SELECTION_DONE], 0);
606 gtk_menu_shell_key_press (GtkWidget *widget,
609 GtkMenuShell *menu_shell;
612 g_return_val_if_fail (GTK_IS_MENU_SHELL (widget), FALSE);
613 g_return_val_if_fail (event != NULL, FALSE);
615 menu_shell = GTK_MENU_SHELL (widget);
617 if (!menu_shell->active_menu_item && menu_shell->parent_menu_shell)
618 return gtk_widget_event (menu_shell->parent_menu_shell, (GdkEvent *)event);
620 if (_gtk_bindings_activate_event (GTK_OBJECT (widget), event))
623 toplevel = gtk_widget_get_toplevel (widget);
624 if (GTK_IS_WINDOW (toplevel) &&
625 _gtk_window_activate_key (GTK_WINDOW (toplevel), event))
632 gtk_menu_shell_enter_notify (GtkWidget *widget,
633 GdkEventCrossing *event)
635 GtkMenuShell *menu_shell;
636 GtkWidget *menu_item;
638 g_return_val_if_fail (GTK_IS_MENU_SHELL (widget), FALSE);
639 g_return_val_if_fail (event != NULL, FALSE);
641 menu_shell = GTK_MENU_SHELL (widget);
643 if (menu_shell->active)
645 menu_item = gtk_get_event_widget ((GdkEvent*) event);
648 (GTK_IS_MENU_ITEM (menu_item) &&
649 !_gtk_menu_item_is_selectable (menu_item)))
652 if ((menu_item->parent == widget) &&
653 (menu_shell->active_menu_item != menu_item) &&
654 GTK_IS_MENU_ITEM (menu_item))
656 if (menu_shell->ignore_enter)
659 if ((event->detail != GDK_NOTIFY_INFERIOR) &&
660 (GTK_WIDGET_STATE (menu_item) != GTK_STATE_PRELIGHT))
662 gtk_menu_shell_select_item (menu_shell, menu_item);
665 else if (menu_shell->parent_menu_shell)
667 gtk_widget_event (menu_shell->parent_menu_shell, (GdkEvent*) event);
675 gtk_menu_shell_leave_notify (GtkWidget *widget,
676 GdkEventCrossing *event)
678 GtkMenuShell *menu_shell;
679 GtkMenuItem *menu_item;
680 GtkWidget *event_widget;
682 g_return_val_if_fail (GTK_IS_MENU_SHELL (widget), FALSE);
683 g_return_val_if_fail (event != NULL, FALSE);
685 if (GTK_WIDGET_VISIBLE (widget))
687 menu_shell = GTK_MENU_SHELL (widget);
688 event_widget = gtk_get_event_widget ((GdkEvent*) event);
690 if (!event_widget || !GTK_IS_MENU_ITEM (event_widget))
693 menu_item = GTK_MENU_ITEM (event_widget);
695 if (menu_shell->ignore_leave)
697 menu_shell->ignore_leave = FALSE;
701 if (!_gtk_menu_item_is_selectable (event_widget))
704 if ((menu_shell->active_menu_item == event_widget) &&
705 (menu_item->submenu == NULL))
707 if ((event->detail != GDK_NOTIFY_INFERIOR) &&
708 (GTK_WIDGET_STATE (menu_item) != GTK_STATE_NORMAL))
710 gtk_menu_shell_deselect (menu_shell);
713 else if (menu_shell->parent_menu_shell)
715 gtk_widget_event (menu_shell->parent_menu_shell, (GdkEvent*) event);
723 gtk_menu_shell_add (GtkContainer *container,
726 gtk_menu_shell_append (GTK_MENU_SHELL (container), widget);
730 gtk_menu_shell_remove (GtkContainer *container,
733 GtkMenuShell *menu_shell;
736 g_return_if_fail (GTK_IS_MENU_SHELL (container));
737 g_return_if_fail (GTK_IS_MENU_ITEM (widget));
739 was_visible = GTK_WIDGET_VISIBLE (widget);
740 menu_shell = GTK_MENU_SHELL (container);
741 menu_shell->children = g_list_remove (menu_shell->children, widget);
743 if (widget == menu_shell->active_menu_item)
745 gtk_item_deselect (GTK_ITEM (menu_shell->active_menu_item));
746 menu_shell->active_menu_item = NULL;
749 gtk_widget_unparent (widget);
751 /* queue resize regardless of GTK_WIDGET_VISIBLE (container),
752 * since that's what is needed by toplevels.
755 gtk_widget_queue_resize (GTK_WIDGET (container));
759 gtk_menu_shell_forall (GtkContainer *container,
760 gboolean include_internals,
761 GtkCallback callback,
762 gpointer callback_data)
764 GtkMenuShell *menu_shell;
768 g_return_if_fail (GTK_IS_MENU_SHELL (container));
769 g_return_if_fail (callback != NULL);
771 menu_shell = GTK_MENU_SHELL (container);
773 children = menu_shell->children;
776 child = children->data;
777 children = children->next;
779 (* callback) (child, callback_data);
785 gtk_real_menu_shell_deactivate (GtkMenuShell *menu_shell)
787 if (menu_shell->active)
789 menu_shell->button = 0;
790 menu_shell->active = FALSE;
792 if (menu_shell->active_menu_item)
794 gtk_menu_item_deselect (GTK_MENU_ITEM (menu_shell->active_menu_item));
795 menu_shell->active_menu_item = NULL;
798 if (menu_shell->have_grab)
800 menu_shell->have_grab = FALSE;
801 gtk_grab_remove (GTK_WIDGET (menu_shell));
803 if (menu_shell->have_xgrab)
805 GdkDisplay *display = gtk_widget_get_display (GTK_WIDGET (menu_shell));
807 menu_shell->have_xgrab = FALSE;
808 gdk_display_pointer_ungrab (display, GDK_CURRENT_TIME);
809 gdk_display_keyboard_ungrab (display, GDK_CURRENT_TIME);
815 gtk_menu_shell_is_item (GtkMenuShell *menu_shell,
820 g_return_val_if_fail (GTK_IS_MENU_SHELL (menu_shell), FALSE);
821 g_return_val_if_fail (child != NULL, FALSE);
823 parent = child->parent;
824 while (parent && GTK_IS_MENU_SHELL (parent))
826 if (parent == (GtkWidget*) menu_shell)
828 parent = GTK_MENU_SHELL (parent)->parent_menu_shell;
835 gtk_menu_shell_get_item (GtkMenuShell *menu_shell,
838 GtkWidget *menu_item;
840 menu_item = gtk_get_event_widget ((GdkEvent*) event);
842 while (menu_item && !GTK_IS_MENU_ITEM (menu_item))
843 menu_item = menu_item->parent;
845 if (menu_item && gtk_menu_shell_is_item (menu_shell, menu_item))
851 /* Handlers for action signals */
854 gtk_menu_shell_select_item (GtkMenuShell *menu_shell,
855 GtkWidget *menu_item)
857 GtkMenuShellClass *class;
859 g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
860 g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
862 class = GTK_MENU_SHELL_GET_CLASS (menu_shell);
864 if (class->select_item)
865 class->select_item (menu_shell, menu_item);
868 void _gtk_menu_item_set_placement (GtkMenuItem *menu_item,
869 GtkSubmenuPlacement placement);
872 gtk_menu_shell_real_select_item (GtkMenuShell *menu_shell,
873 GtkWidget *menu_item)
875 gtk_menu_shell_deselect (menu_shell);
877 if (!_gtk_menu_item_is_selectable (menu_item))
880 menu_shell->active_menu_item = menu_item;
881 _gtk_menu_item_set_placement (GTK_MENU_ITEM (menu_shell->active_menu_item),
882 GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement);
883 gtk_menu_item_select (GTK_MENU_ITEM (menu_shell->active_menu_item));
885 /* This allows the bizarre radio buttons-with-submenus-display-history
888 if (GTK_MENU_ITEM (menu_shell->active_menu_item)->submenu)
889 gtk_widget_activate (menu_shell->active_menu_item);
893 gtk_menu_shell_deselect (GtkMenuShell *menu_shell)
895 g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
897 if (menu_shell->active_menu_item)
899 gtk_menu_item_deselect (GTK_MENU_ITEM (menu_shell->active_menu_item));
900 menu_shell->active_menu_item = NULL;
905 gtk_menu_shell_activate_item (GtkMenuShell *menu_shell,
906 GtkWidget *menu_item,
907 gboolean force_deactivate)
909 GSList *slist, *shells = NULL;
910 gboolean deactivate = force_deactivate;
912 g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
913 g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
916 deactivate = GTK_MENU_ITEM_GET_CLASS (menu_item)->hide_on_activate;
918 g_object_ref (menu_shell);
922 GtkMenuShell *parent_menu_shell = menu_shell;
926 g_object_ref (parent_menu_shell);
927 shells = g_slist_prepend (shells, parent_menu_shell);
928 parent_menu_shell = (GtkMenuShell*) parent_menu_shell->parent_menu_shell;
930 while (parent_menu_shell);
931 shells = g_slist_reverse (shells);
933 gtk_menu_shell_deactivate (menu_shell);
935 /* flush the x-queue, so any grabs are removed and
936 * the menu is actually taken down
938 gdk_display_sync (gtk_widget_get_display (menu_item));
941 gtk_widget_activate (menu_item);
943 for (slist = shells; slist; slist = slist->next)
945 g_signal_emit (slist->data, menu_shell_signals[SELECTION_DONE], 0);
946 g_object_unref (slist->data);
948 g_slist_free (shells);
950 g_object_unref (menu_shell);
953 /* Distance should be +/- 1 */
955 gtk_menu_shell_move_selected (GtkMenuShell *menu_shell,
958 if (menu_shell->active_menu_item)
960 GList *node = g_list_find (menu_shell->children,
961 menu_shell->active_menu_item);
962 GList *start_node = node;
967 while (node != start_node &&
968 (!node || !_gtk_menu_item_is_selectable (node->data)))
971 node = menu_shell->children;
979 while (node != start_node &&
980 (!node || !_gtk_menu_item_is_selectable (node->data)))
983 node = g_list_last (menu_shell->children);
990 gtk_menu_shell_select_item (menu_shell, node->data);
995 * gtk_menu_shell_select_first:
996 * @menu_shell: a #GtkMenuShell
997 * @search_sensitive: if %TRUE, search for the first selectable
998 * menu item, otherwise select nothing if
999 * the first item isn't sensitive. This
1000 * should be %FALSE if the menu is being
1001 * popped up initially.
1003 * Select the first visible or selectable child of the menu shell;
1004 * don't select tearoff items unless the only item is a tearoff
1008 gtk_menu_shell_select_first (GtkMenuShell *menu_shell,
1009 gboolean search_sensitive)
1011 GtkWidget *to_select = NULL;
1014 tmp_list = menu_shell->children;
1017 GtkWidget *child = tmp_list->data;
1019 if ((!search_sensitive && GTK_WIDGET_VISIBLE (child)) ||
1020 _gtk_menu_item_is_selectable (child))
1023 if (!GTK_IS_TEAROFF_MENU_ITEM (child))
1027 tmp_list = tmp_list->next;
1031 gtk_menu_shell_select_item (menu_shell, to_select);
1035 gtk_menu_shell_select_last (GtkMenuShell *menu_shell,
1036 gboolean search_sensitive)
1038 GtkWidget *to_select = NULL;
1041 tmp_list = g_list_last (menu_shell->children);
1044 GtkWidget *child = tmp_list->data;
1046 if ((!search_sensitive && GTK_WIDGET_VISIBLE (child)) ||
1047 _gtk_menu_item_is_selectable (child))
1050 if (!GTK_IS_TEAROFF_MENU_ITEM (child))
1054 tmp_list = tmp_list->prev;
1058 gtk_menu_shell_select_item (menu_shell, to_select);
1062 gtk_menu_shell_select_submenu_first (GtkMenuShell *menu_shell)
1064 GtkMenuItem *menu_item;
1066 menu_item = GTK_MENU_ITEM (menu_shell->active_menu_item);
1068 if (menu_item->submenu)
1069 gtk_menu_shell_select_first (GTK_MENU_SHELL (menu_item->submenu), TRUE);
1073 gtk_real_menu_shell_move_current (GtkMenuShell *menu_shell,
1074 GtkMenuDirectionType direction)
1076 GtkMenuShell *parent_menu_shell = NULL;
1077 gboolean had_selection;
1079 had_selection = menu_shell->active_menu_item != NULL;
1081 if (menu_shell->parent_menu_shell)
1082 parent_menu_shell = GTK_MENU_SHELL (menu_shell->parent_menu_shell);
1086 case GTK_MENU_DIR_PARENT:
1087 if (parent_menu_shell)
1089 if (GTK_MENU_SHELL_GET_CLASS (parent_menu_shell)->submenu_placement ==
1090 GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement)
1091 gtk_menu_shell_deselect (menu_shell);
1094 gtk_menu_shell_move_selected (parent_menu_shell, -1);
1095 gtk_menu_shell_select_submenu_first (parent_menu_shell);
1100 case GTK_MENU_DIR_CHILD:
1101 if (menu_shell->active_menu_item &&
1102 _gtk_menu_item_is_selectable (menu_shell->active_menu_item) &&
1103 GTK_MENU_ITEM (menu_shell->active_menu_item)->submenu)
1105 gtk_menu_shell_select_submenu_first (menu_shell);
1109 /* Try to find a menu running the opposite direction */
1110 while (parent_menu_shell &&
1111 (GTK_MENU_SHELL_GET_CLASS (parent_menu_shell)->submenu_placement ==
1112 GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement))
1114 GtkWidget *tmp_widget = parent_menu_shell->parent_menu_shell;
1117 parent_menu_shell = GTK_MENU_SHELL (tmp_widget);
1119 parent_menu_shell = NULL;
1122 if (parent_menu_shell)
1124 gtk_menu_shell_move_selected (parent_menu_shell, 1);
1125 gtk_menu_shell_select_submenu_first (parent_menu_shell);
1130 case GTK_MENU_DIR_PREV:
1131 gtk_menu_shell_move_selected (menu_shell, -1);
1132 if (!had_selection &&
1133 !menu_shell->active_menu_item &&
1134 menu_shell->children)
1135 gtk_menu_shell_select_last (menu_shell, TRUE);
1137 case GTK_MENU_DIR_NEXT:
1138 gtk_menu_shell_move_selected (menu_shell, 1);
1139 if (!had_selection &&
1140 !menu_shell->active_menu_item &&
1141 menu_shell->children)
1142 gtk_menu_shell_select_first (menu_shell, TRUE);
1149 gtk_real_menu_shell_activate_current (GtkMenuShell *menu_shell,
1150 gboolean force_hide)
1152 if (menu_shell->active_menu_item &&
1153 _gtk_menu_item_is_selectable (menu_shell->active_menu_item) &&
1154 GTK_MENU_ITEM (menu_shell->active_menu_item)->submenu == NULL)
1156 gtk_menu_shell_activate_item (menu_shell,
1157 menu_shell->active_menu_item,
1163 gtk_real_menu_shell_cancel (GtkMenuShell *menu_shell)
1165 /* Unset the active menu item so gtk_menu_popdown() doesn't see it.
1167 gtk_menu_shell_deselect (menu_shell);
1169 gtk_menu_shell_deactivate (menu_shell);
1170 g_signal_emit (menu_shell, menu_shell_signals[SELECTION_DONE], 0);
1174 gtk_real_menu_shell_cycle_focus (GtkMenuShell *menu_shell,
1175 GtkDirectionType dir)
1177 while (menu_shell && !GTK_IS_MENU_BAR (menu_shell))
1179 if (menu_shell->parent_menu_shell)
1180 menu_shell = GTK_MENU_SHELL (menu_shell->parent_menu_shell);
1186 _gtk_menu_bar_cycle_focus (GTK_MENU_BAR (menu_shell), dir);