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