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