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