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