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