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