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