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