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