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