]> Pileus Git - ~andy/gtk/blob - gtk/gtkmenushell.c
Merge the xi2-for-master branch
[~andy/gtk] / gtk / gtkmenushell.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 /*
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/. 
25  */
26
27 #define GTK_MENU_INTERNALS
28
29 #include "config.h"
30 #include "gdk/gdkkeysyms.h"
31 #include "gtkbindings.h"
32 #include "gtkkeyhash.h"
33 #include "gtklabel.h"
34 #include "gtkmain.h"
35 #include "gtkmarshalers.h"
36 #include "gtkmenu.h"
37 #include "gtkmenubar.h"
38 #include "gtkmenuitem.h"
39 #include "gtkmenushell.h"
40 #include "gtkmnemonichash.h"
41 #include "gtktearoffmenuitem.h"
42 #include "gtkwindow.h"
43 #include "gtkprivate.h"
44 #include "gtkintl.h"
45 #include "gtkalias.h"
46
47 #define MENU_SHELL_TIMEOUT   500
48
49 #define PACK_DIRECTION(m)                                 \
50    (GTK_IS_MENU_BAR (m)                                   \
51      ? gtk_menu_bar_get_pack_direction (GTK_MENU_BAR (m)) \
52      : GTK_PACK_DIRECTION_LTR)
53
54 enum {
55   DEACTIVATE,
56   SELECTION_DONE,
57   MOVE_CURRENT,
58   ACTIVATE_CURRENT,
59   CANCEL,
60   CYCLE_FOCUS,
61   MOVE_SELECTED,
62   LAST_SIGNAL
63 };
64
65 enum {
66   PROP_0,
67   PROP_TAKE_FOCUS
68 };
69
70 /* Terminology:
71  * 
72  * A menu item can be "selected", this means that it is displayed
73  * in the prelight state, and if it has a submenu, that submenu
74  * will be popped up. 
75  * 
76  * A menu is "active" when it is visible onscreen and the user
77  * is selecting from it. A menubar is not active until the user
78  * clicks on one of its menuitems. When a menu is active,
79  * passing the mouse over a submenu will pop it up.
80  *
81  * menu_shell->active_menu_item, is however, not an "active"
82  * menu item (there is no such thing) but rather, the selected
83  * menu item in that MenuShell, if there is one.
84  *
85  * There is also is a concept of the current menu and a current
86  * menu item. The current menu item is the selected menu item
87  * that is furthest down in the hierarchy. (Every active menu_shell
88  * does not necessarily contain a selected menu item, but if
89  * it does, then menu_shell->parent_menu_shell must also contain
90  * a selected menu item. The current menu is the menu that 
91  * contains the current menu_item. It will always have a GTK
92  * grab and receive all key presses.
93  *
94  *
95  * Action signals:
96  *
97  *  ::move_current (GtkMenuDirection *dir)
98  *     Moves the current menu item in direction 'dir':
99  *
100  *       GTK_MENU_DIR_PARENT: To the parent menu shell
101  *       GTK_MENU_DIR_CHILD: To the child menu shell (if this item has
102  *          a submenu.
103  *       GTK_MENU_DIR_NEXT/PREV: To the next or previous item
104  *          in this menu.
105  * 
106  *     As a a bit of a hack to get movement between menus and
107  *     menubars working, if submenu_placement is different for
108  *     the menu and its MenuShell then the following apply:
109  * 
110  *       - For 'parent' the current menu is not just moved to
111  *         the parent, but moved to the previous entry in the parent
112  *       - For 'child', if there is no child, then current is
113  *         moved to the next item in the parent.
114  *
115  *    Note that the above explanation of ::move_current was written
116  *    before menus and menubars had support for RTL flipping and
117  *    different packing directions, and therefore only applies for
118  *    when text direction and packing direction are both left-to-right.
119  * 
120  *  ::activate_current (GBoolean *force_hide)
121  *     Activate the current item. If 'force_hide' is true, hide
122  *     the current menu item always. Otherwise, only hide
123  *     it if menu_item->klass->hide_on_activate is true.
124  *
125  *  ::cancel ()
126  *     Cancels the current selection
127  */
128
129 #define GTK_MENU_SHELL_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GTK_TYPE_MENU_SHELL, GtkMenuShellPrivate))
130
131 typedef struct _GtkMenuShellPrivate GtkMenuShellPrivate;
132
133 struct _GtkMenuShellPrivate
134 {
135   GtkMnemonicHash *mnemonic_hash;
136   GtkKeyHash *key_hash;
137
138   GdkDevice *grab_keyboard;
139   GdkDevice *grab_pointer;
140
141   guint take_focus : 1;
142   guint activated_submenu : 1;
143   /* This flag is a crutch to keep mnemonics in the same menu
144    * if the user moves the mouse over an unselectable menuitem.
145    */
146   guint in_unselectable_item : 1;
147 };
148
149 static void gtk_menu_shell_set_property      (GObject           *object,
150                                               guint              prop_id,
151                                               const GValue      *value,
152                                               GParamSpec        *pspec);
153 static void gtk_menu_shell_get_property      (GObject           *object,
154                                               guint              prop_id,
155                                               GValue            *value,
156                                               GParamSpec        *pspec);
157 static void gtk_menu_shell_realize           (GtkWidget         *widget);
158 static void gtk_menu_shell_finalize          (GObject           *object);
159 static gint gtk_menu_shell_button_press      (GtkWidget         *widget,
160                                               GdkEventButton    *event);
161 static gint gtk_menu_shell_button_release    (GtkWidget         *widget,
162                                               GdkEventButton    *event);
163 static gint gtk_menu_shell_key_press         (GtkWidget         *widget,
164                                               GdkEventKey       *event);
165 static gint gtk_menu_shell_enter_notify      (GtkWidget         *widget,
166                                               GdkEventCrossing  *event);
167 static gint gtk_menu_shell_leave_notify      (GtkWidget         *widget,
168                                               GdkEventCrossing  *event);
169 static void gtk_menu_shell_screen_changed    (GtkWidget         *widget,
170                                               GdkScreen         *previous_screen);
171 static gboolean gtk_menu_shell_grab_broken       (GtkWidget         *widget,
172                                               GdkEventGrabBroken *event);
173 static void gtk_menu_shell_add               (GtkContainer      *container,
174                                               GtkWidget         *widget);
175 static void gtk_menu_shell_remove            (GtkContainer      *container,
176                                               GtkWidget         *widget);
177 static void gtk_menu_shell_forall            (GtkContainer      *container,
178                                               gboolean           include_internals,
179                                               GtkCallback        callback,
180                                               gpointer           callback_data);
181 static void gtk_menu_shell_real_insert       (GtkMenuShell *menu_shell,
182                                               GtkWidget    *child,
183                                               gint          position);
184 static void gtk_real_menu_shell_deactivate   (GtkMenuShell      *menu_shell);
185 static gint gtk_menu_shell_is_item           (GtkMenuShell      *menu_shell,
186                                               GtkWidget         *child);
187 static GtkWidget *gtk_menu_shell_get_item    (GtkMenuShell      *menu_shell,
188                                               GdkEvent          *event);
189 static GType    gtk_menu_shell_child_type  (GtkContainer      *container);
190 static void gtk_menu_shell_real_select_item  (GtkMenuShell      *menu_shell,
191                                               GtkWidget         *menu_item);
192 static gboolean gtk_menu_shell_select_submenu_first (GtkMenuShell   *menu_shell); 
193
194 static void gtk_real_menu_shell_move_current (GtkMenuShell      *menu_shell,
195                                               GtkMenuDirectionType direction);
196 static void gtk_real_menu_shell_activate_current (GtkMenuShell      *menu_shell,
197                                                   gboolean           force_hide);
198 static void gtk_real_menu_shell_cancel           (GtkMenuShell      *menu_shell);
199 static void gtk_real_menu_shell_cycle_focus      (GtkMenuShell      *menu_shell,
200                                                   GtkDirectionType   dir);
201
202 static void     gtk_menu_shell_reset_key_hash    (GtkMenuShell *menu_shell);
203 static gboolean gtk_menu_shell_activate_mnemonic (GtkMenuShell *menu_shell,
204                                                   GdkEventKey  *event);
205 static gboolean gtk_menu_shell_real_move_selected (GtkMenuShell  *menu_shell, 
206                                                    gint           distance);
207
208 static guint menu_shell_signals[LAST_SIGNAL] = { 0 };
209
210 G_DEFINE_ABSTRACT_TYPE (GtkMenuShell, gtk_menu_shell, GTK_TYPE_CONTAINER)
211
212 static void
213 gtk_menu_shell_class_init (GtkMenuShellClass *klass)
214 {
215   GObjectClass *object_class;
216   GtkWidgetClass *widget_class;
217   GtkContainerClass *container_class;
218
219   GtkBindingSet *binding_set;
220
221   object_class = (GObjectClass*) klass;
222   widget_class = (GtkWidgetClass*) klass;
223   container_class = (GtkContainerClass*) klass;
224
225   object_class->set_property = gtk_menu_shell_set_property;
226   object_class->get_property = gtk_menu_shell_get_property;
227   object_class->finalize = gtk_menu_shell_finalize;
228
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;
237
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;
242
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;
252
253   menu_shell_signals[DEACTIVATE] =
254     g_signal_new (I_("deactivate"),
255                   G_OBJECT_CLASS_TYPE (object_class),
256                   G_SIGNAL_RUN_FIRST,
257                   G_STRUCT_OFFSET (GtkMenuShellClass, deactivate),
258                   NULL, NULL,
259                   _gtk_marshal_VOID__VOID,
260                   G_TYPE_NONE, 0);
261
262   menu_shell_signals[SELECTION_DONE] =
263     g_signal_new (I_("selection-done"),
264                   G_OBJECT_CLASS_TYPE (object_class),
265                   G_SIGNAL_RUN_FIRST,
266                   G_STRUCT_OFFSET (GtkMenuShellClass, selection_done),
267                   NULL, NULL,
268                   _gtk_marshal_VOID__VOID,
269                   G_TYPE_NONE, 0);
270
271   menu_shell_signals[MOVE_CURRENT] =
272     g_signal_new (I_("move-current"),
273                   G_OBJECT_CLASS_TYPE (object_class),
274                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
275                   G_STRUCT_OFFSET (GtkMenuShellClass, move_current),
276                   NULL, NULL,
277                   _gtk_marshal_VOID__ENUM,
278                   G_TYPE_NONE, 1,
279                   GTK_TYPE_MENU_DIRECTION_TYPE);
280
281   menu_shell_signals[ACTIVATE_CURRENT] =
282     g_signal_new (I_("activate-current"),
283                   G_OBJECT_CLASS_TYPE (object_class),
284                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
285                   G_STRUCT_OFFSET (GtkMenuShellClass, activate_current),
286                   NULL, NULL,
287                   _gtk_marshal_VOID__BOOLEAN,
288                   G_TYPE_NONE, 1,
289                   G_TYPE_BOOLEAN);
290
291   menu_shell_signals[CANCEL] =
292     g_signal_new (I_("cancel"),
293                   G_OBJECT_CLASS_TYPE (object_class),
294                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
295                   G_STRUCT_OFFSET (GtkMenuShellClass, cancel),
296                   NULL, NULL,
297                   _gtk_marshal_VOID__VOID,
298                   G_TYPE_NONE, 0);
299
300   menu_shell_signals[CYCLE_FOCUS] =
301     g_signal_new_class_handler (I_("cycle-focus"),
302                                 G_OBJECT_CLASS_TYPE (object_class),
303                                 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
304                                 G_CALLBACK (gtk_real_menu_shell_cycle_focus),
305                                 NULL, NULL,
306                                 _gtk_marshal_VOID__ENUM,
307                                 G_TYPE_NONE, 1,
308                                 GTK_TYPE_DIRECTION_TYPE);
309
310   /**
311    * GtkMenuShell::move-selected:
312    * @menu_shell: the object on which the signal is emitted
313    * @distance: +1 to move to the next item, -1 to move to the previous
314    *
315    * The ::move-selected signal is emitted to move the selection to
316    * another item.
317    *
318    * Returns: %TRUE to stop the signal emission, %FALSE to continue
319    *
320    * Since: 2.12
321    */
322   menu_shell_signals[MOVE_SELECTED] =
323     g_signal_new (I_("move-selected"),
324                   G_OBJECT_CLASS_TYPE (object_class),
325                   G_SIGNAL_RUN_LAST,
326                   G_STRUCT_OFFSET (GtkMenuShellClass, move_selected),
327                   _gtk_boolean_handled_accumulator, NULL,
328                   _gtk_marshal_BOOLEAN__INT,
329                   G_TYPE_BOOLEAN, 1,
330                   G_TYPE_INT);
331
332   binding_set = gtk_binding_set_by_class (klass);
333   gtk_binding_entry_add_signal (binding_set,
334                                 GDK_Escape, 0,
335                                 "cancel", 0);
336   gtk_binding_entry_add_signal (binding_set,
337                                 GDK_Return, 0,
338                                 "activate-current", 1,
339                                 G_TYPE_BOOLEAN,
340                                 TRUE);
341   gtk_binding_entry_add_signal (binding_set,
342                                 GDK_ISO_Enter, 0,
343                                 "activate-current", 1,
344                                 G_TYPE_BOOLEAN,
345                                 TRUE);
346   gtk_binding_entry_add_signal (binding_set,
347                                 GDK_KP_Enter, 0,
348                                 "activate-current", 1,
349                                 G_TYPE_BOOLEAN,
350                                 TRUE);
351   gtk_binding_entry_add_signal (binding_set,
352                                 GDK_space, 0,
353                                 "activate-current", 1,
354                                 G_TYPE_BOOLEAN,
355                                 FALSE);
356   gtk_binding_entry_add_signal (binding_set,
357                                 GDK_KP_Space, 0,
358                                 "activate-current", 1,
359                                 G_TYPE_BOOLEAN,
360                                 FALSE);
361   gtk_binding_entry_add_signal (binding_set,
362                                 GDK_F10, 0,
363                                 "cycle-focus", 1,
364                                 GTK_TYPE_DIRECTION_TYPE, GTK_DIR_TAB_FORWARD);
365   gtk_binding_entry_add_signal (binding_set,
366                                 GDK_F10, GDK_SHIFT_MASK,
367                                 "cycle-focus", 1,
368                                 GTK_TYPE_DIRECTION_TYPE, GTK_DIR_TAB_BACKWARD);
369
370   /**
371    * GtkMenuShell:take-focus:
372    *
373    * A boolean that determines whether the menu and its submenus grab the
374    * keyboard focus. See gtk_menu_shell_set_take_focus() and
375    * gtk_menu_shell_get_take_focus().
376    *
377    * Since: 2.8
378    **/
379   g_object_class_install_property (object_class,
380                                    PROP_TAKE_FOCUS,
381                                    g_param_spec_boolean ("take-focus",
382                                                          P_("Take Focus"),
383                                                          P_("A boolean that determines whether the menu grabs the keyboard focus"),
384                                                          TRUE,
385                                                          GTK_PARAM_READWRITE));
386
387   g_type_class_add_private (object_class, sizeof (GtkMenuShellPrivate));
388 }
389
390 static GType
391 gtk_menu_shell_child_type (GtkContainer     *container)
392 {
393   return GTK_TYPE_MENU_ITEM;
394 }
395
396 static void
397 gtk_menu_shell_init (GtkMenuShell *menu_shell)
398 {
399   GtkMenuShellPrivate *priv = GTK_MENU_SHELL_GET_PRIVATE (menu_shell);
400
401   menu_shell->children = NULL;
402   menu_shell->active_menu_item = NULL;
403   menu_shell->parent_menu_shell = NULL;
404   menu_shell->active = FALSE;
405   menu_shell->have_grab = FALSE;
406   menu_shell->have_xgrab = FALSE;
407   menu_shell->button = 0;
408   menu_shell->activate_time = 0;
409
410   priv->mnemonic_hash = NULL;
411   priv->key_hash = NULL;
412   priv->take_focus = TRUE;
413   priv->activated_submenu = FALSE;
414 }
415
416 static void
417 gtk_menu_shell_set_property (GObject      *object,
418                              guint         prop_id,
419                              const GValue *value,
420                              GParamSpec   *pspec)
421 {
422   GtkMenuShell *menu_shell = GTK_MENU_SHELL (object);
423
424   switch (prop_id)
425     {
426     case PROP_TAKE_FOCUS:
427       gtk_menu_shell_set_take_focus (menu_shell, g_value_get_boolean (value));
428       break;
429     default:
430       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
431       break;
432     }
433 }
434
435 static void
436 gtk_menu_shell_get_property (GObject     *object,
437                              guint        prop_id,
438                              GValue      *value,
439                              GParamSpec  *pspec)
440 {
441   GtkMenuShell *menu_shell = GTK_MENU_SHELL (object);
442
443   switch (prop_id)
444     {
445     case PROP_TAKE_FOCUS:
446       g_value_set_boolean (value, gtk_menu_shell_get_take_focus (menu_shell));
447       break;
448     default:
449       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
450       break;
451     }
452 }
453
454 static void
455 gtk_menu_shell_finalize (GObject *object)
456 {
457   GtkMenuShell *menu_shell = GTK_MENU_SHELL (object);
458   GtkMenuShellPrivate *priv = GTK_MENU_SHELL_GET_PRIVATE (menu_shell);
459
460   if (priv->mnemonic_hash)
461     _gtk_mnemonic_hash_free (priv->mnemonic_hash);
462   if (priv->key_hash)
463     _gtk_key_hash_free (priv->key_hash);
464
465   G_OBJECT_CLASS (gtk_menu_shell_parent_class)->finalize (object);
466 }
467
468
469 void
470 gtk_menu_shell_append (GtkMenuShell *menu_shell,
471                        GtkWidget    *child)
472 {
473   gtk_menu_shell_insert (menu_shell, child, -1);
474 }
475
476 void
477 gtk_menu_shell_prepend (GtkMenuShell *menu_shell,
478                         GtkWidget    *child)
479 {
480   gtk_menu_shell_insert (menu_shell, child, 0);
481 }
482
483 void
484 gtk_menu_shell_insert (GtkMenuShell *menu_shell,
485                        GtkWidget    *child,
486                        gint          position)
487 {
488   GtkMenuShellClass *class;
489
490   g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
491   g_return_if_fail (GTK_IS_MENU_ITEM (child));
492
493   class = GTK_MENU_SHELL_GET_CLASS (menu_shell);
494
495   if (class->insert)
496     class->insert (menu_shell, child, position);
497 }
498
499 static void
500 gtk_menu_shell_real_insert (GtkMenuShell *menu_shell,
501                             GtkWidget    *child,
502                             gint          position)
503 {
504   menu_shell->children = g_list_insert (menu_shell->children, child, position);
505
506   gtk_widget_set_parent (child, GTK_WIDGET (menu_shell));
507 }
508
509 void
510 gtk_menu_shell_deactivate (GtkMenuShell *menu_shell)
511 {
512   g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
513
514   g_signal_emit (menu_shell, menu_shell_signals[DEACTIVATE], 0);
515 }
516
517 static void
518 gtk_menu_shell_realize (GtkWidget *widget)
519 {
520   GdkWindowAttr attributes;
521   gint attributes_mask;
522
523   gtk_widget_set_realized (widget, TRUE);
524
525   attributes.x = widget->allocation.x;
526   attributes.y = widget->allocation.y;
527   attributes.width = widget->allocation.width;
528   attributes.height = widget->allocation.height;
529   attributes.window_type = GDK_WINDOW_CHILD;
530   attributes.wclass = GDK_INPUT_OUTPUT;
531   attributes.visual = gtk_widget_get_visual (widget);
532   attributes.colormap = gtk_widget_get_colormap (widget);
533   attributes.event_mask = gtk_widget_get_events (widget);
534   attributes.event_mask |= (GDK_EXPOSURE_MASK |
535                             GDK_BUTTON_PRESS_MASK |
536                             GDK_BUTTON_RELEASE_MASK |
537                             GDK_KEY_PRESS_MASK |
538                             GDK_ENTER_NOTIFY_MASK |
539                             GDK_LEAVE_NOTIFY_MASK);
540
541   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
542   widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask);
543   gdk_window_set_user_data (widget->window, widget);
544
545   widget->style = gtk_style_attach (widget->style, widget->window);
546   gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
547 }
548
549 void
550 _gtk_menu_shell_activate (GtkMenuShell *menu_shell)
551 {
552   if (!menu_shell->active)
553     {
554       gtk_device_grab_add (GTK_WIDGET (menu_shell),
555                            gtk_get_current_event_device (),
556                            TRUE);
557       menu_shell->have_grab = TRUE;
558       menu_shell->active = TRUE;
559     }
560 }
561
562 static gint
563 gtk_menu_shell_button_press (GtkWidget      *widget,
564                              GdkEventButton *event)
565 {
566   GtkMenuShell *menu_shell;
567   GtkWidget *menu_item;
568
569   if (event->type != GDK_BUTTON_PRESS)
570     return FALSE;
571
572   menu_shell = GTK_MENU_SHELL (widget);
573
574   if (menu_shell->parent_menu_shell)
575     return gtk_widget_event (menu_shell->parent_menu_shell, (GdkEvent*) event);
576
577   menu_item = gtk_menu_shell_get_item (menu_shell, (GdkEvent *)event);
578
579   if (menu_item && _gtk_menu_item_is_selectable (menu_item) &&
580       menu_item != GTK_MENU_SHELL (menu_item->parent)->active_menu_item)
581     {
582       /*  select the menu item *before* activating the shell, so submenus
583        *  which might be open are closed the friendly way. If we activate
584        *  (and thus grab) this menu shell first, we might get grab_broken
585        *  events which will close the entire menu hierarchy. Selecting the
586        *  menu item also fixes up the state as if enter_notify() would
587        *  have run before (which normally selects the item).
588        */
589       if (GTK_MENU_SHELL_GET_CLASS (menu_item->parent)->submenu_placement != GTK_TOP_BOTTOM)
590         {
591           gtk_menu_shell_select_item (GTK_MENU_SHELL (menu_item->parent), menu_item);
592         }
593     }
594
595   if (!menu_shell->active || !menu_shell->button)
596     {
597       _gtk_menu_shell_activate (menu_shell);
598
599       menu_shell->button = event->button;
600
601       if (menu_item && _gtk_menu_item_is_selectable (menu_item) &&
602           menu_item->parent == widget &&
603           menu_item != menu_shell->active_menu_item)
604         {
605           if (GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement == GTK_TOP_BOTTOM)
606             {
607               menu_shell->activate_time = event->time;
608               gtk_menu_shell_select_item (menu_shell, menu_item);
609             }
610         }
611     }
612   else
613     {
614       widget = gtk_get_event_widget ((GdkEvent*) event);
615       if (widget == GTK_WIDGET (menu_shell))
616         {
617           gtk_menu_shell_deactivate (menu_shell);
618           g_signal_emit (menu_shell, menu_shell_signals[SELECTION_DONE], 0);
619         }
620     }
621
622   if (menu_item && _gtk_menu_item_is_selectable (menu_item) &&
623       GTK_MENU_ITEM (menu_item)->submenu != NULL &&
624       !gtk_widget_get_visible (GTK_MENU_ITEM (menu_item)->submenu))
625     {
626       GtkMenuShellPrivate *priv;
627
628       _gtk_menu_item_popup_submenu (menu_item, FALSE);
629
630       priv = GTK_MENU_SHELL_GET_PRIVATE (menu_item->parent);
631       priv->activated_submenu = TRUE;
632     }
633
634   return TRUE;
635 }
636
637 static gboolean
638 gtk_menu_shell_grab_broken (GtkWidget          *widget,
639                             GdkEventGrabBroken *event)
640 {
641   GtkMenuShell *menu_shell = GTK_MENU_SHELL (widget);
642
643   if (menu_shell->have_xgrab && event->grab_window == NULL)
644     {
645       /* Unset the active menu item so gtk_menu_popdown() doesn't see it.
646        */
647       
648       gtk_menu_shell_deselect (menu_shell);
649       
650       gtk_menu_shell_deactivate (menu_shell);
651       g_signal_emit (menu_shell, menu_shell_signals[SELECTION_DONE], 0);
652     }
653
654   return TRUE;
655 }
656
657 static gint
658 gtk_menu_shell_button_release (GtkWidget      *widget,
659                                GdkEventButton *event)
660 {
661   GtkMenuShell *menu_shell = GTK_MENU_SHELL (widget);
662   GtkMenuShellPrivate *priv = GTK_MENU_SHELL_GET_PRIVATE (widget);
663
664   if (menu_shell->active)
665     {
666       GtkWidget *menu_item;
667       gboolean   deactivate = TRUE;
668
669       if (menu_shell->button && (event->button != menu_shell->button))
670         {
671           menu_shell->button = 0;
672           if (menu_shell->parent_menu_shell)
673             return gtk_widget_event (menu_shell->parent_menu_shell, (GdkEvent*) event);
674         }
675
676       menu_shell->button = 0;
677       menu_item = gtk_menu_shell_get_item (menu_shell, (GdkEvent*) event);
678
679       if ((event->time - menu_shell->activate_time) > MENU_SHELL_TIMEOUT)
680         {
681           if (menu_item && (menu_shell->active_menu_item == menu_item) &&
682               _gtk_menu_item_is_selectable (menu_item))
683             {
684               GtkWidget *submenu = GTK_MENU_ITEM (menu_item)->submenu;
685
686               if (submenu == NULL)
687                 {
688                   gtk_menu_shell_activate_item (menu_shell, menu_item, TRUE);
689
690                   deactivate = FALSE;
691                 }
692               else if (GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement != GTK_TOP_BOTTOM ||
693                        priv->activated_submenu)
694                 {
695                   gint popdown_delay;
696                   GTimeVal *popup_time;
697                   gint64 usec_since_popup = 0;
698
699                   g_object_get (gtk_widget_get_settings (widget),
700                                 "gtk-menu-popdown-delay", &popdown_delay,
701                                 NULL);
702
703                   popup_time = g_object_get_data (G_OBJECT (submenu),
704                                                   "gtk-menu-exact-popup-time");
705
706                   if (popup_time)
707                     {
708                       GTimeVal current_time;
709
710                       g_get_current_time (&current_time);
711
712                       usec_since_popup = ((gint64) current_time.tv_sec * 1000 * 1000 +
713                                           (gint64) current_time.tv_usec -
714                                           (gint64) popup_time->tv_sec * 1000 * 1000 -
715                                           (gint64) popup_time->tv_usec);
716
717                       g_object_set_data (G_OBJECT (submenu),
718                                          "gtk-menu-exact-popup-time", NULL);
719                     }
720
721                   /*  only close the submenu on click if we opened the
722                    *  menu explicitely (usec_since_popup == 0) or
723                    *  enough time has passed since it was opened by
724                    *  GtkMenuItem's timeout (usec_since_popup > delay).
725                    */
726                   if (!priv->activated_submenu &&
727                       (usec_since_popup == 0 ||
728                        usec_since_popup > popdown_delay * 1000))
729                     {
730                       _gtk_menu_item_popdown_submenu (menu_item);
731                     }
732                   else
733                     {
734                       gtk_menu_item_select (GTK_MENU_ITEM (menu_item));
735                     }
736
737                   deactivate = FALSE;
738                 }
739             }
740           else if (menu_item &&
741                    !_gtk_menu_item_is_selectable (menu_item) &&
742                    GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement != GTK_TOP_BOTTOM)
743             {
744               deactivate = FALSE;
745             }
746           else if (menu_shell->parent_menu_shell)
747             {
748               menu_shell->active = TRUE;
749               gtk_widget_event (menu_shell->parent_menu_shell, (GdkEvent*) event);
750               deactivate = FALSE;
751             }
752
753           /* If we ended up on an item with a submenu, leave the menu up.
754            */
755           if (menu_item && (menu_shell->active_menu_item == menu_item) &&
756               GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement != GTK_TOP_BOTTOM)
757             {
758               deactivate = FALSE;
759             }
760         }
761       else /* a very fast press-release */
762         {
763           /* We only ever want to prevent deactivation on the first
764            * press/release. Setting the time to zero is a bit of a
765            * hack, since we could be being triggered in the first
766            * few fractions of a second after a server time wraparound.
767            * the chances of that happening are ~1/10^6, without
768            * serious harm if we lose.
769            */
770           menu_shell->activate_time = 0;
771           deactivate = FALSE;
772         }
773
774       if (deactivate)
775         {
776           gtk_menu_shell_deactivate (menu_shell);
777           g_signal_emit (menu_shell, menu_shell_signals[SELECTION_DONE], 0);
778         }
779
780       priv->activated_submenu = FALSE;
781     }
782
783   return TRUE;
784 }
785
786 void
787 _gtk_menu_shell_set_keyboard_mode (GtkMenuShell *menu_shell,
788                                    gboolean      keyboard_mode)
789 {
790   menu_shell->keyboard_mode = keyboard_mode;
791 }
792
793 gboolean
794 _gtk_menu_shell_get_keyboard_mode (GtkMenuShell *menu_shell)
795 {
796   return menu_shell->keyboard_mode;
797 }
798
799 void
800 _gtk_menu_shell_update_mnemonics (GtkMenuShell *menu_shell)
801 {
802   GtkMenuShell *target;
803   gboolean auto_mnemonics;
804   gboolean found;
805   gboolean mnemonics_visible;
806
807   g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu_shell)),
808                 "gtk-auto-mnemonics", &auto_mnemonics, NULL);
809
810   if (!auto_mnemonics)
811     return;
812
813   target = menu_shell;
814   found = FALSE;
815   while (target)
816     {
817       GtkMenuShellPrivate *priv = GTK_MENU_SHELL_GET_PRIVATE (target);
818       GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (target));
819
820       /* The idea with keyboard mode is that once you start using
821        * the keyboard to navigate the menus, we show mnemonics
822        * until the menu navigation is over. To that end, we spread
823        * the keyboard mode upwards in the menu hierarchy here.
824        * Also see gtk_menu_popup, where we inherit it downwards.
825        */
826       if (menu_shell->keyboard_mode)
827         target->keyboard_mode = TRUE;
828
829       /* While navigating menus, the first parent menu with an active
830        * item is the one where mnemonics are effective, as can be seen
831        * in gtk_menu_shell_key_press below.
832        * We also show mnemonics in context menus. The grab condition is
833        * necessary to ensure we remove underlines from menu bars when
834        * dismissing menus.
835        */
836       mnemonics_visible = target->keyboard_mode &&
837                           (((target->active_menu_item || priv->in_unselectable_item) && !found) ||
838                            (target == menu_shell &&
839                             !target->parent_menu_shell &&
840                             gtk_widget_has_grab (GTK_WIDGET (target))));
841
842       /* While menus are up, only show underlines inside the menubar,
843        * not in the entire window.
844        */
845       if (GTK_IS_MENU_BAR (target))
846         {
847           gtk_window_set_mnemonics_visible (GTK_WINDOW (toplevel), FALSE);
848           _gtk_label_mnemonics_visible_apply_recursively (GTK_WIDGET (target),
849                                                           mnemonics_visible);
850         }
851       else
852         gtk_window_set_mnemonics_visible (GTK_WINDOW (toplevel), mnemonics_visible);
853
854       if (target->active_menu_item || priv->in_unselectable_item)
855         found = TRUE;
856
857       target = GTK_MENU_SHELL (target->parent_menu_shell);
858     }
859 }
860
861 static gint
862 gtk_menu_shell_key_press (GtkWidget   *widget,
863                           GdkEventKey *event)
864 {
865   GtkMenuShell *menu_shell = GTK_MENU_SHELL (widget);
866   GtkMenuShellPrivate *priv = GTK_MENU_SHELL_GET_PRIVATE (menu_shell);
867   gboolean enable_mnemonics;
868
869   menu_shell->keyboard_mode = TRUE;
870
871   if (!(menu_shell->active_menu_item || priv->in_unselectable_item) && menu_shell->parent_menu_shell)
872     return gtk_widget_event (menu_shell->parent_menu_shell, (GdkEvent *)event);
873
874   if (gtk_bindings_activate_event (GTK_OBJECT (widget), event))
875     return TRUE;
876
877   g_object_get (gtk_widget_get_settings (widget),
878                 "gtk-enable-mnemonics", &enable_mnemonics,
879                 NULL);
880
881   if (enable_mnemonics)
882     return gtk_menu_shell_activate_mnemonic (menu_shell, event);
883
884   return FALSE;
885 }
886
887 static gint
888 gtk_menu_shell_enter_notify (GtkWidget        *widget,
889                              GdkEventCrossing *event)
890 {
891   GtkMenuShell *menu_shell = GTK_MENU_SHELL (widget);
892
893   if (event->mode == GDK_CROSSING_GTK_GRAB ||
894       event->mode == GDK_CROSSING_GTK_UNGRAB ||
895       event->mode == GDK_CROSSING_STATE_CHANGED)
896     return TRUE;
897
898   if (menu_shell->active)
899     {
900       GtkWidget *menu_item;
901
902       menu_item = gtk_get_event_widget ((GdkEvent*) event);
903
904       if (!menu_item)
905         return TRUE;
906
907       if (GTK_IS_MENU_ITEM (menu_item) &&
908           !_gtk_menu_item_is_selectable (menu_item))
909         {
910           GtkMenuShellPrivate *priv;
911
912           priv = GTK_MENU_SHELL_GET_PRIVATE (menu_shell);
913           priv->in_unselectable_item = TRUE;
914
915           return TRUE;
916         }
917
918       if (menu_item->parent == widget &&
919           GTK_IS_MENU_ITEM (menu_item))
920         {
921           if (menu_shell->ignore_enter)
922             return TRUE;
923
924           if (event->detail != GDK_NOTIFY_INFERIOR)
925             {
926               if (gtk_widget_get_state (menu_item) != GTK_STATE_PRELIGHT)
927                 gtk_menu_shell_select_item (menu_shell, menu_item);
928
929               /* If any mouse button is down, and there is a submenu
930                * that is not yet visible, activate it. It's sufficient
931                * to check for any button's mask (not only the one
932                * matching menu_shell->button), because there is no
933                * situation a mouse button could be pressed while
934                * entering a menu item where we wouldn't want to show
935                * its submenu.
936                */
937               if ((event->state & (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK | GDK_BUTTON3_MASK)) &&
938                   GTK_MENU_ITEM (menu_item)->submenu != NULL)
939                 {
940                   GtkMenuShellPrivate *priv;
941
942                   priv = GTK_MENU_SHELL_GET_PRIVATE (menu_item->parent);
943                   priv->activated_submenu = TRUE;
944
945                   if (!gtk_widget_get_visible (GTK_MENU_ITEM (menu_item)->submenu))
946                     {
947                       gboolean touchscreen_mode;
948
949                       g_object_get (gtk_widget_get_settings (widget),
950                                     "gtk-touchscreen-mode", &touchscreen_mode,
951                                     NULL);
952
953                       if (touchscreen_mode)
954                         _gtk_menu_item_popup_submenu (menu_item, TRUE);
955                     }
956                 }
957             }
958         }
959       else if (menu_shell->parent_menu_shell)
960         {
961           gtk_widget_event (menu_shell->parent_menu_shell, (GdkEvent*) event);
962         }
963     }
964
965   return TRUE;
966 }
967
968 static gint
969 gtk_menu_shell_leave_notify (GtkWidget        *widget,
970                              GdkEventCrossing *event)
971 {
972   if (event->mode == GDK_CROSSING_GTK_GRAB ||
973       event->mode == GDK_CROSSING_GTK_GRAB ||
974       event->mode == GDK_CROSSING_STATE_CHANGED)
975     return TRUE;
976
977   if (gtk_widget_get_visible (widget))
978     {
979       GtkMenuShell *menu_shell = GTK_MENU_SHELL (widget);
980       GtkWidget *event_widget = gtk_get_event_widget ((GdkEvent*) event);
981       GtkMenuItem *menu_item;
982
983       if (!event_widget || !GTK_IS_MENU_ITEM (event_widget))
984         return TRUE;
985
986       menu_item = GTK_MENU_ITEM (event_widget);
987
988       if (!_gtk_menu_item_is_selectable (event_widget))
989         {
990           GtkMenuShellPrivate *priv;
991
992           priv = GTK_MENU_SHELL_GET_PRIVATE (menu_shell);
993           priv->in_unselectable_item = TRUE;
994
995           return TRUE;
996         }
997
998       if ((menu_shell->active_menu_item == event_widget) &&
999           (menu_item->submenu == NULL))
1000         {
1001           if ((event->detail != GDK_NOTIFY_INFERIOR) &&
1002               (gtk_widget_get_state (GTK_WIDGET (menu_item)) != GTK_STATE_NORMAL))
1003             {
1004               gtk_menu_shell_deselect (menu_shell);
1005             }
1006         }
1007       else if (menu_shell->parent_menu_shell)
1008         {
1009           gtk_widget_event (menu_shell->parent_menu_shell, (GdkEvent*) event);
1010         }
1011     }
1012
1013   return TRUE;
1014 }
1015
1016 static void
1017 gtk_menu_shell_screen_changed (GtkWidget *widget,
1018                                GdkScreen *previous_screen)
1019 {
1020   gtk_menu_shell_reset_key_hash (GTK_MENU_SHELL (widget));
1021 }
1022
1023 static void
1024 gtk_menu_shell_add (GtkContainer *container,
1025                     GtkWidget    *widget)
1026 {
1027   gtk_menu_shell_append (GTK_MENU_SHELL (container), widget);
1028 }
1029
1030 static void
1031 gtk_menu_shell_remove (GtkContainer *container,
1032                        GtkWidget    *widget)
1033 {
1034   GtkMenuShell *menu_shell = GTK_MENU_SHELL (container);
1035   gint was_visible;
1036
1037   was_visible = gtk_widget_get_visible (widget);
1038   menu_shell->children = g_list_remove (menu_shell->children, widget);
1039   
1040   if (widget == menu_shell->active_menu_item)
1041     {
1042       gtk_item_deselect (GTK_ITEM (menu_shell->active_menu_item));
1043       menu_shell->active_menu_item = NULL;
1044     }
1045
1046   gtk_widget_unparent (widget);
1047   
1048   /* queue resize regardless of gtk_widget_get_visible (container),
1049    * since that's what is needed by toplevels.
1050    */
1051   if (was_visible)
1052     gtk_widget_queue_resize (GTK_WIDGET (container));
1053 }
1054
1055 static void
1056 gtk_menu_shell_forall (GtkContainer *container,
1057                        gboolean      include_internals,
1058                        GtkCallback   callback,
1059                        gpointer      callback_data)
1060 {
1061   GtkMenuShell *menu_shell = GTK_MENU_SHELL (container);
1062   GtkWidget *child;
1063   GList *children;
1064
1065   children = menu_shell->children;
1066   while (children)
1067     {
1068       child = children->data;
1069       children = children->next;
1070
1071       (* callback) (child, callback_data);
1072     }
1073 }
1074
1075
1076 static void
1077 gtk_real_menu_shell_deactivate (GtkMenuShell *menu_shell)
1078 {
1079   if (menu_shell->active)
1080     {
1081       GtkMenuShellPrivate *priv = GTK_MENU_SHELL_GET_PRIVATE (menu_shell);
1082
1083       menu_shell->button = 0;
1084       menu_shell->active = FALSE;
1085       menu_shell->activate_time = 0;
1086
1087       if (menu_shell->active_menu_item)
1088         {
1089           gtk_menu_item_deselect (GTK_MENU_ITEM (menu_shell->active_menu_item));
1090           menu_shell->active_menu_item = NULL;
1091         }
1092
1093       if (menu_shell->have_grab)
1094         {
1095           menu_shell->have_grab = FALSE;
1096           gtk_device_grab_remove (GTK_WIDGET (menu_shell), priv->grab_pointer);
1097         }
1098       if (menu_shell->have_xgrab)
1099         {
1100           gdk_device_ungrab (priv->grab_pointer, GDK_CURRENT_TIME);
1101           gdk_device_ungrab (priv->grab_keyboard, GDK_CURRENT_TIME);
1102
1103           menu_shell->have_xgrab = FALSE;
1104           priv->grab_pointer = NULL;
1105           priv->grab_keyboard = NULL;
1106         }
1107
1108       menu_shell->keyboard_mode = FALSE;
1109
1110       _gtk_menu_shell_update_mnemonics (menu_shell);
1111     }
1112 }
1113
1114 static gint
1115 gtk_menu_shell_is_item (GtkMenuShell *menu_shell,
1116                         GtkWidget    *child)
1117 {
1118   GtkWidget *parent;
1119
1120   g_return_val_if_fail (GTK_IS_MENU_SHELL (menu_shell), FALSE);
1121   g_return_val_if_fail (child != NULL, FALSE);
1122
1123   parent = child->parent;
1124   while (GTK_IS_MENU_SHELL (parent))
1125     {
1126       if (parent == (GtkWidget*) menu_shell)
1127         return TRUE;
1128       parent = GTK_MENU_SHELL (parent)->parent_menu_shell;
1129     }
1130
1131   return FALSE;
1132 }
1133
1134 static GtkWidget*
1135 gtk_menu_shell_get_item (GtkMenuShell *menu_shell,
1136                          GdkEvent     *event)
1137 {
1138   GtkWidget *menu_item;
1139
1140   menu_item = gtk_get_event_widget ((GdkEvent*) event);
1141   
1142   while (menu_item && !GTK_IS_MENU_ITEM (menu_item))
1143     menu_item = menu_item->parent;
1144
1145   if (menu_item && gtk_menu_shell_is_item (menu_shell, menu_item))
1146     return menu_item;
1147   else
1148     return NULL;
1149 }
1150
1151 /* Handlers for action signals */
1152
1153 void
1154 gtk_menu_shell_select_item (GtkMenuShell *menu_shell,
1155                             GtkWidget    *menu_item)
1156 {
1157   GtkMenuShellClass *class;
1158
1159   g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
1160   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
1161
1162   class = GTK_MENU_SHELL_GET_CLASS (menu_shell);
1163
1164   if (class->select_item &&
1165       !(menu_shell->active &&
1166         menu_shell->active_menu_item == menu_item))
1167     class->select_item (menu_shell, menu_item);
1168 }
1169
1170 void _gtk_menu_item_set_placement (GtkMenuItem         *menu_item,
1171                                    GtkSubmenuPlacement  placement);
1172
1173 static void
1174 gtk_menu_shell_real_select_item (GtkMenuShell *menu_shell,
1175                                  GtkWidget    *menu_item)
1176 {
1177   GtkPackDirection pack_dir = PACK_DIRECTION (menu_shell);
1178
1179   if (menu_shell->active_menu_item)
1180     {
1181       gtk_menu_item_deselect (GTK_MENU_ITEM (menu_shell->active_menu_item));
1182       menu_shell->active_menu_item = NULL;
1183     }
1184
1185   if (!_gtk_menu_item_is_selectable (menu_item))
1186     {
1187       GtkMenuShellPrivate *priv = GTK_MENU_SHELL_GET_PRIVATE (menu_shell);
1188
1189       priv->in_unselectable_item = TRUE;
1190       _gtk_menu_shell_update_mnemonics (menu_shell);
1191
1192       return;
1193     }
1194
1195   menu_shell->active_menu_item = menu_item;
1196   if (pack_dir == GTK_PACK_DIRECTION_TTB || pack_dir == GTK_PACK_DIRECTION_BTT)
1197     _gtk_menu_item_set_placement (GTK_MENU_ITEM (menu_shell->active_menu_item),
1198                                   GTK_LEFT_RIGHT);
1199   else
1200     _gtk_menu_item_set_placement (GTK_MENU_ITEM (menu_shell->active_menu_item),
1201                                   GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement);
1202   gtk_menu_item_select (GTK_MENU_ITEM (menu_shell->active_menu_item));
1203
1204   _gtk_menu_shell_update_mnemonics (menu_shell);
1205
1206   /* This allows the bizarre radio buttons-with-submenus-display-history
1207    * behavior
1208    */
1209   if (GTK_MENU_ITEM (menu_shell->active_menu_item)->submenu)
1210     gtk_widget_activate (menu_shell->active_menu_item);
1211 }
1212
1213 void
1214 gtk_menu_shell_deselect (GtkMenuShell *menu_shell)
1215 {
1216   g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
1217
1218   if (menu_shell->active_menu_item)
1219     {
1220       gtk_menu_item_deselect (GTK_MENU_ITEM (menu_shell->active_menu_item));
1221       menu_shell->active_menu_item = NULL;
1222       _gtk_menu_shell_update_mnemonics (menu_shell);
1223     }
1224 }
1225
1226 void
1227 gtk_menu_shell_activate_item (GtkMenuShell      *menu_shell,
1228                               GtkWidget         *menu_item,
1229                               gboolean           force_deactivate)
1230 {
1231   GSList *slist, *shells = NULL;
1232   gboolean deactivate = force_deactivate;
1233
1234   g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
1235   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
1236
1237   if (!deactivate)
1238     deactivate = GTK_MENU_ITEM_GET_CLASS (menu_item)->hide_on_activate;
1239
1240   g_object_ref (menu_shell);
1241   g_object_ref (menu_item);
1242
1243   if (deactivate)
1244     {
1245       GtkMenuShell *parent_menu_shell = menu_shell;
1246
1247       do
1248         {
1249           g_object_ref (parent_menu_shell);
1250           shells = g_slist_prepend (shells, parent_menu_shell);
1251           parent_menu_shell = (GtkMenuShell*) parent_menu_shell->parent_menu_shell;
1252         }
1253       while (parent_menu_shell);
1254       shells = g_slist_reverse (shells);
1255
1256       gtk_menu_shell_deactivate (menu_shell);
1257   
1258       /* flush the x-queue, so any grabs are removed and
1259        * the menu is actually taken down
1260        */
1261       gdk_display_sync (gtk_widget_get_display (menu_item));
1262     }
1263
1264   gtk_widget_activate (menu_item);
1265
1266   for (slist = shells; slist; slist = slist->next)
1267     {
1268       g_signal_emit (slist->data, menu_shell_signals[SELECTION_DONE], 0);
1269       g_object_unref (slist->data);
1270     }
1271   g_slist_free (shells);
1272
1273   g_object_unref (menu_shell);
1274   g_object_unref (menu_item);
1275 }
1276
1277 /* Distance should be +/- 1 */
1278 static gboolean
1279 gtk_menu_shell_real_move_selected (GtkMenuShell  *menu_shell, 
1280                                    gint           distance)
1281 {
1282   if (menu_shell->active_menu_item)
1283     {
1284       GList *node = g_list_find (menu_shell->children,
1285                                  menu_shell->active_menu_item);
1286       GList *start_node = node;
1287       gboolean wrap_around;
1288
1289       g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu_shell)),
1290                     "gtk-keynav-wrap-around", &wrap_around,
1291                     NULL);
1292
1293       if (distance > 0)
1294         {
1295           node = node->next;
1296           while (node != start_node && 
1297                  (!node || !_gtk_menu_item_is_selectable (node->data)))
1298             {
1299               if (node)
1300                 node = node->next;
1301               else if (wrap_around)
1302                 node = menu_shell->children;
1303               else
1304                 {
1305                   gtk_widget_error_bell (GTK_WIDGET (menu_shell));
1306                   break;
1307                 }
1308             }
1309         }
1310       else
1311         {
1312           node = node->prev;
1313           while (node != start_node &&
1314                  (!node || !_gtk_menu_item_is_selectable (node->data)))
1315             {
1316               if (node)
1317                 node = node->prev;
1318               else if (wrap_around)
1319                 node = g_list_last (menu_shell->children);
1320               else
1321                 {
1322                   gtk_widget_error_bell (GTK_WIDGET (menu_shell));
1323                   break;
1324                 }
1325             }
1326         }
1327       
1328       if (node)
1329         gtk_menu_shell_select_item (menu_shell, node->data);
1330     }
1331
1332   return TRUE;
1333 }
1334
1335 /* Distance should be +/- 1 */
1336 static void
1337 gtk_menu_shell_move_selected (GtkMenuShell  *menu_shell, 
1338                               gint           distance)
1339 {
1340   gboolean handled = FALSE;
1341
1342   g_signal_emit (menu_shell, menu_shell_signals[MOVE_SELECTED], 0,
1343                  distance, &handled);
1344 }
1345
1346 /**
1347  * gtk_menu_shell_select_first:
1348  * @menu_shell: a #GtkMenuShell
1349  * @search_sensitive: if %TRUE, search for the first selectable
1350  *                    menu item, otherwise select nothing if
1351  *                    the first item isn't sensitive. This
1352  *                    should be %FALSE if the menu is being
1353  *                    popped up initially.
1354  * 
1355  * Select the first visible or selectable child of the menu shell;
1356  * don't select tearoff items unless the only item is a tearoff
1357  * item.
1358  *
1359  * Since: 2.2
1360  **/
1361 void
1362 gtk_menu_shell_select_first (GtkMenuShell *menu_shell,
1363                              gboolean      search_sensitive)
1364 {
1365   GtkWidget *to_select = NULL;
1366   GList *tmp_list;
1367
1368   tmp_list = menu_shell->children;
1369   while (tmp_list)
1370     {
1371       GtkWidget *child = tmp_list->data;
1372       
1373       if ((!search_sensitive && gtk_widget_get_visible (child)) ||
1374           _gtk_menu_item_is_selectable (child))
1375         {
1376           to_select = child;
1377           if (!GTK_IS_TEAROFF_MENU_ITEM (child))
1378             break;
1379         }
1380       
1381       tmp_list = tmp_list->next;
1382     }
1383
1384   if (to_select)
1385     gtk_menu_shell_select_item (menu_shell, to_select);
1386 }
1387
1388 void
1389 _gtk_menu_shell_select_last (GtkMenuShell *menu_shell,
1390                              gboolean      search_sensitive)
1391 {
1392   GtkWidget *to_select = NULL;
1393   GList *tmp_list;
1394
1395   tmp_list = g_list_last (menu_shell->children);
1396   while (tmp_list)
1397     {
1398       GtkWidget *child = tmp_list->data;
1399       
1400       if ((!search_sensitive && gtk_widget_get_visible (child)) ||
1401           _gtk_menu_item_is_selectable (child))
1402         {
1403           to_select = child;
1404           if (!GTK_IS_TEAROFF_MENU_ITEM (child))
1405             break;
1406         }
1407       
1408       tmp_list = tmp_list->prev;
1409     }
1410
1411   if (to_select)
1412     gtk_menu_shell_select_item (menu_shell, to_select);
1413 }
1414
1415 static gboolean
1416 gtk_menu_shell_select_submenu_first (GtkMenuShell     *menu_shell)
1417 {
1418   GtkMenuItem *menu_item;
1419
1420   if (menu_shell->active_menu_item == NULL)
1421     return FALSE;
1422
1423   menu_item = GTK_MENU_ITEM (menu_shell->active_menu_item); 
1424   
1425   if (menu_item->submenu)
1426     {
1427       _gtk_menu_item_popup_submenu (GTK_WIDGET (menu_item), FALSE);
1428       gtk_menu_shell_select_first (GTK_MENU_SHELL (menu_item->submenu), TRUE);
1429       if (GTK_MENU_SHELL (menu_item->submenu)->active_menu_item)
1430         return TRUE;
1431     }
1432
1433   return FALSE;
1434 }
1435
1436 static void
1437 gtk_real_menu_shell_move_current (GtkMenuShell         *menu_shell,
1438                                   GtkMenuDirectionType  direction)
1439 {
1440   GtkMenuShellPrivate *priv = GTK_MENU_SHELL_GET_PRIVATE (menu_shell);
1441   GtkMenuShell *parent_menu_shell = NULL;
1442   gboolean had_selection;
1443   gboolean touchscreen_mode;
1444
1445   priv->in_unselectable_item = FALSE;
1446
1447   had_selection = menu_shell->active_menu_item != NULL;
1448
1449   g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu_shell)),
1450                 "gtk-touchscreen-mode", &touchscreen_mode,
1451                 NULL);
1452
1453   if (menu_shell->parent_menu_shell)
1454     parent_menu_shell = GTK_MENU_SHELL (menu_shell->parent_menu_shell);
1455
1456   switch (direction)
1457     {
1458     case GTK_MENU_DIR_PARENT:
1459       if (touchscreen_mode &&
1460           menu_shell->active_menu_item &&
1461           GTK_MENU_ITEM (menu_shell->active_menu_item)->submenu &&
1462           gtk_widget_get_visible (GTK_MENU_ITEM (menu_shell->active_menu_item)->submenu))
1463         {
1464           /* if we are on a menu item that has an open submenu but the
1465            * focus is not in that submenu (e.g. because it's empty or
1466            * has only insensitive items), close that submenu instead
1467            * of running into the code below which would close *this*
1468            * menu.
1469            */
1470           _gtk_menu_item_popdown_submenu (menu_shell->active_menu_item);
1471           _gtk_menu_shell_update_mnemonics (menu_shell);
1472         }
1473       else if (parent_menu_shell)
1474         {
1475           if (touchscreen_mode)
1476             {
1477               /* close menu when returning from submenu. */
1478               _gtk_menu_item_popdown_submenu (GTK_MENU (menu_shell)->parent_menu_item);
1479               _gtk_menu_shell_update_mnemonics (parent_menu_shell);
1480               break;
1481             }
1482
1483           if (GTK_MENU_SHELL_GET_CLASS (parent_menu_shell)->submenu_placement ==
1484               GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement)
1485             gtk_menu_shell_deselect (menu_shell);
1486           else
1487             {
1488               if (PACK_DIRECTION (parent_menu_shell) == GTK_PACK_DIRECTION_LTR)
1489                 gtk_menu_shell_move_selected (parent_menu_shell, -1);
1490               else
1491                 gtk_menu_shell_move_selected (parent_menu_shell, 1);
1492               gtk_menu_shell_select_submenu_first (parent_menu_shell);
1493             }
1494         }
1495       /* If there is no parent and the submenu is in the opposite direction
1496        * to the menu, then make the PARENT direction wrap around to
1497        * the bottom of the submenu.
1498        */
1499       else if (menu_shell->active_menu_item &&
1500                _gtk_menu_item_is_selectable (menu_shell->active_menu_item) &&
1501                GTK_MENU_ITEM (menu_shell->active_menu_item)->submenu)
1502         {
1503           GtkMenuShell *submenu = GTK_MENU_SHELL (GTK_MENU_ITEM (menu_shell->active_menu_item)->submenu);
1504
1505           if (GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement !=
1506               GTK_MENU_SHELL_GET_CLASS (submenu)->submenu_placement)
1507             _gtk_menu_shell_select_last (submenu, TRUE);
1508         }
1509       break;
1510
1511     case GTK_MENU_DIR_CHILD:
1512       if (menu_shell->active_menu_item &&
1513           _gtk_menu_item_is_selectable (menu_shell->active_menu_item) &&
1514           GTK_MENU_ITEM (menu_shell->active_menu_item)->submenu)
1515         {
1516           if (gtk_menu_shell_select_submenu_first (menu_shell))
1517             break;
1518         }
1519
1520       /* Try to find a menu running the opposite direction */
1521       while (parent_menu_shell &&
1522              (GTK_MENU_SHELL_GET_CLASS (parent_menu_shell)->submenu_placement ==
1523               GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement))
1524         {
1525           parent_menu_shell = GTK_MENU_SHELL (parent_menu_shell->parent_menu_shell);
1526         }
1527
1528       if (parent_menu_shell)
1529         {
1530           if (PACK_DIRECTION (parent_menu_shell) == GTK_PACK_DIRECTION_LTR)
1531             gtk_menu_shell_move_selected (parent_menu_shell, 1);
1532           else
1533             gtk_menu_shell_move_selected (parent_menu_shell, -1);
1534
1535           gtk_menu_shell_select_submenu_first (parent_menu_shell);
1536         }
1537       break;
1538
1539     case GTK_MENU_DIR_PREV:
1540       gtk_menu_shell_move_selected (menu_shell, -1);
1541       if (!had_selection &&
1542           !menu_shell->active_menu_item &&
1543           menu_shell->children)
1544         _gtk_menu_shell_select_last (menu_shell, TRUE);
1545       break;
1546
1547     case GTK_MENU_DIR_NEXT:
1548       gtk_menu_shell_move_selected (menu_shell, 1);
1549       if (!had_selection &&
1550           !menu_shell->active_menu_item &&
1551           menu_shell->children)
1552         gtk_menu_shell_select_first (menu_shell, TRUE);
1553       break;
1554     }
1555 }
1556
1557 static void
1558 gtk_real_menu_shell_activate_current (GtkMenuShell      *menu_shell,
1559                                       gboolean           force_hide)
1560 {
1561   if (menu_shell->active_menu_item &&
1562       _gtk_menu_item_is_selectable (menu_shell->active_menu_item))
1563   {
1564     if (GTK_MENU_ITEM (menu_shell->active_menu_item)->submenu == NULL)
1565       gtk_menu_shell_activate_item (menu_shell,
1566                                     menu_shell->active_menu_item,
1567                                     force_hide);
1568     else
1569       _gtk_menu_item_popup_submenu (menu_shell->active_menu_item, FALSE);
1570   }
1571 }
1572
1573 static void
1574 gtk_real_menu_shell_cancel (GtkMenuShell      *menu_shell)
1575 {
1576   /* Unset the active menu item so gtk_menu_popdown() doesn't see it.
1577    */
1578   gtk_menu_shell_deselect (menu_shell);
1579   
1580   gtk_menu_shell_deactivate (menu_shell);
1581   g_signal_emit (menu_shell, menu_shell_signals[SELECTION_DONE], 0);
1582 }
1583
1584 static void
1585 gtk_real_menu_shell_cycle_focus (GtkMenuShell      *menu_shell,
1586                                  GtkDirectionType   dir)
1587 {
1588   while (menu_shell && !GTK_IS_MENU_BAR (menu_shell))
1589     {
1590       if (menu_shell->parent_menu_shell)
1591         menu_shell = GTK_MENU_SHELL (menu_shell->parent_menu_shell);
1592       else
1593         menu_shell = NULL;
1594     }
1595
1596   if (menu_shell)
1597     _gtk_menu_bar_cycle_focus (GTK_MENU_BAR (menu_shell), dir);
1598 }
1599
1600 gint
1601 _gtk_menu_shell_get_popup_delay (GtkMenuShell *menu_shell)
1602 {
1603   GtkMenuShellClass *klass = GTK_MENU_SHELL_GET_CLASS (menu_shell);
1604   
1605   if (klass->get_popup_delay)
1606     {
1607       return klass->get_popup_delay (menu_shell);
1608     }
1609   else
1610     {
1611       gint popup_delay;
1612       GtkWidget *widget = GTK_WIDGET (menu_shell);
1613       
1614       g_object_get (gtk_widget_get_settings (widget),
1615                     "gtk-menu-popup-delay", &popup_delay,
1616                     NULL);
1617       
1618       return popup_delay;
1619     }
1620 }
1621
1622 /**
1623  * gtk_menu_shell_cancel:
1624  * @menu_shell: a #GtkMenuShell
1625  * 
1626  * Cancels the selection within the menu shell.  
1627  * 
1628  * Since: 2.4
1629  */
1630 void
1631 gtk_menu_shell_cancel (GtkMenuShell *menu_shell)
1632 {
1633   g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
1634
1635   g_signal_emit (menu_shell, menu_shell_signals[CANCEL], 0);
1636 }
1637
1638 static GtkMnemonicHash *
1639 gtk_menu_shell_get_mnemonic_hash (GtkMenuShell *menu_shell,
1640                                   gboolean      create)
1641 {
1642   GtkMenuShellPrivate *private = GTK_MENU_SHELL_GET_PRIVATE (menu_shell);
1643
1644   if (!private->mnemonic_hash && create)
1645     private->mnemonic_hash = _gtk_mnemonic_hash_new ();
1646   
1647   return private->mnemonic_hash;
1648 }
1649
1650 static void
1651 menu_shell_add_mnemonic_foreach (guint    keyval,
1652                                  GSList  *targets,
1653                                  gpointer data)
1654 {
1655   GtkKeyHash *key_hash = data;
1656
1657   _gtk_key_hash_add_entry (key_hash, keyval, 0, GUINT_TO_POINTER (keyval));
1658 }
1659
1660 static GtkKeyHash *
1661 gtk_menu_shell_get_key_hash (GtkMenuShell *menu_shell,
1662                              gboolean      create)
1663 {
1664   GtkMenuShellPrivate *private = GTK_MENU_SHELL_GET_PRIVATE (menu_shell);
1665   GtkWidget *widget = GTK_WIDGET (menu_shell);
1666
1667   if (!private->key_hash && create && gtk_widget_has_screen (widget))
1668     {
1669       GtkMnemonicHash *mnemonic_hash = gtk_menu_shell_get_mnemonic_hash (menu_shell, FALSE);
1670       GdkScreen *screen = gtk_widget_get_screen (widget);
1671       GdkKeymap *keymap = gdk_keymap_get_for_display (gdk_screen_get_display (screen));
1672
1673       if (!mnemonic_hash)
1674         return NULL;
1675       
1676       private->key_hash = _gtk_key_hash_new (keymap, NULL);
1677
1678       _gtk_mnemonic_hash_foreach (mnemonic_hash,
1679                                   menu_shell_add_mnemonic_foreach,
1680                                   private->key_hash);
1681     }
1682   
1683   return private->key_hash;
1684 }
1685
1686 static void
1687 gtk_menu_shell_reset_key_hash (GtkMenuShell *menu_shell)
1688 {
1689   GtkMenuShellPrivate *private = GTK_MENU_SHELL_GET_PRIVATE (menu_shell);
1690
1691   if (private->key_hash)
1692     {
1693       _gtk_key_hash_free (private->key_hash);
1694       private->key_hash = NULL;
1695     }
1696 }
1697
1698 static gboolean
1699 gtk_menu_shell_activate_mnemonic (GtkMenuShell *menu_shell,
1700                                   GdkEventKey  *event)
1701 {
1702   GtkMnemonicHash *mnemonic_hash;
1703   GtkKeyHash *key_hash;
1704   GSList *entries;
1705   gboolean result = FALSE;
1706
1707   mnemonic_hash = gtk_menu_shell_get_mnemonic_hash (menu_shell, FALSE);
1708   if (!mnemonic_hash)
1709     return FALSE;
1710
1711   key_hash = gtk_menu_shell_get_key_hash (menu_shell, TRUE);
1712   if (!key_hash)
1713     return FALSE;
1714   
1715   entries = _gtk_key_hash_lookup (key_hash,
1716                                   event->hardware_keycode,
1717                                   event->state,
1718                                   gtk_accelerator_get_default_mod_mask (),
1719                                   event->group);
1720
1721   if (entries)
1722     result = _gtk_mnemonic_hash_activate (mnemonic_hash,
1723                                           GPOINTER_TO_UINT (entries->data));
1724
1725   return result;
1726 }
1727
1728 void
1729 _gtk_menu_shell_add_mnemonic (GtkMenuShell *menu_shell,
1730                               guint      keyval,
1731                               GtkWidget *target)
1732 {
1733   g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
1734   g_return_if_fail (GTK_IS_WIDGET (target));
1735
1736   _gtk_mnemonic_hash_add (gtk_menu_shell_get_mnemonic_hash (menu_shell, TRUE),
1737                           keyval, target);
1738   gtk_menu_shell_reset_key_hash (menu_shell);
1739 }
1740
1741 void
1742 _gtk_menu_shell_remove_mnemonic (GtkMenuShell *menu_shell,
1743                                  guint      keyval,
1744                                  GtkWidget *target)
1745 {
1746   g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
1747   g_return_if_fail (GTK_IS_WIDGET (target));
1748   
1749   _gtk_mnemonic_hash_remove (gtk_menu_shell_get_mnemonic_hash (menu_shell, TRUE),
1750                              keyval, target);
1751   gtk_menu_shell_reset_key_hash (menu_shell);
1752 }
1753
1754 void
1755 _gtk_menu_shell_set_grab_devices (GtkMenuShell *menu_shell,
1756                                   GdkDevice    *keyboard,
1757                                   GdkDevice    *pointer)
1758 {
1759   GtkMenuShellPrivate *priv = GTK_MENU_SHELL_GET_PRIVATE (menu_shell);
1760
1761   g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
1762   g_return_if_fail (!keyboard || GDK_IS_DEVICE (keyboard));
1763   g_return_if_fail (!pointer || GDK_IS_DEVICE (pointer));
1764
1765   priv->grab_keyboard = keyboard;
1766   priv->grab_pointer = pointer;
1767 }
1768
1769 gboolean
1770 _gtk_menu_shell_get_grab_devices (GtkMenuShell  *menu_shell,
1771                                   GdkDevice    **keyboard,
1772                                   GdkDevice    **pointer)
1773 {
1774   GtkMenuShellPrivate *priv = GTK_MENU_SHELL_GET_PRIVATE (menu_shell);
1775
1776   g_return_val_if_fail (GTK_IS_MENU_SHELL (menu_shell), FALSE);
1777
1778   if (keyboard)
1779     *keyboard = priv->grab_keyboard;
1780
1781   if (pointer)
1782     *pointer = priv->grab_pointer;
1783
1784   return TRUE;
1785 }
1786
1787 /**
1788  * gtk_menu_shell_get_take_focus:
1789  * @menu_shell: a #GtkMenuShell
1790  *
1791  * Returns %TRUE if the menu shell will take the keyboard focus on popup.
1792  *
1793  * Returns: %TRUE if the menu shell will take the keyboard focus on popup.
1794  *
1795  * Since: 2.8
1796  **/
1797 gboolean
1798 gtk_menu_shell_get_take_focus (GtkMenuShell *menu_shell)
1799 {
1800   GtkMenuShellPrivate *priv;
1801
1802   g_return_val_if_fail (GTK_IS_MENU_SHELL (menu_shell), FALSE);
1803
1804   priv = GTK_MENU_SHELL_GET_PRIVATE (menu_shell);
1805
1806   return priv->take_focus;
1807 }
1808
1809 /**
1810  * gtk_menu_shell_set_take_focus:
1811  * @menu_shell: a #GtkMenuShell
1812  * @take_focus: %TRUE if the menu shell should take the keyboard focus on popup.
1813  *
1814  * If @take_focus is %TRUE (the default) the menu shell will take the keyboard 
1815  * focus so that it will receive all keyboard events which is needed to enable
1816  * keyboard navigation in menus.
1817  *
1818  * Setting @take_focus to %FALSE is useful only for special applications
1819  * like virtual keyboard implementations which should not take keyboard
1820  * focus.
1821  *
1822  * The @take_focus state of a menu or menu bar is automatically propagated
1823  * to submenus whenever a submenu is popped up, so you don't have to worry
1824  * about recursively setting it for your entire menu hierarchy. Only when
1825  * programmatically picking a submenu and popping it up manually, the
1826  * @take_focus property of the submenu needs to be set explicitely.
1827  *
1828  * Note that setting it to %FALSE has side-effects:
1829  *
1830  * If the focus is in some other app, it keeps the focus and keynav in
1831  * the menu doesn't work. Consequently, keynav on the menu will only
1832  * work if the focus is on some toplevel owned by the onscreen keyboard.
1833  *
1834  * To avoid confusing the user, menus with @take_focus set to %FALSE
1835  * should not display mnemonics or accelerators, since it cannot be
1836  * guaranteed that they will work.
1837  *
1838  * See also gdk_keyboard_grab()
1839  *
1840  * Since: 2.8
1841  **/
1842 void
1843 gtk_menu_shell_set_take_focus (GtkMenuShell *menu_shell,
1844                                gboolean      take_focus)
1845 {
1846   GtkMenuShellPrivate *priv;
1847
1848   g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
1849
1850   priv = GTK_MENU_SHELL_GET_PRIVATE (menu_shell);
1851
1852   if (priv->take_focus != take_focus)
1853     {
1854       priv->take_focus = take_focus;
1855       g_object_notify (G_OBJECT (menu_shell), "take-focus");
1856     }
1857 }
1858
1859 #define __GTK_MENU_SHELL_C__
1860 #include "gtkaliasdef.c"