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