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 "gtksignal.h"
37 #include "gtktearoffmenuitem.h"
38 #include "gtkwindow.h"
40 #define MENU_SHELL_TIMEOUT 500
52 typedef void (*GtkMenuShellSignal1) (GtkObject *object,
53 GtkMenuDirectionType arg1,
55 typedef void (*GtkMenuShellSignal2) (GtkObject *object,
61 * A menu item can be "selected", this means that it is displayed
62 * in the prelight state, and if it has a submenu, that submenu
65 * A menu is "active" when it is visible onscreen and the user
66 * is selecting from it. A menubar is not active until the user
67 * clicks on one of its menuitems. When a menu is active,
68 * passing the mouse over a submenu will pop it up.
70 * menu_shell->active_menu_item, is however, not an "active"
71 * menu item (there is no such thing) but rather, the selected
72 * menu item in that MenuShell, if there is one.
74 * There is also is a concept of the current menu and a current
75 * menu item. The current menu item is the selected menu item
76 * that is furthest down in the heirarchy. (Every active menu_shell
77 * does not necessarily contain a selected menu item, but if
78 * it does, then menu_shell->parent_menu_shell must also contain
79 * a selected menu item. The current menu is the menu that
80 * contains the current menu_item. It will always have a GTK
81 * grab and receive all key presses.
86 * ::move_current (GtkMenuDirection *dir)
87 * Moves the current menu item in direction 'dir':
89 * GTK_MENU_DIR_PARENT: To the parent menu shell
90 * GTK_MENU_DIR_CHILD: To the child menu shell (if this item has
92 * GTK_MENU_DIR_NEXT/PREV: To the next or previous item
95 * As a a bit of a hack to get movement between menus and
96 * menubars working, if submenu_placement is different for
97 * the menu and its MenuShell then the following apply:
99 * - For 'parent' the current menu is not just moved to
100 * the parent, but moved to the previous entry in the parent
101 * - For 'child', if there is no child, then current is
102 * moved to the next item in the parent.
105 * ::activate_current (GBoolean *force_hide)
106 * Activate the current item. If 'force_hide' is true, hide
107 * the current menu item always. Otherwise, only hide
108 * it if menu_item->klass->hide_on_activate is true.
111 * Cancels the current selection
114 static void gtk_menu_shell_class_init (GtkMenuShellClass *klass);
115 static void gtk_menu_shell_init (GtkMenuShell *menu_shell);
116 static void gtk_menu_shell_realize (GtkWidget *widget);
117 static gint gtk_menu_shell_button_press (GtkWidget *widget,
118 GdkEventButton *event);
119 static gint gtk_menu_shell_button_release (GtkWidget *widget,
120 GdkEventButton *event);
121 static gint gtk_menu_shell_key_press (GtkWidget *widget,
123 static gint gtk_menu_shell_enter_notify (GtkWidget *widget,
124 GdkEventCrossing *event);
125 static gint gtk_menu_shell_leave_notify (GtkWidget *widget,
126 GdkEventCrossing *event);
127 static void gtk_menu_shell_add (GtkContainer *container,
129 static void gtk_menu_shell_remove (GtkContainer *container,
131 static void gtk_menu_shell_forall (GtkContainer *container,
132 gboolean include_internals,
133 GtkCallback callback,
134 gpointer callback_data);
135 static void gtk_menu_shell_real_insert (GtkMenuShell *menu_shell,
138 static void gtk_real_menu_shell_deactivate (GtkMenuShell *menu_shell);
139 static gint gtk_menu_shell_is_item (GtkMenuShell *menu_shell,
141 static GtkWidget *gtk_menu_shell_get_item (GtkMenuShell *menu_shell,
143 static GtkType gtk_menu_shell_child_type (GtkContainer *container);
144 static void gtk_menu_shell_real_select_item (GtkMenuShell *menu_shell,
145 GtkWidget *menu_item);
146 static void gtk_menu_shell_select_submenu_first (GtkMenuShell *menu_shell);
148 static void gtk_real_menu_shell_move_current (GtkMenuShell *menu_shell,
149 GtkMenuDirectionType direction);
150 static void gtk_real_menu_shell_activate_current (GtkMenuShell *menu_shell,
151 gboolean force_hide);
152 static void gtk_real_menu_shell_cancel (GtkMenuShell *menu_shell);
153 static void gtk_real_menu_shell_cycle_focus (GtkMenuShell *menu_shell,
154 GtkDirectionType dir);
156 static GtkContainerClass *parent_class = NULL;
157 static guint menu_shell_signals[LAST_SIGNAL] = { 0 };
161 gtk_menu_shell_get_type (void)
163 static GtkType menu_shell_type = 0;
165 if (!menu_shell_type)
167 static const GTypeInfo menu_shell_info =
169 sizeof (GtkMenuShellClass),
170 NULL, /* base_init */
171 NULL, /* base_finalize */
172 (GClassInitFunc) gtk_menu_shell_class_init,
173 NULL, /* class_finalize */
174 NULL, /* class_data */
175 sizeof (GtkMenuShell),
177 (GInstanceInitFunc) gtk_menu_shell_init,
178 NULL /* value_table */
181 menu_shell_type = 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 GtkObjectClass *object_class;
221 GtkWidgetClass *widget_class;
222 GtkContainerClass *container_class;
224 GtkBindingSet *binding_set;
226 object_class = (GtkObjectClass*) klass;
227 widget_class = (GtkWidgetClass*) klass;
228 container_class = (GtkContainerClass*) klass;
230 parent_class = gtk_type_class (gtk_container_get_type ());
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 gtk_signal_new ("deactivate",
256 GTK_CLASS_TYPE (object_class),
257 GTK_SIGNAL_OFFSET (GtkMenuShellClass, deactivate),
258 _gtk_marshal_VOID__VOID,
260 menu_shell_signals[SELECTION_DONE] =
261 gtk_signal_new ("selection-done",
263 GTK_CLASS_TYPE (object_class),
264 GTK_SIGNAL_OFFSET (GtkMenuShellClass, selection_done),
265 _gtk_marshal_VOID__VOID,
267 menu_shell_signals[MOVE_CURRENT] =
268 gtk_signal_new ("move_current",
269 GTK_RUN_LAST | GTK_RUN_ACTION,
270 GTK_CLASS_TYPE (object_class),
271 GTK_SIGNAL_OFFSET (GtkMenuShellClass, move_current),
272 _gtk_marshal_VOID__ENUM,
274 GTK_TYPE_MENU_DIRECTION_TYPE);
275 menu_shell_signals[ACTIVATE_CURRENT] =
276 gtk_signal_new ("activate_current",
277 GTK_RUN_LAST | GTK_RUN_ACTION,
278 GTK_CLASS_TYPE (object_class),
279 GTK_SIGNAL_OFFSET (GtkMenuShellClass, activate_current),
280 _gtk_marshal_VOID__BOOLEAN,
283 menu_shell_signals[CANCEL] =
284 gtk_signal_new ("cancel",
285 GTK_RUN_LAST | GTK_RUN_ACTION,
286 GTK_CLASS_TYPE (object_class),
287 GTK_SIGNAL_OFFSET (GtkMenuShellClass, cancel),
288 _gtk_marshal_VOID__VOID,
290 menu_shell_signals[CYCLE_FOCUS] =
291 binding_signal_new ("cycle_focus",
292 G_OBJECT_CLASS_TYPE (object_class),
293 G_SIGNAL_RUN_LAST | GTK_RUN_ACTION,
294 G_CALLBACK (gtk_real_menu_shell_cycle_focus),
296 _gtk_marshal_VOID__ENUM,
298 GTK_TYPE_DIRECTION_TYPE);
301 binding_set = gtk_binding_set_by_class (klass);
302 gtk_binding_entry_add_signal (binding_set,
305 gtk_binding_entry_add_signal (binding_set,
307 "activate_current", 1,
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,
328 GTK_TYPE_DIRECTION_TYPE, GTK_DIR_TAB_FORWARD);
329 gtk_binding_entry_add_signal (binding_set,
330 GDK_F10, GDK_SHIFT_MASK,
332 GTK_TYPE_DIRECTION_TYPE, GTK_DIR_TAB_BACKWARD);
336 gtk_menu_shell_child_type (GtkContainer *container)
338 return GTK_TYPE_MENU_ITEM;
342 gtk_menu_shell_init (GtkMenuShell *menu_shell)
344 menu_shell->children = NULL;
345 menu_shell->active_menu_item = NULL;
346 menu_shell->parent_menu_shell = NULL;
347 menu_shell->active = FALSE;
348 menu_shell->have_grab = FALSE;
349 menu_shell->have_xgrab = FALSE;
350 menu_shell->ignore_leave = FALSE;
351 menu_shell->button = 0;
352 menu_shell->menu_flag = 0;
353 menu_shell->activate_time = 0;
357 gtk_menu_shell_append (GtkMenuShell *menu_shell,
360 gtk_menu_shell_insert (menu_shell, child, -1);
364 gtk_menu_shell_prepend (GtkMenuShell *menu_shell,
367 gtk_menu_shell_insert (menu_shell, child, 0);
371 gtk_menu_shell_insert (GtkMenuShell *menu_shell,
375 GtkMenuShellClass *class;
377 g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
378 g_return_if_fail (GTK_IS_MENU_ITEM (child));
380 class = GTK_MENU_SHELL_GET_CLASS (menu_shell);
383 class->insert (menu_shell, child, position);
387 gtk_menu_shell_real_insert (GtkMenuShell *menu_shell,
391 menu_shell->children = g_list_insert (menu_shell->children, child, position);
393 gtk_widget_set_parent (child, GTK_WIDGET (menu_shell));
397 gtk_menu_shell_deactivate (GtkMenuShell *menu_shell)
399 g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
401 gtk_signal_emit (GTK_OBJECT (menu_shell), menu_shell_signals[DEACTIVATE]);
405 gtk_menu_shell_realize (GtkWidget *widget)
407 GdkWindowAttr attributes;
408 gint attributes_mask;
410 g_return_if_fail (GTK_IS_MENU_SHELL (widget));
412 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
414 attributes.x = widget->allocation.x;
415 attributes.y = widget->allocation.y;
416 attributes.width = widget->allocation.width;
417 attributes.height = widget->allocation.height;
418 attributes.window_type = GDK_WINDOW_CHILD;
419 attributes.wclass = GDK_INPUT_OUTPUT;
420 attributes.visual = gtk_widget_get_visual (widget);
421 attributes.colormap = gtk_widget_get_colormap (widget);
422 attributes.event_mask = gtk_widget_get_events (widget);
423 attributes.event_mask |= (GDK_EXPOSURE_MASK |
424 GDK_BUTTON_PRESS_MASK |
425 GDK_BUTTON_RELEASE_MASK |
427 GDK_ENTER_NOTIFY_MASK |
428 GDK_LEAVE_NOTIFY_MASK);
430 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
431 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
432 gdk_window_set_user_data (widget->window, widget);
434 widget->style = gtk_style_attach (widget->style, widget->window);
435 gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
439 _gtk_menu_shell_activate (GtkMenuShell *menu_shell)
441 if (!menu_shell->active)
443 gtk_grab_add (GTK_WIDGET (menu_shell));
444 menu_shell->have_grab = TRUE;
445 menu_shell->active = TRUE;
450 gtk_menu_shell_button_press (GtkWidget *widget,
451 GdkEventButton *event)
453 GtkMenuShell *menu_shell;
454 GtkWidget *menu_item;
456 g_return_val_if_fail (GTK_IS_MENU_SHELL (widget), FALSE);
457 g_return_val_if_fail (event != NULL, FALSE);
459 if (event->type != GDK_BUTTON_PRESS)
462 menu_shell = GTK_MENU_SHELL (widget);
464 if (menu_shell->parent_menu_shell)
466 return gtk_widget_event (menu_shell->parent_menu_shell, (GdkEvent*) event);
468 else if (!menu_shell->active || !menu_shell->button)
470 _gtk_menu_shell_activate (menu_shell);
472 menu_shell->button = event->button;
474 menu_item = gtk_menu_shell_get_item (menu_shell, (GdkEvent *)event);
476 if (menu_item && _gtk_menu_item_is_selectable (menu_item))
478 if ((menu_item->parent == widget) &&
479 (menu_item != menu_shell->active_menu_item))
481 if (GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement == GTK_TOP_BOTTOM)
482 g_object_set_data (G_OBJECT (menu_shell),
483 "gtk-menushell-just-activated",
484 GUINT_TO_POINTER (1));
485 gtk_menu_shell_select_item (menu_shell, menu_item);
491 widget = gtk_get_event_widget ((GdkEvent*) event);
492 if (widget == GTK_WIDGET (menu_shell))
494 gtk_menu_shell_deactivate (menu_shell);
495 gtk_signal_emit (GTK_OBJECT (menu_shell), menu_shell_signals[SELECTION_DONE]);
503 gtk_menu_shell_button_release (GtkWidget *widget,
504 GdkEventButton *event)
506 GtkMenuShell *menu_shell;
507 GtkWidget *menu_item;
510 g_return_val_if_fail (GTK_IS_MENU_SHELL (widget), FALSE);
511 g_return_val_if_fail (event != NULL, FALSE);
513 menu_shell = GTK_MENU_SHELL (widget);
514 if (menu_shell->active)
516 gboolean deactivate_immediately = FALSE;
518 if (menu_shell->button && (event->button != menu_shell->button))
520 menu_shell->button = 0;
521 if (menu_shell->parent_menu_shell)
522 return gtk_widget_event (menu_shell->parent_menu_shell, (GdkEvent*) event);
525 menu_shell->button = 0;
526 menu_item = gtk_menu_shell_get_item (menu_shell, (GdkEvent*) event);
531 && GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement == GTK_TOP_BOTTOM)
533 if (g_object_get_data (G_OBJECT (menu_shell), "gtk-menushell-just-activated"))
534 g_object_set_data (G_OBJECT (menu_shell), "gtk-menushell-just-activated", NULL);
536 deactivate_immediately = TRUE;
539 if ((event->time - menu_shell->activate_time) > MENU_SHELL_TIMEOUT)
541 if (deactivate_immediately)
543 gtk_menu_shell_deactivate (menu_shell);
547 if (menu_item && (menu_shell->active_menu_item == menu_item) &&
548 _gtk_menu_item_is_selectable (menu_item))
550 if (GTK_MENU_ITEM (menu_item)->submenu == NULL)
552 gtk_menu_shell_activate_item (menu_shell, menu_item, TRUE);
556 else if (menu_item && !_gtk_menu_item_is_selectable (menu_item))
558 else if (menu_shell->parent_menu_shell)
560 menu_shell->active = TRUE;
561 gtk_widget_event (menu_shell->parent_menu_shell, (GdkEvent*) event);
567 /* We only ever want to prevent deactivation on the first
568 * press/release. Setting the time to zero is a bit of a
569 * hack, since we could be being triggered in the first
570 * few fractions of a second after a server time wraparound.
571 * the chances of that happening are ~1/10^6, without
572 * serious harm if we lose.
574 menu_shell->activate_time = 0;
578 /* If the button click was very fast, or we ended up on a submenu,
582 (menu_item && (menu_shell->active_menu_item == menu_item)))
585 menu_shell->ignore_leave = TRUE;
592 gtk_menu_shell_deactivate (menu_shell);
593 gtk_signal_emit (GTK_OBJECT (menu_shell), menu_shell_signals[SELECTION_DONE]);
601 gtk_menu_shell_key_press (GtkWidget *widget,
604 GtkMenuShell *menu_shell;
607 g_return_val_if_fail (GTK_IS_MENU_SHELL (widget), FALSE);
608 g_return_val_if_fail (event != NULL, FALSE);
610 menu_shell = GTK_MENU_SHELL (widget);
612 if (!menu_shell->active_menu_item && menu_shell->parent_menu_shell)
613 return gtk_widget_event (menu_shell->parent_menu_shell, (GdkEvent *)event);
615 if (_gtk_bindings_activate_event (GTK_OBJECT (widget), event))
618 toplevel = gtk_widget_get_toplevel (widget);
619 if (GTK_IS_WINDOW (toplevel) &&
620 _gtk_window_activate_key (GTK_WINDOW (toplevel), event))
627 gtk_menu_shell_enter_notify (GtkWidget *widget,
628 GdkEventCrossing *event)
630 GtkMenuShell *menu_shell;
631 GtkWidget *menu_item;
633 g_return_val_if_fail (GTK_IS_MENU_SHELL (widget), FALSE);
634 g_return_val_if_fail (event != NULL, FALSE);
636 menu_shell = GTK_MENU_SHELL (widget);
638 if (menu_shell->active)
640 menu_item = gtk_get_event_widget ((GdkEvent*) event);
643 (GTK_IS_MENU_ITEM (menu_item) &&
644 !_gtk_menu_item_is_selectable (menu_item)))
647 if ((menu_item->parent == widget) &&
648 (menu_shell->active_menu_item != menu_item) &&
649 GTK_IS_MENU_ITEM (menu_item))
651 if (menu_shell->ignore_enter)
654 if ((event->detail != GDK_NOTIFY_INFERIOR) &&
655 (GTK_WIDGET_STATE (menu_item) != GTK_STATE_PRELIGHT))
657 gtk_menu_shell_select_item (menu_shell, menu_item);
660 else if (menu_shell->parent_menu_shell)
662 gtk_widget_event (menu_shell->parent_menu_shell, (GdkEvent*) event);
670 gtk_menu_shell_leave_notify (GtkWidget *widget,
671 GdkEventCrossing *event)
673 GtkMenuShell *menu_shell;
674 GtkMenuItem *menu_item;
675 GtkWidget *event_widget;
677 g_return_val_if_fail (GTK_IS_MENU_SHELL (widget), FALSE);
678 g_return_val_if_fail (event != NULL, FALSE);
680 if (GTK_WIDGET_VISIBLE (widget))
682 menu_shell = GTK_MENU_SHELL (widget);
683 event_widget = gtk_get_event_widget ((GdkEvent*) event);
685 if (!event_widget || !GTK_IS_MENU_ITEM (event_widget))
688 menu_item = GTK_MENU_ITEM (event_widget);
690 if (menu_shell->ignore_leave)
692 menu_shell->ignore_leave = FALSE;
696 if (!_gtk_menu_item_is_selectable (event_widget))
699 if ((menu_shell->active_menu_item == event_widget) &&
700 (menu_item->submenu == NULL))
702 if ((event->detail != GDK_NOTIFY_INFERIOR) &&
703 (GTK_WIDGET_STATE (menu_item) != GTK_STATE_NORMAL))
705 gtk_menu_shell_deselect (menu_shell);
708 else if (menu_shell->parent_menu_shell)
710 gtk_widget_event (menu_shell->parent_menu_shell, (GdkEvent*) event);
718 gtk_menu_shell_add (GtkContainer *container,
721 gtk_menu_shell_append (GTK_MENU_SHELL (container), widget);
725 gtk_menu_shell_remove (GtkContainer *container,
728 GtkMenuShell *menu_shell;
731 g_return_if_fail (GTK_IS_MENU_SHELL (container));
732 g_return_if_fail (GTK_IS_MENU_ITEM (widget));
734 was_visible = GTK_WIDGET_VISIBLE (widget);
735 menu_shell = GTK_MENU_SHELL (container);
736 menu_shell->children = g_list_remove (menu_shell->children, widget);
738 if (widget == menu_shell->active_menu_item)
740 gtk_item_deselect (GTK_ITEM (menu_shell->active_menu_item));
741 menu_shell->active_menu_item = NULL;
744 gtk_widget_unparent (widget);
746 /* queue resize regardless of GTK_WIDGET_VISIBLE (container),
747 * since that's what is needed by toplevels.
750 gtk_widget_queue_resize (GTK_WIDGET (container));
754 gtk_menu_shell_forall (GtkContainer *container,
755 gboolean include_internals,
756 GtkCallback callback,
757 gpointer callback_data)
759 GtkMenuShell *menu_shell;
763 g_return_if_fail (GTK_IS_MENU_SHELL (container));
764 g_return_if_fail (callback != NULL);
766 menu_shell = GTK_MENU_SHELL (container);
768 children = menu_shell->children;
771 child = children->data;
772 children = children->next;
774 (* callback) (child, callback_data);
780 gtk_real_menu_shell_deactivate (GtkMenuShell *menu_shell)
782 if (menu_shell->active)
784 menu_shell->button = 0;
785 menu_shell->active = FALSE;
787 if (menu_shell->active_menu_item)
789 gtk_menu_item_deselect (GTK_MENU_ITEM (menu_shell->active_menu_item));
790 menu_shell->active_menu_item = NULL;
793 if (menu_shell->have_grab)
795 menu_shell->have_grab = FALSE;
796 gtk_grab_remove (GTK_WIDGET (menu_shell));
798 if (menu_shell->have_xgrab)
800 GdkDisplay *display = gtk_widget_get_display (GTK_WIDGET (menu_shell));
802 menu_shell->have_xgrab = FALSE;
803 gdk_display_pointer_ungrab (display, GDK_CURRENT_TIME);
804 gdk_display_keyboard_ungrab (display, GDK_CURRENT_TIME);
810 gtk_menu_shell_is_item (GtkMenuShell *menu_shell,
815 g_return_val_if_fail (GTK_IS_MENU_SHELL (menu_shell), FALSE);
816 g_return_val_if_fail (child != NULL, FALSE);
818 parent = child->parent;
819 while (parent && GTK_IS_MENU_SHELL (parent))
821 if (parent == (GtkWidget*) menu_shell)
823 parent = GTK_MENU_SHELL (parent)->parent_menu_shell;
830 gtk_menu_shell_get_item (GtkMenuShell *menu_shell,
833 GtkWidget *menu_item;
835 menu_item = gtk_get_event_widget ((GdkEvent*) event);
837 while (menu_item && !GTK_IS_MENU_ITEM (menu_item))
838 menu_item = menu_item->parent;
840 if (menu_item && gtk_menu_shell_is_item (menu_shell, menu_item))
846 /* Handlers for action signals */
849 gtk_menu_shell_select_item (GtkMenuShell *menu_shell,
850 GtkWidget *menu_item)
852 GtkMenuShellClass *class;
854 g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
855 g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
857 class = GTK_MENU_SHELL_GET_CLASS (menu_shell);
859 if (class->select_item)
860 class->select_item (menu_shell, menu_item);
863 void _gtk_menu_item_set_placement (GtkMenuItem *menu_item,
864 GtkSubmenuPlacement placement);
867 gtk_menu_shell_real_select_item (GtkMenuShell *menu_shell,
868 GtkWidget *menu_item)
870 gtk_menu_shell_deselect (menu_shell);
872 if (!_gtk_menu_item_is_selectable (menu_item))
875 menu_shell->active_menu_item = menu_item;
876 _gtk_menu_item_set_placement (GTK_MENU_ITEM (menu_shell->active_menu_item),
877 GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement);
878 gtk_menu_item_select (GTK_MENU_ITEM (menu_shell->active_menu_item));
880 /* This allows the bizarre radio buttons-with-submenus-display-history
883 if (GTK_MENU_ITEM (menu_shell->active_menu_item)->submenu)
884 gtk_widget_activate (menu_shell->active_menu_item);
888 gtk_menu_shell_deselect (GtkMenuShell *menu_shell)
890 g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
892 if (menu_shell->active_menu_item)
894 gtk_menu_item_deselect (GTK_MENU_ITEM (menu_shell->active_menu_item));
895 menu_shell->active_menu_item = NULL;
900 gtk_menu_shell_activate_item (GtkMenuShell *menu_shell,
901 GtkWidget *menu_item,
902 gboolean force_deactivate)
904 GSList *slist, *shells = NULL;
905 gboolean deactivate = force_deactivate;
907 g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
908 g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
911 deactivate = GTK_MENU_ITEM_GET_CLASS (menu_item)->hide_on_activate;
913 gtk_widget_ref (GTK_WIDGET (menu_shell));
917 GtkMenuShell *parent_menu_shell = menu_shell;
921 gtk_widget_ref (GTK_WIDGET (parent_menu_shell));
922 shells = g_slist_prepend (shells, parent_menu_shell);
923 parent_menu_shell = (GtkMenuShell*) parent_menu_shell->parent_menu_shell;
925 while (parent_menu_shell);
926 shells = g_slist_reverse (shells);
928 gtk_menu_shell_deactivate (menu_shell);
930 /* flush the x-queue, so any grabs are removed and
931 * the menu is actually taken down
933 gdk_display_sync (gtk_widget_get_display (menu_item));
936 gtk_widget_activate (menu_item);
938 for (slist = shells; slist; slist = slist->next)
940 gtk_signal_emit (slist->data, menu_shell_signals[SELECTION_DONE]);
941 gtk_widget_unref (slist->data);
943 g_slist_free (shells);
945 gtk_widget_unref (GTK_WIDGET (menu_shell));
948 /* Distance should be +/- 1 */
950 gtk_menu_shell_move_selected (GtkMenuShell *menu_shell,
953 if (menu_shell->active_menu_item)
955 GList *node = g_list_find (menu_shell->children,
956 menu_shell->active_menu_item);
957 GList *start_node = node;
962 while (node != start_node &&
963 (!node || !_gtk_menu_item_is_selectable (node->data)))
966 node = menu_shell->children;
974 while (node != start_node &&
975 (!node || !_gtk_menu_item_is_selectable (node->data)))
978 node = g_list_last (menu_shell->children);
985 gtk_menu_shell_select_item (menu_shell, node->data);
990 * _gtk_menu_shell_select_first:
991 * @menu_shell: a #GtkMenuShell
992 * @search_sensitive: if %TRUE, search for the first selectable
993 * menu item, otherwise select nothing if
994 * the first item isn't sensitive. This
995 * should be %FALSE if the menu is being
996 * popped up initially.
998 * Select the first visible or selectable child of the menu shell;
999 * don't select tearoff items unless the only item is a tearoff
1003 _gtk_menu_shell_select_first (GtkMenuShell *menu_shell,
1004 gboolean search_sensitive)
1006 GtkWidget *to_select = NULL;
1009 tmp_list = menu_shell->children;
1012 GtkWidget *child = tmp_list->data;
1014 if ((!search_sensitive && GTK_WIDGET_VISIBLE (child)) ||
1015 _gtk_menu_item_is_selectable (child))
1018 if (!GTK_IS_TEAROFF_MENU_ITEM (child))
1022 tmp_list = tmp_list->next;
1026 gtk_menu_shell_select_item (menu_shell, to_select);
1030 gtk_menu_shell_select_last (GtkMenuShell *menu_shell,
1031 gboolean search_sensitive)
1033 GtkWidget *to_select = NULL;
1036 tmp_list = g_list_last (menu_shell->children);
1039 GtkWidget *child = tmp_list->data;
1041 if ((!search_sensitive && GTK_WIDGET_VISIBLE (child)) ||
1042 _gtk_menu_item_is_selectable (child))
1045 if (!GTK_IS_TEAROFF_MENU_ITEM (child))
1049 tmp_list = tmp_list->prev;
1053 gtk_menu_shell_select_item (menu_shell, to_select);
1057 gtk_menu_shell_select_submenu_first (GtkMenuShell *menu_shell)
1059 GtkMenuItem *menu_item;
1061 menu_item = GTK_MENU_ITEM (menu_shell->active_menu_item);
1063 if (menu_item->submenu)
1064 _gtk_menu_shell_select_first (GTK_MENU_SHELL (menu_item->submenu), TRUE);
1068 gtk_real_menu_shell_move_current (GtkMenuShell *menu_shell,
1069 GtkMenuDirectionType direction)
1071 GtkMenuShell *parent_menu_shell = NULL;
1072 gboolean had_selection;
1074 had_selection = menu_shell->active_menu_item != NULL;
1076 if (menu_shell->parent_menu_shell)
1077 parent_menu_shell = GTK_MENU_SHELL (menu_shell->parent_menu_shell);
1081 case GTK_MENU_DIR_PARENT:
1082 if (parent_menu_shell)
1084 if (GTK_MENU_SHELL_GET_CLASS (parent_menu_shell)->submenu_placement ==
1085 GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement)
1086 gtk_menu_shell_deselect (menu_shell);
1089 gtk_menu_shell_move_selected (parent_menu_shell, -1);
1090 gtk_menu_shell_select_submenu_first (parent_menu_shell);
1095 case GTK_MENU_DIR_CHILD:
1096 if (menu_shell->active_menu_item &&
1097 _gtk_menu_item_is_selectable (menu_shell->active_menu_item) &&
1098 GTK_MENU_ITEM (menu_shell->active_menu_item)->submenu)
1100 gtk_menu_shell_select_submenu_first (menu_shell);
1104 /* Try to find a menu running the opposite direction */
1105 while (parent_menu_shell &&
1106 (GTK_MENU_SHELL_GET_CLASS (parent_menu_shell)->submenu_placement ==
1107 GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement))
1109 GtkWidget *tmp_widget = parent_menu_shell->parent_menu_shell;
1112 parent_menu_shell = GTK_MENU_SHELL (tmp_widget);
1114 parent_menu_shell = NULL;
1117 if (parent_menu_shell)
1119 gtk_menu_shell_move_selected (parent_menu_shell, 1);
1120 gtk_menu_shell_select_submenu_first (parent_menu_shell);
1125 case GTK_MENU_DIR_PREV:
1126 gtk_menu_shell_move_selected (menu_shell, -1);
1127 if (!had_selection &&
1128 !menu_shell->active_menu_item &&
1129 menu_shell->children)
1130 gtk_menu_shell_select_last (menu_shell, TRUE);
1132 case GTK_MENU_DIR_NEXT:
1133 gtk_menu_shell_move_selected (menu_shell, 1);
1134 if (!had_selection &&
1135 !menu_shell->active_menu_item &&
1136 menu_shell->children)
1137 _gtk_menu_shell_select_first (menu_shell, TRUE);
1144 gtk_real_menu_shell_activate_current (GtkMenuShell *menu_shell,
1145 gboolean force_hide)
1147 if (menu_shell->active_menu_item &&
1148 _gtk_menu_item_is_selectable (menu_shell->active_menu_item) &&
1149 GTK_MENU_ITEM (menu_shell->active_menu_item)->submenu == NULL)
1151 gtk_menu_shell_activate_item (menu_shell,
1152 menu_shell->active_menu_item,
1158 gtk_real_menu_shell_cancel (GtkMenuShell *menu_shell)
1160 /* Unset the active menu item so gtk_menu_popdown() doesn't see it.
1162 gtk_menu_shell_deselect (menu_shell);
1164 gtk_menu_shell_deactivate (menu_shell);
1165 gtk_signal_emit (GTK_OBJECT (menu_shell), menu_shell_signals[SELECTION_DONE]);
1169 gtk_real_menu_shell_cycle_focus (GtkMenuShell *menu_shell,
1170 GtkDirectionType dir)
1172 while (menu_shell && !GTK_IS_MENU_BAR (menu_shell))
1174 if (menu_shell->parent_menu_shell)
1175 menu_shell = GTK_MENU_SHELL (menu_shell->parent_menu_shell);
1181 _gtk_menu_bar_cycle_focus (GTK_MENU_BAR (menu_shell), dir);