~canonical-dx-team/ubuntu/maverick/gtk+2.0/menuproxy

« back to all changes in this revision

Viewing changes to gtk/gtkmenushell.c

  • Committer: Bazaar Package Importer
  • Author(s): Sebastien Bacher
  • Date: 2007-05-04 12:24:25 UTC
  • mfrom: (1.1.21 upstream)
  • Revision ID: james.westby@ubuntu.com-20070504122425-0m8midgzrp40y8w2
Tags: 2.10.12-1ubuntu1
* Sync with Debian
* New upstream version:
  Fixed bugs:
  - 379414 file chooser warnings when changing path in the entry
  - 418585 GtkFileChooserDefault sizing code is not DPI independent
  - 419568 Crash in search if start with special letter
  - 435062 build dies with icon cache validation
  - 379399 Segfault to call gtk_print_operation_run twice.
  - 387889 cups backend has problems when there are too many printers
  - 418531 invalid read to gtkicontheme.c gtk_icon_theme_lookup_icon...
  - 423916 crash in color scheme code
  - 424042 Segmentation fault while quickly pressing Alt+arrows
  - 415260 Protect against negative indices when setting values in G...
  - 419171 XGetVisualInfo() may not set nxvisuals
  - 128852 Gdk cursors don't look good on win32
  - 344657 Ctrl-H doesn't toggle "Show Hidden Files" setting
  - 345345 PrintOperation::paginate is not emitted for class handler
  - 347567 GtkPrintOperation::end-print is not emitted if it's cance...
  - 369112 gtk_ui_manager_add_ui should accept unnamed separator
  - 392015 Selected menu item invisible on Windows Vista
  - 399253 MS-Windows Theme Bottom Tab placement rendering glitches
  - 399425 gtk_input_dialog_fill_axes() adds child to gtkscrolledwin...
  - 403251 [patch] little memory leak in GtkPrintJob
  - 403267 [patch] memory leak in GtkPageSetupUnixDialog
  - 403470 MS-Windows Theme tab placement other than on top leaks a ...
  - 404506 Windows system fonts that have multi-byte font names cann...
  - 405089 Incorrect window placement for GtkEventBox private window
  - 405515 Minor leak in gtkfilesystemmodel.c
  - 405539 gdk_pixbuf_save() for PNG saver can return FALSE without ...
  - 415681 gdk_window_clear_area includes an extra line and column o...
  - 418219 GtkRecentChooser should apply filter before sorting and c...
  - 418403 Scroll to printer after selecting it from settings
  - 421985 _gtk_print_operation_platform_backend_launch_preview
  - 421990 gtk_print_job_get_surface
  - 421993 gtk_print_operation_init
  - 423064 Conditional jump or move depends on uninitialised value(s...
  - 423722 Fix printing header in gtk-demo
  - 424168 gtk_print_operation_run on async preview
  - 425655 Don't install gtk+-unix-print-2.0.pc on non-UNIX platforms
  - 425786 GDK segfaults if XineramaQueryScreens fails
  - 428665 Lpr Backend gets stuck in infinite loop during gtk_enumer...
  - 429902 GtkPrintOperation leaks cairo contextes
  - 431997 First delay of GdkPixbufAnimationIter is wrong
  - 433242 Inconsistent scroll arrow position calculations
  - 433972 Placing gtk.Expander inside a gtk.TextView() changes gtk....
  - 434261 _gtk_toolbar_elide_underscores incorrectly handles some s...
  - 383354 ctrl-L should make 'Location' entry disappear
  - 418673 gtk_recent_manager_add_item
  - 429732 gtk_accel_group_finalize accesses invalid memory
  - 435028 WM_CLIENT_LEADER is wrong on the leader_window
  - 431067 Background of the header window is not updated
  - 338843 add recent files support inside the ui manager
  - 148535 add drop shadow to menus, tooltips, etc. under Windows XP
* debian/control.in:
  - Conflicts on ubuntulooks (<= 0.9.11-1)
* debian/patches/15_default-fallback-icon-theme.patch:
  - patch from Debian, fallback on gnome icon theme

Show diffs side-by-side

added added

removed removed

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