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