]> Pileus Git - ~andy/gtk/blob - gtk/gtkmenushell.c
Add new infrastructure for notifications of failed keyboard navigation and
[~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   
689   g_return_val_if_fail (GTK_IS_MENU_SHELL (widget), FALSE);
690   g_return_val_if_fail (event != NULL, FALSE);
691       
692   menu_shell = GTK_MENU_SHELL (widget);
693
694   if (!menu_shell->active_menu_item && menu_shell->parent_menu_shell)
695     return gtk_widget_event (menu_shell->parent_menu_shell, (GdkEvent *)event);
696   
697   if (gtk_bindings_activate_event (GTK_OBJECT (widget), event))
698     return TRUE;
699
700   return gtk_menu_shell_activate_mnemonic (menu_shell, event);
701 }
702
703 static gint
704 gtk_menu_shell_enter_notify (GtkWidget        *widget,
705                              GdkEventCrossing *event)
706 {
707   GtkMenuShell *menu_shell;
708   GtkWidget *menu_item;
709
710   g_return_val_if_fail (GTK_IS_MENU_SHELL (widget), FALSE);
711   g_return_val_if_fail (event != NULL, FALSE);
712
713   menu_shell = GTK_MENU_SHELL (widget);
714
715   if (menu_shell->active)
716     {
717       menu_item = gtk_get_event_widget ((GdkEvent*) event);
718
719       if (!menu_item ||
720           (GTK_IS_MENU_ITEM (menu_item) && 
721            !_gtk_menu_item_is_selectable (menu_item)))
722         return TRUE;
723       
724       if ((menu_item->parent == widget) &&
725           (menu_shell->active_menu_item != menu_item) &&
726           GTK_IS_MENU_ITEM (menu_item))
727         {
728           if (menu_shell->ignore_enter)
729             return TRUE;
730           
731           if ((event->detail != GDK_NOTIFY_INFERIOR) &&
732               (GTK_WIDGET_STATE (menu_item) != GTK_STATE_PRELIGHT))
733             {
734               gtk_menu_shell_select_item (menu_shell, menu_item);
735             }
736         }
737       else if (menu_shell->parent_menu_shell)
738         {
739           gtk_widget_event (menu_shell->parent_menu_shell, (GdkEvent*) event);
740         }
741     }
742
743   return TRUE;
744 }
745
746 static gint
747 gtk_menu_shell_leave_notify (GtkWidget        *widget,
748                              GdkEventCrossing *event)
749 {
750   GtkMenuShell *menu_shell;
751   GtkMenuItem *menu_item;
752   GtkWidget *event_widget;
753
754   g_return_val_if_fail (GTK_IS_MENU_SHELL (widget), FALSE);
755   g_return_val_if_fail (event != NULL, FALSE);
756
757   if (GTK_WIDGET_VISIBLE (widget))
758     {
759       menu_shell = GTK_MENU_SHELL (widget);
760       event_widget = gtk_get_event_widget ((GdkEvent*) event);
761
762       if (!event_widget || !GTK_IS_MENU_ITEM (event_widget))
763         return TRUE;
764
765       menu_item = GTK_MENU_ITEM (event_widget);
766
767       if (!_gtk_menu_item_is_selectable (event_widget))
768         return TRUE;
769
770       if ((menu_shell->active_menu_item == event_widget) &&
771           (menu_item->submenu == NULL))
772         {
773           if ((event->detail != GDK_NOTIFY_INFERIOR) &&
774               (GTK_WIDGET_STATE (menu_item) != GTK_STATE_NORMAL))
775             {
776               gtk_menu_shell_deselect (menu_shell);
777             }
778         }
779       else if (menu_shell->parent_menu_shell)
780         {
781           gtk_widget_event (menu_shell->parent_menu_shell, (GdkEvent*) event);
782         }
783     }
784
785   return TRUE;
786 }
787
788 static void
789 gtk_menu_shell_screen_changed (GtkWidget *widget,
790                                GdkScreen *previous_screen)
791 {
792   gtk_menu_shell_reset_key_hash (GTK_MENU_SHELL (widget));
793 }
794
795 static void
796 gtk_menu_shell_add (GtkContainer *container,
797                     GtkWidget    *widget)
798 {
799   gtk_menu_shell_append (GTK_MENU_SHELL (container), widget);
800 }
801
802 static void
803 gtk_menu_shell_remove (GtkContainer *container,
804                        GtkWidget    *widget)
805 {
806   GtkMenuShell *menu_shell;
807   gint was_visible;
808   
809   g_return_if_fail (GTK_IS_MENU_SHELL (container));
810   g_return_if_fail (GTK_IS_MENU_ITEM (widget));
811   
812   was_visible = GTK_WIDGET_VISIBLE (widget);
813   menu_shell = GTK_MENU_SHELL (container);
814   menu_shell->children = g_list_remove (menu_shell->children, widget);
815   
816   if (widget == menu_shell->active_menu_item)
817     {
818       gtk_item_deselect (GTK_ITEM (menu_shell->active_menu_item));
819       menu_shell->active_menu_item = NULL;
820     }
821
822   gtk_widget_unparent (widget);
823   
824   /* queue resize regardless of GTK_WIDGET_VISIBLE (container),
825    * since that's what is needed by toplevels.
826    */
827   if (was_visible)
828     gtk_widget_queue_resize (GTK_WIDGET (container));
829 }
830
831 static void
832 gtk_menu_shell_forall (GtkContainer *container,
833                        gboolean      include_internals,
834                        GtkCallback   callback,
835                        gpointer      callback_data)
836 {
837   GtkMenuShell *menu_shell;
838   GtkWidget *child;
839   GList *children;
840
841   g_return_if_fail (GTK_IS_MENU_SHELL (container));
842   g_return_if_fail (callback != NULL);
843
844   menu_shell = GTK_MENU_SHELL (container);
845
846   children = menu_shell->children;
847   while (children)
848     {
849       child = children->data;
850       children = children->next;
851
852       (* callback) (child, callback_data);
853     }
854 }
855
856
857 static void
858 gtk_real_menu_shell_deactivate (GtkMenuShell *menu_shell)
859 {
860   if (menu_shell->active)
861     {
862       menu_shell->button = 0;
863       menu_shell->active = FALSE;
864       menu_shell->activate_time = 0;
865
866       if (menu_shell->active_menu_item)
867         {
868           gtk_menu_item_deselect (GTK_MENU_ITEM (menu_shell->active_menu_item));
869           menu_shell->active_menu_item = NULL;
870         }
871
872       if (menu_shell->have_grab)
873         {
874           menu_shell->have_grab = FALSE;
875           gtk_grab_remove (GTK_WIDGET (menu_shell));
876         }
877       if (menu_shell->have_xgrab)
878         {
879           GdkDisplay *display = gtk_widget_get_display (GTK_WIDGET (menu_shell));
880           
881           menu_shell->have_xgrab = FALSE;
882           gdk_display_pointer_ungrab (display, GDK_CURRENT_TIME);
883           gdk_display_keyboard_ungrab (display, GDK_CURRENT_TIME);
884         }
885     }
886 }
887
888 static gint
889 gtk_menu_shell_is_item (GtkMenuShell *menu_shell,
890                         GtkWidget    *child)
891 {
892   GtkWidget *parent;
893
894   g_return_val_if_fail (GTK_IS_MENU_SHELL (menu_shell), FALSE);
895   g_return_val_if_fail (child != NULL, FALSE);
896
897   parent = child->parent;
898   while (parent && GTK_IS_MENU_SHELL (parent))
899     {
900       if (parent == (GtkWidget*) menu_shell)
901         return TRUE;
902       parent = GTK_MENU_SHELL (parent)->parent_menu_shell;
903     }
904
905   return FALSE;
906 }
907
908 static GtkWidget*
909 gtk_menu_shell_get_item (GtkMenuShell *menu_shell,
910                          GdkEvent     *event)
911 {
912   GtkWidget *menu_item;
913
914   menu_item = gtk_get_event_widget ((GdkEvent*) event);
915   
916   while (menu_item && !GTK_IS_MENU_ITEM (menu_item))
917     menu_item = menu_item->parent;
918
919   if (menu_item && gtk_menu_shell_is_item (menu_shell, menu_item))
920     return menu_item;
921   else
922     return NULL;
923 }
924
925 /* Handlers for action signals */
926
927 void
928 gtk_menu_shell_select_item (GtkMenuShell *menu_shell,
929                             GtkWidget    *menu_item)
930 {
931   GtkMenuShellClass *class;
932
933   g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
934   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
935
936   class = GTK_MENU_SHELL_GET_CLASS (menu_shell);
937
938   if (class->select_item &&
939       !(menu_shell->active &&
940         menu_shell->active_menu_item == menu_item))
941     class->select_item (menu_shell, menu_item);
942 }
943
944 void _gtk_menu_item_set_placement (GtkMenuItem         *menu_item,
945                                    GtkSubmenuPlacement  placement);
946
947 static void
948 gtk_menu_shell_real_select_item (GtkMenuShell *menu_shell,
949                                  GtkWidget    *menu_item)
950 {
951   GtkPackDirection pack_dir = PACK_DIRECTION (menu_shell);
952
953   gtk_menu_shell_deselect (menu_shell);
954
955   if (!_gtk_menu_item_is_selectable (menu_item))
956     return;
957
958   menu_shell->active_menu_item = menu_item;
959   if (pack_dir == GTK_PACK_DIRECTION_TTB || pack_dir == GTK_PACK_DIRECTION_BTT)
960     _gtk_menu_item_set_placement (GTK_MENU_ITEM (menu_shell->active_menu_item),
961                                   GTK_LEFT_RIGHT);
962   else
963     _gtk_menu_item_set_placement (GTK_MENU_ITEM (menu_shell->active_menu_item),
964                                   GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement);
965   gtk_menu_item_select (GTK_MENU_ITEM (menu_shell->active_menu_item));
966
967   /* This allows the bizarre radio buttons-with-submenus-display-history
968    * behavior
969    */
970   if (GTK_MENU_ITEM (menu_shell->active_menu_item)->submenu)
971     gtk_widget_activate (menu_shell->active_menu_item);
972 }
973
974 void
975 gtk_menu_shell_deselect (GtkMenuShell *menu_shell)
976 {
977   g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
978
979   if (menu_shell->active_menu_item)
980     {
981       gtk_menu_item_deselect (GTK_MENU_ITEM (menu_shell->active_menu_item));
982       menu_shell->active_menu_item = NULL;
983     }
984 }
985
986 void
987 gtk_menu_shell_activate_item (GtkMenuShell      *menu_shell,
988                               GtkWidget         *menu_item,
989                               gboolean           force_deactivate)
990 {
991   GSList *slist, *shells = NULL;
992   gboolean deactivate = force_deactivate;
993
994   g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
995   g_return_if_fail (GTK_IS_MENU_ITEM (menu_item));
996
997   if (!deactivate)
998     deactivate = GTK_MENU_ITEM_GET_CLASS (menu_item)->hide_on_activate;
999
1000   g_object_ref (menu_shell);
1001   g_object_ref (menu_item);
1002
1003   if (deactivate)
1004     {
1005       GtkMenuShell *parent_menu_shell = menu_shell;
1006
1007       do
1008         {
1009           g_object_ref (parent_menu_shell);
1010           shells = g_slist_prepend (shells, parent_menu_shell);
1011           parent_menu_shell = (GtkMenuShell*) parent_menu_shell->parent_menu_shell;
1012         }
1013       while (parent_menu_shell);
1014       shells = g_slist_reverse (shells);
1015
1016       gtk_menu_shell_deactivate (menu_shell);
1017   
1018       /* flush the x-queue, so any grabs are removed and
1019        * the menu is actually taken down
1020        */
1021       gdk_display_sync (gtk_widget_get_display (menu_item));
1022     }
1023
1024   gtk_widget_activate (menu_item);
1025
1026   for (slist = shells; slist; slist = slist->next)
1027     {
1028       g_signal_emit (slist->data, menu_shell_signals[SELECTION_DONE], 0);
1029       g_object_unref (slist->data);
1030     }
1031   g_slist_free (shells);
1032
1033   g_object_unref (menu_shell);
1034   g_object_unref (menu_item);
1035 }
1036
1037 /* Distance should be +/- 1 */
1038 static void
1039 gtk_menu_shell_move_selected (GtkMenuShell  *menu_shell, 
1040                               gint           distance)
1041 {
1042   if (menu_shell->active_menu_item)
1043     {
1044       GList *node = g_list_find (menu_shell->children,
1045                                  menu_shell->active_menu_item);
1046       GList *start_node = node;
1047       gboolean wrap_around;
1048
1049       g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu_shell)),
1050                     "gtk-keynav-wrap-around", &wrap_around,
1051                     NULL);
1052
1053       if (distance > 0)
1054         {
1055           node = node->next;
1056           while (node != start_node && 
1057                  (!node || !_gtk_menu_item_is_selectable (node->data)))
1058             {
1059               if (node)
1060                 node = node->next;
1061               else if (wrap_around)
1062                 node = menu_shell->children;
1063               else
1064                 {
1065                   gtk_widget_error_bell (GTK_WIDGET (menu_shell));
1066                   break;
1067                 }
1068             }
1069         }
1070       else
1071         {
1072           node = node->prev;
1073           while (node != start_node &&
1074                  (!node || !_gtk_menu_item_is_selectable (node->data)))
1075             {
1076               if (node)
1077                 node = node->prev;
1078               else if (wrap_around)
1079                 node = g_list_last (menu_shell->children);
1080               else
1081                 {
1082                   gtk_widget_error_bell (GTK_WIDGET (menu_shell));
1083                   break;
1084                 }
1085             }
1086         }
1087       
1088       if (node)
1089         gtk_menu_shell_select_item (menu_shell, node->data);
1090     }
1091 }
1092
1093 /**
1094  * gtk_menu_shell_select_first:
1095  * @menu_shell: a #GtkMenuShell
1096  * @search_sensitive: if %TRUE, search for the first selectable
1097  *                    menu item, otherwise select nothing if
1098  *                    the first item isn't sensitive. This
1099  *                    should be %FALSE if the menu is being
1100  *                    popped up initially.
1101  * 
1102  * Select the first visible or selectable child of the menu shell;
1103  * don't select tearoff items unless the only item is a tearoff
1104  * item.
1105  *
1106  * Since: 2.2
1107  **/
1108 void
1109 gtk_menu_shell_select_first (GtkMenuShell *menu_shell,
1110                              gboolean      search_sensitive)
1111 {
1112   GtkWidget *to_select = NULL;
1113   GList *tmp_list;
1114
1115   tmp_list = menu_shell->children;
1116   while (tmp_list)
1117     {
1118       GtkWidget *child = tmp_list->data;
1119       
1120       if ((!search_sensitive && GTK_WIDGET_VISIBLE (child)) ||
1121           _gtk_menu_item_is_selectable (child))
1122         {
1123           to_select = child;
1124           if (!GTK_IS_TEAROFF_MENU_ITEM (child))
1125             break;
1126         }
1127       
1128       tmp_list = tmp_list->next;
1129     }
1130
1131   if (to_select)
1132     gtk_menu_shell_select_item (menu_shell, to_select);
1133 }
1134
1135 void
1136 _gtk_menu_shell_select_last (GtkMenuShell *menu_shell,
1137                              gboolean      search_sensitive)
1138 {
1139   GtkWidget *to_select = NULL;
1140   GList *tmp_list;
1141
1142   tmp_list = g_list_last (menu_shell->children);
1143   while (tmp_list)
1144     {
1145       GtkWidget *child = tmp_list->data;
1146       
1147       if ((!search_sensitive && GTK_WIDGET_VISIBLE (child)) ||
1148           _gtk_menu_item_is_selectable (child))
1149         {
1150           to_select = child;
1151           if (!GTK_IS_TEAROFF_MENU_ITEM (child))
1152             break;
1153         }
1154       
1155       tmp_list = tmp_list->prev;
1156     }
1157
1158   if (to_select)
1159     gtk_menu_shell_select_item (menu_shell, to_select);
1160 }
1161
1162 static gboolean
1163 gtk_menu_shell_select_submenu_first (GtkMenuShell     *menu_shell)
1164 {
1165   GtkMenuItem *menu_item;
1166
1167   menu_item = GTK_MENU_ITEM (menu_shell->active_menu_item); 
1168   
1169   if (menu_item->submenu)
1170     {
1171       _gtk_menu_item_popup_submenu (GTK_WIDGET (menu_item));
1172       gtk_menu_shell_select_first (GTK_MENU_SHELL (menu_item->submenu), TRUE);
1173       if (GTK_MENU_SHELL (menu_item->submenu)->active_menu_item)
1174         return TRUE;
1175     }
1176
1177   return FALSE;
1178 }
1179
1180 static void
1181 gtk_real_menu_shell_move_current (GtkMenuShell      *menu_shell,
1182                                   GtkMenuDirectionType direction)
1183 {
1184   GtkMenuShell *parent_menu_shell = NULL;
1185   gboolean had_selection;
1186
1187   had_selection = menu_shell->active_menu_item != NULL;
1188
1189   if (menu_shell->parent_menu_shell)
1190     parent_menu_shell = GTK_MENU_SHELL (menu_shell->parent_menu_shell);
1191
1192   switch (direction)
1193     {
1194     case GTK_MENU_DIR_PARENT:
1195       if (parent_menu_shell)
1196         {
1197           if (GTK_MENU_SHELL_GET_CLASS (parent_menu_shell)->submenu_placement == 
1198                        GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement)
1199             gtk_menu_shell_deselect (menu_shell);
1200           else 
1201             {
1202               if (PACK_DIRECTION (parent_menu_shell) == GTK_PACK_DIRECTION_LTR)
1203                 gtk_menu_shell_move_selected (parent_menu_shell, -1);
1204               else
1205                 gtk_menu_shell_move_selected (parent_menu_shell, 1);
1206               gtk_menu_shell_select_submenu_first (parent_menu_shell); 
1207             }
1208         }
1209       /* If there is no parent and the submenu is in the opposite direction
1210        * to the menu, then make the PARENT direction wrap around to
1211        * the bottom of the submenu.
1212        */
1213       else if (menu_shell->active_menu_item &&
1214                _gtk_menu_item_is_selectable (menu_shell->active_menu_item) &&
1215                GTK_MENU_ITEM (menu_shell->active_menu_item)->submenu)
1216         {
1217           GtkMenuShell *submenu = GTK_MENU_SHELL (GTK_MENU_ITEM (menu_shell->active_menu_item)->submenu);
1218
1219           if (GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement !=
1220               GTK_MENU_SHELL_GET_CLASS (submenu)->submenu_placement)
1221             _gtk_menu_shell_select_last (submenu, TRUE);
1222         }
1223       break;
1224       
1225     case GTK_MENU_DIR_CHILD:
1226       if (menu_shell->active_menu_item &&
1227           _gtk_menu_item_is_selectable (menu_shell->active_menu_item) &&
1228           GTK_MENU_ITEM (menu_shell->active_menu_item)->submenu)
1229         {
1230           if (gtk_menu_shell_select_submenu_first (menu_shell))
1231             break;
1232         }
1233
1234       /* Try to find a menu running the opposite direction */
1235       while (parent_menu_shell && 
1236              (GTK_MENU_SHELL_GET_CLASS (parent_menu_shell)->submenu_placement ==
1237               GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement))
1238         {
1239           GtkWidget *tmp_widget = parent_menu_shell->parent_menu_shell;
1240           
1241           if (tmp_widget)
1242             parent_menu_shell = GTK_MENU_SHELL (tmp_widget);
1243           else
1244             parent_menu_shell = NULL;
1245         }
1246       
1247       if (parent_menu_shell)
1248         {
1249           if (PACK_DIRECTION (parent_menu_shell) == GTK_PACK_DIRECTION_LTR)
1250             gtk_menu_shell_move_selected (parent_menu_shell, 1);
1251           else
1252             gtk_menu_shell_move_selected (parent_menu_shell, -1);
1253
1254           gtk_menu_shell_select_submenu_first (parent_menu_shell);
1255         }
1256       break;
1257       
1258     case GTK_MENU_DIR_PREV:
1259       gtk_menu_shell_move_selected (menu_shell, -1);
1260       if (!had_selection &&
1261           !menu_shell->active_menu_item &&
1262           menu_shell->children)
1263         _gtk_menu_shell_select_last (menu_shell, TRUE);
1264       break;
1265     case GTK_MENU_DIR_NEXT:
1266       gtk_menu_shell_move_selected (menu_shell, 1);
1267       if (!had_selection &&
1268           !menu_shell->active_menu_item &&
1269           menu_shell->children)
1270         gtk_menu_shell_select_first (menu_shell, TRUE);
1271       break;
1272     }
1273 }
1274
1275 static void
1276 gtk_real_menu_shell_activate_current (GtkMenuShell      *menu_shell,
1277                                       gboolean           force_hide)
1278 {
1279   if (menu_shell->active_menu_item &&
1280       _gtk_menu_item_is_selectable (menu_shell->active_menu_item))
1281   {
1282    
1283     if (GTK_MENU_ITEM (menu_shell->active_menu_item)->submenu == NULL)
1284       gtk_menu_shell_activate_item (menu_shell,
1285                                     menu_shell->active_menu_item,
1286                                     force_hide);
1287     else
1288       _gtk_menu_item_popup_submenu (menu_shell->active_menu_item);
1289   }
1290 }
1291
1292 static void
1293 gtk_real_menu_shell_cancel (GtkMenuShell      *menu_shell)
1294 {
1295   /* Unset the active menu item so gtk_menu_popdown() doesn't see it.
1296    */
1297   gtk_menu_shell_deselect (menu_shell);
1298   
1299   gtk_menu_shell_deactivate (menu_shell);
1300   g_signal_emit (menu_shell, menu_shell_signals[SELECTION_DONE], 0);
1301 }
1302
1303 static void
1304 gtk_real_menu_shell_cycle_focus (GtkMenuShell      *menu_shell,
1305                                  GtkDirectionType   dir)
1306 {
1307   while (menu_shell && !GTK_IS_MENU_BAR (menu_shell))
1308     {
1309       if (menu_shell->parent_menu_shell)
1310         menu_shell = GTK_MENU_SHELL (menu_shell->parent_menu_shell);
1311       else
1312         menu_shell = NULL;
1313     }
1314
1315   if (menu_shell)
1316     _gtk_menu_bar_cycle_focus (GTK_MENU_BAR (menu_shell), dir);
1317 }
1318
1319 gint
1320 _gtk_menu_shell_get_popup_delay (GtkMenuShell *menu_shell)
1321 {
1322   GtkMenuShellClass *klass = GTK_MENU_SHELL_GET_CLASS (menu_shell);
1323   
1324   if (klass->get_popup_delay)
1325     {
1326       return klass->get_popup_delay (menu_shell);
1327     }
1328   else
1329     {
1330       gint popup_delay;
1331       GtkWidget *widget = GTK_WIDGET (menu_shell);
1332       
1333       g_object_get (gtk_widget_get_settings (widget),
1334                     "gtk-menu-popup-delay", &popup_delay,
1335                     NULL);
1336       
1337       return popup_delay;
1338     }
1339 }
1340
1341 /**
1342  * gtk_menu_shell_cancel:
1343  * @menu_shell: a #GtkMenuShell
1344  * 
1345  * Cancels the selection within the menu shell.  
1346  * 
1347  * Since: 2.4
1348  */
1349 void
1350 gtk_menu_shell_cancel (GtkMenuShell *menu_shell)
1351 {
1352   g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
1353
1354   g_signal_emit (menu_shell, menu_shell_signals[CANCEL], 0);
1355 }
1356
1357 static GtkMnemonicHash *
1358 gtk_menu_shell_get_mnemonic_hash (GtkMenuShell *menu_shell,
1359                                   gboolean      create)
1360 {
1361   GtkMenuShellPrivate *private = GTK_MENU_SHELL_GET_PRIVATE (menu_shell);
1362
1363   if (!private->mnemonic_hash && create)
1364     private->mnemonic_hash = _gtk_mnemonic_hash_new ();
1365   
1366   return private->mnemonic_hash;
1367 }
1368
1369 static void
1370 menu_shell_add_mnemonic_foreach (guint    keyval,
1371                                  GSList  *targets,
1372                                  gpointer data)
1373 {
1374   GtkKeyHash *key_hash = data;
1375
1376   _gtk_key_hash_add_entry (key_hash, keyval, 0, GUINT_TO_POINTER (keyval));
1377 }
1378
1379 static GtkKeyHash *
1380 gtk_menu_shell_get_key_hash (GtkMenuShell *menu_shell,
1381                              gboolean      create)
1382 {
1383   GtkMenuShellPrivate *private = GTK_MENU_SHELL_GET_PRIVATE (menu_shell);
1384   GtkWidget *widget = GTK_WIDGET (menu_shell);
1385
1386   if (!private->key_hash && create && gtk_widget_has_screen (widget))
1387     {
1388       GtkMnemonicHash *mnemonic_hash = gtk_menu_shell_get_mnemonic_hash (menu_shell, FALSE);
1389       GdkScreen *screen = gtk_widget_get_screen (widget);
1390       GdkKeymap *keymap = gdk_keymap_get_for_display (gdk_screen_get_display (screen));
1391
1392       if (!mnemonic_hash)
1393         return NULL;
1394       
1395       private->key_hash = _gtk_key_hash_new (keymap, NULL);
1396
1397       _gtk_mnemonic_hash_foreach (mnemonic_hash,
1398                                   menu_shell_add_mnemonic_foreach,
1399                                   private->key_hash);
1400     }
1401   
1402   return private->key_hash;
1403 }
1404
1405 static void
1406 gtk_menu_shell_reset_key_hash (GtkMenuShell *menu_shell)
1407 {
1408   GtkMenuShellPrivate *private = GTK_MENU_SHELL_GET_PRIVATE (menu_shell);
1409
1410   if (private->key_hash)
1411     {
1412       _gtk_key_hash_free (private->key_hash);
1413       private->key_hash = NULL;
1414     }
1415 }
1416
1417 static gboolean
1418 gtk_menu_shell_activate_mnemonic (GtkMenuShell *menu_shell,
1419                                   GdkEventKey  *event)
1420 {
1421   GtkMnemonicHash *mnemonic_hash;
1422   GtkKeyHash *key_hash;
1423   GSList *entries;
1424   gboolean result = FALSE;
1425
1426   mnemonic_hash = gtk_menu_shell_get_mnemonic_hash (menu_shell, FALSE);
1427   if (!mnemonic_hash)
1428     return FALSE;
1429
1430   key_hash = gtk_menu_shell_get_key_hash (menu_shell, TRUE);
1431   if (!key_hash)
1432     return FALSE;
1433   
1434   entries = _gtk_key_hash_lookup (key_hash,
1435                                   event->hardware_keycode,
1436                                   event->state,
1437                                   gtk_accelerator_get_default_mod_mask (),
1438                                   event->group);
1439
1440   if (entries)
1441     result = _gtk_mnemonic_hash_activate (mnemonic_hash,
1442                                           GPOINTER_TO_UINT (entries->data));
1443
1444   return result;
1445 }
1446
1447 void
1448 _gtk_menu_shell_add_mnemonic (GtkMenuShell *menu_shell,
1449                               guint      keyval,
1450                               GtkWidget *target)
1451 {
1452   g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
1453   g_return_if_fail (GTK_IS_WIDGET (target));
1454
1455   _gtk_mnemonic_hash_add (gtk_menu_shell_get_mnemonic_hash (menu_shell, TRUE),
1456                           keyval, target);
1457   gtk_menu_shell_reset_key_hash (menu_shell);
1458 }
1459
1460 void
1461 _gtk_menu_shell_remove_mnemonic (GtkMenuShell *menu_shell,
1462                                  guint      keyval,
1463                                  GtkWidget *target)
1464 {
1465   g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
1466   g_return_if_fail (GTK_IS_WIDGET (target));
1467   
1468   _gtk_mnemonic_hash_remove (gtk_menu_shell_get_mnemonic_hash (menu_shell, TRUE),
1469                              keyval, target);
1470   gtk_menu_shell_reset_key_hash (menu_shell);
1471 }
1472
1473 /**
1474  * gtk_menu_shell_get_take_focus:
1475  * @menu_shell: a #GtkMenuShell
1476  *
1477  * Returns %TRUE if the menu shell will take the keyboard focus on popup.
1478  *
1479  * Returns: %TRUE if the menu shell will take the keyboard focus on popup.
1480  *
1481  * Since: 2.8
1482  **/
1483 gboolean
1484 gtk_menu_shell_get_take_focus (GtkMenuShell *menu_shell)
1485 {
1486   GtkMenuShellPrivate *priv;
1487
1488   g_return_val_if_fail (GTK_IS_MENU_SHELL (menu_shell), FALSE);
1489
1490   priv = GTK_MENU_SHELL_GET_PRIVATE (menu_shell);
1491
1492   return priv->take_focus;
1493 }
1494
1495 /**
1496  * gtk_menu_shell_set_take_focus:
1497  * @menu_shell: a #GtkMenuShell
1498  * @take_focus: %TRUE if the menu shell should take the keyboard focus on popup.
1499  *
1500  * If @take_focus is %TRUE (the default) the menu shell will take the keyboard 
1501  * focus so that it will receive all keyboard events which is needed to enable
1502  * keyboard navigation in menus.
1503  *
1504  * Setting @take_focus to %FALSE is useful only for special applications
1505  * like virtual keyboard implementations which should not take keyboard
1506  * focus.
1507  *
1508  * The @take_focus state of a menu or menu bar is automatically propagated
1509  * to submenus whenever a submenu is popped up, so you don't have to worry
1510  * about recursively setting it for your entire menu hierarchy. Only when
1511  * programmatically picking a submenu and popping it up manually, the
1512  * @take_focus property of the submenu needs to be set explicitely.
1513  *
1514  * Note that setting it to %FALSE has side-effects:
1515  *
1516  * If the focus is in some other app, it keeps the focus and keynav in
1517  * the menu doesn't work. Consequently, keynav on the menu will only
1518  * work if the focus is on some toplevel owned by the onscreen keyboard.
1519  *
1520  * To avoid confusing the user, menus with @take_focus set to %FALSE
1521  * should not display mnemonics or accelerators, since it cannot be
1522  * guaranteed that they will work.
1523  *
1524  * See also gdk_keyboard_grab()
1525  *
1526  * Since: 2.8
1527  **/
1528 void
1529 gtk_menu_shell_set_take_focus (GtkMenuShell *menu_shell,
1530                                gboolean      take_focus)
1531 {
1532   GtkMenuShellPrivate *priv;
1533
1534   g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
1535
1536   priv = GTK_MENU_SHELL_GET_PRIVATE (menu_shell);
1537
1538   if (priv->take_focus != take_focus)
1539     {
1540       priv->take_focus = take_focus;
1541       g_object_notify (G_OBJECT (menu_shell), "take-focus");
1542     }
1543 }
1544
1545 #define __GTK_MENU_SHELL_C__
1546 #include "gtkaliasdef.c"