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 #include "gdk/gdkkeysyms.h"
28 #include "gtkbindings.h"
30 #include "gtkmenuitem.h"
31 #include "gtktearoffmenuitem.h" /* FIXME */
32 #include "gtkmenushell.h"
33 #include "gtksignal.h"
34 #include "gtkwindow.h"
36 #define MENU_SHELL_TIMEOUT 500
48 typedef void (*GtkMenuShellSignal1) (GtkObject *object,
49 GtkMenuDirectionType arg1,
51 typedef void (*GtkMenuShellSignal2) (GtkObject *object,
57 * A menu item can be "selected", this means that it is displayed
58 * in the prelight state, and if it has a submenu, that submenu
61 * A menu is "active" when it is visible onscreen and the user
62 * is selecting from it. A menubar is not active until the user
63 * clicks on one of its menuitems. When a menu is active,
64 * passing the mouse over a submenu will pop it up.
66 * menu_shell->active_menu_item, is however, not an "active"
67 * menu item (there is no such thing) but rather, the selected
68 * menu item in that MenuShell, if there is one.
70 * There is also is a concept of the current menu and a current
71 * menu item. The current menu item is the selected menu item
72 * that is furthest down in the heirarchy. (Every active menu_shell
73 * does not necessarily contain a selected menu item, but if
74 * it does, then menu_shell->parent_menu_shell must also contain
75 * a selected menu item. The current menu is the menu that
76 * contains the current menu_item. It will always have a GTK
77 * grab and receive all key presses.
82 * ::move_current (GtkMenuDirection *dir)
83 * Moves the current menu item in direction 'dir':
85 * GTK_MENU_DIR_PARENT: To the parent menu shell
86 * GTK_MENU_DIR_CHILD: To the child menu shell (if this item has
88 * GTK_MENU_DIR_NEXT/PREV: To the next or previous item
91 * As a a bit of a hack to get movement between menus and
92 * menubars working, if submenu_placement is different for
93 * the menu and its MenuShell then the following apply:
95 * - For 'parent' the current menu is not just moved to
96 * the parent, but moved to the previous entry in the parent
97 * - For 'child', if there is no child, then current is
98 * moved to the next item in the parent.
101 * ::activate_current (GBoolean *force_hide)
102 * Activate the current item. If 'force_hide' is true, hide
103 * the current menu item always. Otherwise, only hide
104 * it if menu_item->klass->hide_on_activate is true.
107 * Cancels the current selection
110 static void gtk_menu_shell_class_init (GtkMenuShellClass *klass);
111 static void gtk_menu_shell_init (GtkMenuShell *menu_shell);
112 static void gtk_menu_shell_map (GtkWidget *widget);
113 static void gtk_menu_shell_realize (GtkWidget *widget);
114 static gint gtk_menu_shell_button_press (GtkWidget *widget,
115 GdkEventButton *event);
116 static gint gtk_menu_shell_button_release (GtkWidget *widget,
117 GdkEventButton *event);
118 static gint gtk_menu_shell_key_press (GtkWidget *widget,
120 static gint gtk_menu_shell_enter_notify (GtkWidget *widget,
121 GdkEventCrossing *event);
122 static gint gtk_menu_shell_leave_notify (GtkWidget *widget,
123 GdkEventCrossing *event);
124 static void gtk_menu_shell_add (GtkContainer *container,
126 static void gtk_menu_shell_remove (GtkContainer *container,
128 static void gtk_menu_shell_forall (GtkContainer *container,
129 gboolean include_internals,
130 GtkCallback callback,
131 gpointer callback_data);
132 static void gtk_menu_shell_real_insert (GtkMenuShell *menu_shell,
135 static void gtk_real_menu_shell_deactivate (GtkMenuShell *menu_shell);
136 static gint gtk_menu_shell_is_item (GtkMenuShell *menu_shell,
138 static GtkWidget *gtk_menu_shell_get_item (GtkMenuShell *menu_shell,
140 static GtkType gtk_menu_shell_child_type (GtkContainer *container);
141 static void gtk_menu_shell_real_select_item (GtkMenuShell *menu_shell,
142 GtkWidget *menu_item);
143 static void gtk_menu_shell_select_submenu_first (GtkMenuShell *menu_shell);
145 static void gtk_real_menu_shell_move_current (GtkMenuShell *menu_shell,
146 GtkMenuDirectionType direction);
147 static void gtk_real_menu_shell_activate_current (GtkMenuShell *menu_shell,
148 gboolean force_hide);
149 static void gtk_real_menu_shell_cancel (GtkMenuShell *menu_shell);
151 static GtkContainerClass *parent_class = NULL;
152 static guint menu_shell_signals[LAST_SIGNAL] = { 0 };
156 gtk_menu_shell_get_type (void)
158 static GtkType menu_shell_type = 0;
160 if (!menu_shell_type)
162 static const GtkTypeInfo menu_shell_info =
165 sizeof (GtkMenuShell),
166 sizeof (GtkMenuShellClass),
167 (GtkClassInitFunc) gtk_menu_shell_class_init,
168 (GtkObjectInitFunc) gtk_menu_shell_init,
169 /* reserved_1 */ NULL,
170 /* reserved_2 */ NULL,
171 (GtkClassInitFunc) NULL,
174 menu_shell_type = gtk_type_unique (gtk_container_get_type (), &menu_shell_info);
177 return menu_shell_type;
181 gtk_menu_shell_class_init (GtkMenuShellClass *klass)
183 GtkObjectClass *object_class;
184 GtkWidgetClass *widget_class;
185 GtkContainerClass *container_class;
187 GtkBindingSet *binding_set;
189 object_class = (GtkObjectClass*) klass;
190 widget_class = (GtkWidgetClass*) klass;
191 container_class = (GtkContainerClass*) klass;
193 parent_class = gtk_type_class (gtk_container_get_type ());
195 widget_class->map = gtk_menu_shell_map;
196 widget_class->realize = gtk_menu_shell_realize;
197 widget_class->button_press_event = gtk_menu_shell_button_press;
198 widget_class->button_release_event = gtk_menu_shell_button_release;
199 widget_class->key_press_event = gtk_menu_shell_key_press;
200 widget_class->enter_notify_event = gtk_menu_shell_enter_notify;
201 widget_class->leave_notify_event = gtk_menu_shell_leave_notify;
203 container_class->add = gtk_menu_shell_add;
204 container_class->remove = gtk_menu_shell_remove;
205 container_class->forall = gtk_menu_shell_forall;
206 container_class->child_type = gtk_menu_shell_child_type;
208 klass->submenu_placement = GTK_TOP_BOTTOM;
209 klass->deactivate = gtk_real_menu_shell_deactivate;
210 klass->selection_done = NULL;
211 klass->move_current = gtk_real_menu_shell_move_current;
212 klass->activate_current = gtk_real_menu_shell_activate_current;
213 klass->cancel = gtk_real_menu_shell_cancel;
214 klass->select_item = gtk_menu_shell_real_select_item;
215 klass->insert = gtk_menu_shell_real_insert;
217 menu_shell_signals[DEACTIVATE] =
218 gtk_signal_new ("deactivate",
220 GTK_CLASS_TYPE (object_class),
221 GTK_SIGNAL_OFFSET (GtkMenuShellClass, deactivate),
222 gtk_marshal_VOID__VOID,
224 menu_shell_signals[SELECTION_DONE] =
225 gtk_signal_new ("selection-done",
227 GTK_CLASS_TYPE (object_class),
228 GTK_SIGNAL_OFFSET (GtkMenuShellClass, selection_done),
229 gtk_marshal_VOID__VOID,
231 menu_shell_signals[MOVE_CURRENT] =
232 gtk_signal_new ("move_current",
233 GTK_RUN_LAST | GTK_RUN_ACTION,
234 GTK_CLASS_TYPE (object_class),
235 GTK_SIGNAL_OFFSET (GtkMenuShellClass, move_current),
236 gtk_marshal_VOID__ENUM,
238 GTK_TYPE_MENU_DIRECTION_TYPE);
239 menu_shell_signals[ACTIVATE_CURRENT] =
240 gtk_signal_new ("activate_current",
241 GTK_RUN_LAST | GTK_RUN_ACTION,
242 GTK_CLASS_TYPE (object_class),
243 GTK_SIGNAL_OFFSET (GtkMenuShellClass, activate_current),
244 gtk_marshal_VOID__BOOLEAN,
247 menu_shell_signals[CANCEL] =
248 gtk_signal_new ("cancel",
249 GTK_RUN_LAST | GTK_RUN_ACTION,
250 GTK_CLASS_TYPE (object_class),
251 GTK_SIGNAL_OFFSET (GtkMenuShellClass, cancel),
252 gtk_marshal_VOID__VOID,
255 binding_set = gtk_binding_set_by_class (klass);
256 gtk_binding_entry_add_signal (binding_set,
259 gtk_binding_entry_add_signal (binding_set,
261 "activate_current", 1,
264 gtk_binding_entry_add_signal (binding_set,
266 "activate_current", 1,
272 gtk_menu_shell_child_type (GtkContainer *container)
274 return GTK_TYPE_MENU_ITEM;
278 gtk_menu_shell_init (GtkMenuShell *menu_shell)
280 menu_shell->children = NULL;
281 menu_shell->active_menu_item = NULL;
282 menu_shell->parent_menu_shell = NULL;
283 menu_shell->active = FALSE;
284 menu_shell->have_grab = FALSE;
285 menu_shell->have_xgrab = FALSE;
286 menu_shell->ignore_leave = FALSE;
287 menu_shell->button = 0;
288 menu_shell->menu_flag = 0;
289 menu_shell->activate_time = 0;
293 gtk_menu_shell_append (GtkMenuShell *menu_shell,
296 gtk_menu_shell_insert (menu_shell, child, -1);
300 gtk_menu_shell_prepend (GtkMenuShell *menu_shell,
303 gtk_menu_shell_insert (menu_shell, child, 0);
307 gtk_menu_shell_insert (GtkMenuShell *menu_shell,
311 GtkMenuShellClass *class;
313 g_return_if_fail (menu_shell != NULL);
314 g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
315 g_return_if_fail (child != NULL);
316 g_return_if_fail (GTK_IS_MENU_ITEM (child));
318 class = GTK_MENU_SHELL_GET_CLASS (menu_shell);
321 class->insert (menu_shell, child, position);
325 gtk_menu_shell_real_insert (GtkMenuShell *menu_shell,
329 menu_shell->children = g_list_insert (menu_shell->children, child, position);
331 gtk_widget_set_parent (child, GTK_WIDGET (menu_shell));
333 if (GTK_WIDGET_REALIZED (child->parent))
334 gtk_widget_realize (child);
336 if (GTK_WIDGET_VISIBLE (child->parent) && GTK_WIDGET_VISIBLE (child))
338 if (GTK_WIDGET_MAPPED (child->parent))
339 gtk_widget_map (child);
341 gtk_widget_queue_resize (child);
346 gtk_menu_shell_deactivate (GtkMenuShell *menu_shell)
348 g_return_if_fail (menu_shell != NULL);
349 g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
351 gtk_signal_emit (GTK_OBJECT (menu_shell), menu_shell_signals[DEACTIVATE]);
355 gtk_menu_shell_map (GtkWidget *widget)
357 GtkMenuShell *menu_shell;
361 g_return_if_fail (widget != NULL);
362 g_return_if_fail (GTK_IS_MENU_SHELL (widget));
364 menu_shell = GTK_MENU_SHELL (widget);
365 GTK_WIDGET_SET_FLAGS (menu_shell, GTK_MAPPED);
367 children = menu_shell->children;
370 child = children->data;
371 children = children->next;
373 if (GTK_WIDGET_VISIBLE (child) && !GTK_WIDGET_MAPPED (child))
374 gtk_widget_map (child);
377 gdk_window_show (widget->window);
381 gtk_menu_shell_realize (GtkWidget *widget)
383 GdkWindowAttr attributes;
384 gint attributes_mask;
386 g_return_if_fail (widget != NULL);
387 g_return_if_fail (GTK_IS_MENU_SHELL (widget));
389 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
391 attributes.x = widget->allocation.x;
392 attributes.y = widget->allocation.y;
393 attributes.width = widget->allocation.width;
394 attributes.height = widget->allocation.height;
395 attributes.window_type = GDK_WINDOW_CHILD;
396 attributes.wclass = GDK_INPUT_OUTPUT;
397 attributes.visual = gtk_widget_get_visual (widget);
398 attributes.colormap = gtk_widget_get_colormap (widget);
399 attributes.event_mask = gtk_widget_get_events (widget);
400 attributes.event_mask |= (GDK_EXPOSURE_MASK |
401 GDK_BUTTON_PRESS_MASK |
402 GDK_BUTTON_RELEASE_MASK |
404 GDK_ENTER_NOTIFY_MASK |
405 GDK_LEAVE_NOTIFY_MASK);
407 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
408 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
409 gdk_window_set_user_data (widget->window, widget);
411 widget->style = gtk_style_attach (widget->style, widget->window);
412 gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
416 gtk_menu_shell_button_press (GtkWidget *widget,
417 GdkEventButton *event)
419 GtkMenuShell *menu_shell;
420 GtkWidget *menu_item;
422 g_return_val_if_fail (widget != NULL, FALSE);
423 g_return_val_if_fail (GTK_IS_MENU_SHELL (widget), FALSE);
424 g_return_val_if_fail (event != NULL, FALSE);
426 if (event->type != GDK_BUTTON_PRESS)
429 menu_shell = GTK_MENU_SHELL (widget);
431 if (menu_shell->parent_menu_shell)
433 return gtk_widget_event (menu_shell->parent_menu_shell, (GdkEvent*) event);
435 else if (!menu_shell->active || !menu_shell->button)
437 if (!menu_shell->active)
439 gtk_grab_add (GTK_WIDGET (widget));
440 menu_shell->have_grab = TRUE;
441 menu_shell->active = TRUE;
443 menu_shell->button = event->button;
445 menu_item = gtk_menu_shell_get_item (menu_shell, (GdkEvent *)event);
448 GTK_WIDGET_IS_SENSITIVE (menu_item))
450 if ((menu_item->parent == widget) &&
451 (menu_item != menu_shell->active_menu_item))
452 gtk_menu_shell_select_item (menu_shell, menu_item);
457 widget = gtk_get_event_widget ((GdkEvent*) event);
458 if (widget == GTK_WIDGET (menu_shell))
460 gtk_menu_shell_deactivate (menu_shell);
461 gtk_signal_emit (GTK_OBJECT (menu_shell), menu_shell_signals[SELECTION_DONE]);
469 gtk_menu_shell_button_release (GtkWidget *widget,
470 GdkEventButton *event)
472 GtkMenuShell *menu_shell;
473 GtkWidget *menu_item;
476 g_return_val_if_fail (widget != NULL, FALSE);
477 g_return_val_if_fail (GTK_IS_MENU_SHELL (widget), FALSE);
478 g_return_val_if_fail (event != NULL, FALSE);
480 menu_shell = GTK_MENU_SHELL (widget);
481 if (menu_shell->active)
483 if (menu_shell->button && (event->button != menu_shell->button))
485 menu_shell->button = 0;
486 if (menu_shell->parent_menu_shell)
487 return gtk_widget_event (menu_shell->parent_menu_shell, (GdkEvent*) event);
490 menu_shell->button = 0;
491 menu_item = gtk_menu_shell_get_item (menu_shell, (GdkEvent*) event);
495 if ((event->time - menu_shell->activate_time) > MENU_SHELL_TIMEOUT)
497 if (menu_item && (menu_shell->active_menu_item == menu_item) &&
498 GTK_WIDGET_IS_SENSITIVE (menu_item))
500 if (GTK_MENU_ITEM (menu_item)->submenu == NULL)
502 gtk_menu_shell_activate_item (menu_shell, menu_item, TRUE);
506 else if (menu_shell->parent_menu_shell)
508 menu_shell->active = TRUE;
509 gtk_widget_event (menu_shell->parent_menu_shell, (GdkEvent*) event);
515 /* We only ever want to prevent deactivation on the first
516 * press/release. Setting the time to zero is a bit of a
517 * hack, since we could be being triggered in the first
518 * few fractions of a second after a server time wraparound.
519 * the chances of that happening are ~1/10^6, without
520 * serious harm if we lose.
522 menu_shell->activate_time = 0;
526 /* If the button click was very fast, or we ended up on a submenu,
530 (menu_item && (menu_shell->active_menu_item == menu_item)))
533 menu_shell->ignore_leave = TRUE;
540 gtk_menu_shell_deactivate (menu_shell);
541 gtk_signal_emit (GTK_OBJECT (menu_shell), menu_shell_signals[SELECTION_DONE]);
549 gtk_menu_shell_key_press (GtkWidget *widget,
552 GtkMenuShell *menu_shell;
555 g_return_val_if_fail (widget != NULL, FALSE);
556 g_return_val_if_fail (GTK_IS_MENU_SHELL (widget), FALSE);
557 g_return_val_if_fail (event != NULL, FALSE);
559 menu_shell = GTK_MENU_SHELL (widget);
561 if (!menu_shell->active_menu_item && menu_shell->parent_menu_shell)
562 return gtk_widget_event (menu_shell->parent_menu_shell, (GdkEvent *)event);
564 if (gtk_bindings_activate (GTK_OBJECT (widget),
569 toplevel = gtk_widget_get_toplevel (widget);
570 if (GTK_IS_WINDOW (toplevel) &&
571 gtk_window_mnemonic_activate (GTK_WINDOW (toplevel),
576 if (gtk_accel_groups_activate (GTK_OBJECT (widget), event->keyval, event->state))
583 gtk_menu_shell_enter_notify (GtkWidget *widget,
584 GdkEventCrossing *event)
586 GtkMenuShell *menu_shell;
587 GtkWidget *menu_item;
589 g_return_val_if_fail (widget != NULL, FALSE);
590 g_return_val_if_fail (GTK_IS_MENU_SHELL (widget), FALSE);
591 g_return_val_if_fail (event != NULL, FALSE);
593 menu_shell = GTK_MENU_SHELL (widget);
595 if (menu_shell->active)
597 menu_item = gtk_get_event_widget ((GdkEvent*) event);
599 if (!menu_item || !GTK_WIDGET_IS_SENSITIVE (menu_item))
602 if ((menu_item->parent == widget) &&
603 (menu_shell->active_menu_item != menu_item) &&
604 GTK_IS_MENU_ITEM (menu_item))
606 if (menu_shell->ignore_enter)
609 if ((event->detail != GDK_NOTIFY_INFERIOR) &&
610 (GTK_WIDGET_STATE (menu_item) != GTK_STATE_PRELIGHT))
612 gtk_menu_shell_select_item (menu_shell, menu_item);
615 else if (menu_shell->parent_menu_shell)
617 gtk_widget_event (menu_shell->parent_menu_shell, (GdkEvent*) event);
625 gtk_menu_shell_leave_notify (GtkWidget *widget,
626 GdkEventCrossing *event)
628 GtkMenuShell *menu_shell;
629 GtkMenuItem *menu_item;
630 GtkWidget *event_widget;
632 g_return_val_if_fail (widget != NULL, FALSE);
633 g_return_val_if_fail (GTK_IS_MENU_SHELL (widget), FALSE);
634 g_return_val_if_fail (event != NULL, FALSE);
636 if (GTK_WIDGET_VISIBLE (widget))
638 menu_shell = GTK_MENU_SHELL (widget);
639 event_widget = gtk_get_event_widget ((GdkEvent*) event);
641 if (!event_widget || !GTK_IS_MENU_ITEM (event_widget))
644 menu_item = GTK_MENU_ITEM (event_widget);
646 if (menu_shell->ignore_leave)
648 menu_shell->ignore_leave = FALSE;
652 if (!GTK_WIDGET_IS_SENSITIVE (menu_item))
655 if ((menu_shell->active_menu_item == event_widget) &&
656 (menu_item->submenu == NULL))
658 if ((event->detail != GDK_NOTIFY_INFERIOR) &&
659 (GTK_WIDGET_STATE (menu_item) != GTK_STATE_NORMAL))
661 gtk_menu_shell_deselect (menu_shell);
664 else if (menu_shell->parent_menu_shell)
666 gtk_widget_event (menu_shell->parent_menu_shell, (GdkEvent*) event);
674 gtk_menu_shell_add (GtkContainer *container,
677 gtk_menu_shell_append (GTK_MENU_SHELL (container), widget);
681 gtk_menu_shell_remove (GtkContainer *container,
684 GtkMenuShell *menu_shell;
687 g_return_if_fail (container != NULL);
688 g_return_if_fail (GTK_IS_MENU_SHELL (container));
689 g_return_if_fail (widget != NULL);
690 g_return_if_fail (GTK_IS_MENU_ITEM (widget));
692 was_visible = GTK_WIDGET_VISIBLE (widget);
693 menu_shell = GTK_MENU_SHELL (container);
694 menu_shell->children = g_list_remove (menu_shell->children, widget);
696 if (widget == menu_shell->active_menu_item)
698 gtk_item_deselect (GTK_ITEM (menu_shell->active_menu_item));
699 menu_shell->active_menu_item = NULL;
702 gtk_widget_unparent (widget);
704 /* queue resize regardless of GTK_WIDGET_VISIBLE (container),
705 * since that's what is needed by toplevels.
708 gtk_widget_queue_resize (GTK_WIDGET (container));
712 gtk_menu_shell_forall (GtkContainer *container,
713 gboolean include_internals,
714 GtkCallback callback,
715 gpointer callback_data)
717 GtkMenuShell *menu_shell;
721 g_return_if_fail (container != NULL);
722 g_return_if_fail (GTK_IS_MENU_SHELL (container));
723 g_return_if_fail (callback != NULL);
725 menu_shell = GTK_MENU_SHELL (container);
727 children = menu_shell->children;
730 child = children->data;
731 children = children->next;
733 (* callback) (child, callback_data);
739 gtk_real_menu_shell_deactivate (GtkMenuShell *menu_shell)
741 if (menu_shell->active)
743 menu_shell->button = 0;
744 menu_shell->active = FALSE;
746 if (menu_shell->active_menu_item)
748 gtk_menu_item_deselect (GTK_MENU_ITEM (menu_shell->active_menu_item));
749 menu_shell->active_menu_item = NULL;
752 if (menu_shell->have_grab)
754 menu_shell->have_grab = FALSE;
755 gtk_grab_remove (GTK_WIDGET (menu_shell));
757 if (menu_shell->have_xgrab)
759 menu_shell->have_xgrab = FALSE;
760 gdk_pointer_ungrab (GDK_CURRENT_TIME);
761 gdk_keyboard_ungrab (GDK_CURRENT_TIME);
767 gtk_menu_shell_is_item (GtkMenuShell *menu_shell,
772 g_return_val_if_fail (menu_shell != NULL, FALSE);
773 g_return_val_if_fail (GTK_IS_MENU_SHELL (menu_shell), FALSE);
774 g_return_val_if_fail (child != NULL, FALSE);
776 parent = child->parent;
777 while (parent && GTK_IS_MENU_SHELL (parent))
779 if (parent == (GtkWidget*) menu_shell)
781 parent = GTK_MENU_SHELL (parent)->parent_menu_shell;
788 gtk_menu_shell_get_item (GtkMenuShell *menu_shell,
791 GtkWidget *menu_item;
793 menu_item = gtk_get_event_widget ((GdkEvent*) event);
795 while (menu_item && !GTK_IS_MENU_ITEM (menu_item))
796 menu_item = menu_item->parent;
798 if (menu_item && gtk_menu_shell_is_item (menu_shell, menu_item))
804 /* Handlers for action signals */
807 gtk_menu_shell_select_item (GtkMenuShell *menu_shell,
808 GtkWidget *menu_item)
810 GtkMenuShellClass *class;
812 g_return_if_fail (menu_shell != NULL);
813 g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
814 g_return_if_fail (menu_item != NULL);
815 g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
817 class = GTK_MENU_SHELL_GET_CLASS (menu_shell);
819 if (class->select_item)
820 class->select_item (menu_shell, menu_item);
825 gtk_menu_shell_real_select_item (GtkMenuShell *menu_shell,
826 GtkWidget *menu_item)
828 gtk_menu_shell_deselect (menu_shell);
830 menu_shell->active_menu_item = menu_item;
831 gtk_menu_item_set_placement (GTK_MENU_ITEM (menu_shell->active_menu_item),
832 GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement);
833 gtk_menu_item_select (GTK_MENU_ITEM (menu_shell->active_menu_item));
835 /* This allows the bizarre radio buttons-with-submenus-display-history
838 if (GTK_MENU_ITEM (menu_shell->active_menu_item)->submenu)
839 gtk_widget_activate (menu_shell->active_menu_item);
843 gtk_menu_shell_deselect (GtkMenuShell *menu_shell)
845 g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
847 if (menu_shell->active_menu_item)
849 gtk_menu_item_deselect (GTK_MENU_ITEM (menu_shell->active_menu_item));
850 menu_shell->active_menu_item = NULL;
855 gtk_menu_shell_activate_item (GtkMenuShell *menu_shell,
856 GtkWidget *menu_item,
857 gboolean force_deactivate)
859 GSList *slist, *shells = NULL;
860 gboolean deactivate = force_deactivate;
862 g_return_if_fail (menu_shell != NULL);
863 g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
864 g_return_if_fail (menu_item != NULL);
865 g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
868 deactivate = GTK_MENU_ITEM_GET_CLASS (menu_item)->hide_on_activate;
870 gtk_widget_ref (GTK_WIDGET (menu_shell));
874 GtkMenuShell *parent_menu_shell = menu_shell;
878 gtk_widget_ref (GTK_WIDGET (parent_menu_shell));
879 shells = g_slist_prepend (shells, parent_menu_shell);
880 parent_menu_shell = (GtkMenuShell*) parent_menu_shell->parent_menu_shell;
882 while (parent_menu_shell);
883 shells = g_slist_reverse (shells);
885 gtk_menu_shell_deactivate (menu_shell);
887 /* flush the x-queue, so any grabs are removed and
888 * the menu is actually taken down
893 gtk_widget_activate (menu_item);
895 for (slist = shells; slist; slist = slist->next)
897 gtk_signal_emit (slist->data, menu_shell_signals[SELECTION_DONE]);
898 gtk_widget_unref (slist->data);
900 g_slist_free (shells);
902 gtk_widget_unref (GTK_WIDGET (menu_shell));
905 /* Distance should be +/- 1 */
907 gtk_menu_shell_move_selected (GtkMenuShell *menu_shell,
910 if (menu_shell->active_menu_item)
912 GList *node = g_list_find (menu_shell->children,
913 menu_shell->active_menu_item);
914 GList *start_node = node;
919 while (node != start_node &&
921 !GTK_WIDGET_IS_SENSITIVE (node->data) ||
922 !GTK_WIDGET_VISIBLE (node->data) ))
925 node = menu_shell->children;
933 while (node != start_node &&
935 !GTK_WIDGET_IS_SENSITIVE (node->data) ||
936 !GTK_WIDGET_VISIBLE (node->data) ))
939 node = g_list_last (menu_shell->children);
946 gtk_menu_shell_select_item (menu_shell, node->data);
951 gtk_menu_shell_select_submenu_first (GtkMenuShell *menu_shell)
953 GtkMenuItem *menu_item;
955 menu_item = GTK_MENU_ITEM (menu_shell->active_menu_item);
957 if (menu_item->submenu)
959 GtkMenuShell *submenu = GTK_MENU_SHELL (menu_item->submenu);
960 if (submenu->children)
961 gtk_menu_shell_select_item (submenu, submenu->children->data);
966 gtk_real_menu_shell_move_current (GtkMenuShell *menu_shell,
967 GtkMenuDirectionType direction)
969 GtkMenuShell *parent_menu_shell = NULL;
970 gboolean had_selection;
972 had_selection = menu_shell->active_menu_item != NULL;
974 if (menu_shell->parent_menu_shell)
975 parent_menu_shell = GTK_MENU_SHELL (menu_shell->parent_menu_shell);
979 case GTK_MENU_DIR_PARENT:
980 if (parent_menu_shell)
982 if (GTK_MENU_SHELL_GET_CLASS (parent_menu_shell)->submenu_placement ==
983 GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement)
984 gtk_menu_shell_deselect (menu_shell);
987 gtk_menu_shell_move_selected (parent_menu_shell, -1);
988 gtk_menu_shell_select_submenu_first (parent_menu_shell);
993 case GTK_MENU_DIR_CHILD:
994 if (menu_shell->active_menu_item &&
995 GTK_BIN (menu_shell->active_menu_item)->child &&
996 GTK_MENU_ITEM (menu_shell->active_menu_item)->submenu)
998 menu_shell = GTK_MENU_SHELL (GTK_MENU_ITEM (menu_shell->active_menu_item)->submenu);
999 if (menu_shell->children)
1000 gtk_menu_shell_select_item (menu_shell, menu_shell->children->data);
1004 /* Try to find a menu running the opposite direction */
1005 while (parent_menu_shell &&
1006 (GTK_MENU_SHELL_GET_CLASS (parent_menu_shell)->submenu_placement ==
1007 GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement))
1008 parent_menu_shell = GTK_MENU_SHELL (parent_menu_shell->parent_menu_shell);
1010 if (parent_menu_shell)
1012 gtk_menu_shell_move_selected (parent_menu_shell, 1);
1013 gtk_menu_shell_select_submenu_first (parent_menu_shell);
1018 case GTK_MENU_DIR_PREV:
1019 gtk_menu_shell_move_selected (menu_shell, -1);
1020 if (!had_selection &&
1021 !menu_shell->active_menu_item &&
1022 menu_shell->children)
1023 gtk_menu_shell_select_item (menu_shell, g_list_last (menu_shell->children)->data);
1025 case GTK_MENU_DIR_NEXT:
1026 gtk_menu_shell_move_selected (menu_shell, 1);
1027 if (!had_selection &&
1028 !menu_shell->active_menu_item &&
1029 menu_shell->children)
1030 gtk_menu_shell_select_item (menu_shell, menu_shell->children->data);
1037 gtk_real_menu_shell_activate_current (GtkMenuShell *menu_shell,
1038 gboolean force_hide)
1040 if (menu_shell->active_menu_item &&
1041 GTK_WIDGET_IS_SENSITIVE (menu_shell->active_menu_item) &&
1042 GTK_MENU_ITEM (menu_shell->active_menu_item)->submenu == NULL)
1044 gtk_menu_shell_activate_item (menu_shell,
1045 menu_shell->active_menu_item,
1051 gtk_real_menu_shell_cancel (GtkMenuShell *menu_shell)
1053 /* Unset the active menu item so gtk_menu_popdown() doesn't see it.
1055 gtk_menu_shell_deselect (menu_shell);
1057 gtk_menu_shell_deactivate (menu_shell);
1058 gtk_signal_emit (GTK_OBJECT (menu_shell), menu_shell_signals[SELECTION_DONE]);