1 --- gtk+-2.6.4/gtk/gtkmenushell.c 2005-02-09 18:46:54.000000000 +0200
2 +++ gtk+-2.6.4/gtk/gtkmenushell.c 2005-04-06 16:19:36.999913520 +0300
4 #include "gtkmnemonichash.h"
5 #include "gtktearoffmenuitem.h"
7 +#include "gtkprivate.h"
10 #define MENU_SHELL_TIMEOUT 500
21 typedef void (*GtkMenuShellSignal1) (GtkObject *object,
22 GtkMenuDirectionType arg1,
26 GtkMnemonicHash *mnemonic_hash;
28 + gboolean activated_submenu;
29 + gboolean take_focus;
32 static void gtk_menu_shell_class_init (GtkMenuShellClass *klass);
33 static void gtk_menu_shell_init (GtkMenuShell *menu_shell);
34 +static void gtk_menu_shell_set_property (GObject *object,
36 + const GValue *value,
38 +static void gtk_menu_shell_get_property (GObject *object,
42 static void gtk_menu_shell_realize (GtkWidget *widget);
43 static void gtk_menu_shell_finalize (GObject *object);
44 static gint gtk_menu_shell_button_press (GtkWidget *widget,
46 static GtkContainerClass *parent_class = NULL;
47 static guint menu_shell_signals[LAST_SIGNAL] = { 0 };
51 gtk_menu_shell_get_type (void)
54 container_class = (GtkContainerClass*) klass;
56 parent_class = g_type_class_peek_parent (klass);
57 + object_class->set_property = gtk_menu_shell_set_property;
58 + object_class->get_property = gtk_menu_shell_get_property;
60 object_class->finalize = gtk_menu_shell_finalize;
65 binding_set = gtk_binding_set_by_class (klass);
66 +/* Hildon : The following binding is commented out
67 + * because we want the Escape key to only exit the
68 + * currently opened submenu. Therefore, the handling
69 + * of esc-key will be moved to gtkmenuitem.c */
71 gtk_binding_entry_add_signal (binding_set,
75 gtk_binding_entry_add_signal (binding_set,
77 "activate_current", 1,
79 GDK_F10, GDK_SHIFT_MASK,
81 GTK_TYPE_DIRECTION_TYPE, GTK_DIR_TAB_BACKWARD);
84 + * GtkMenuShell:take-focus:
86 + * A boolean that determines whether the menu and its submenus grab the
87 + * keyboard focus. See gtk_menu_shell_set_take_focus() and
88 + * gtk_menu_shell_get_take_focus().
92 + g_object_class_install_property (object_class,
94 + g_param_spec_boolean ("take-focus",
96 + P_("A boolean that determines whether the menu grabs the keyboard focus"),
98 + G_PARAM_READWRITE));
100 g_type_class_add_private (object_class, sizeof (GtkMenuShellPrivate));
105 priv->mnemonic_hash = NULL;
106 priv->key_hash = NULL;
107 + priv->take_focus = TRUE;
108 + priv->activated_submenu = FALSE;
112 +gtk_menu_shell_set_property (GObject *object,
114 + const GValue *value,
117 + GtkMenuShell *menu_shell = GTK_MENU_SHELL (object);
121 + case PROP_TAKE_FOCUS:
122 + gtk_menu_shell_set_take_focus (menu_shell, g_value_get_boolean (value));
125 + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
131 +gtk_menu_shell_get_property (GObject *object,
136 + GtkMenuShell *menu_shell = GTK_MENU_SHELL (object);
140 + case PROP_TAKE_FOCUS:
141 + g_value_set_boolean (value, gtk_menu_shell_get_take_focus (menu_shell));
144 + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
151 gtk_menu_shell_button_press (GtkWidget *widget,
152 GdkEventButton *event)
154 + GtkMenuShellPrivate *priv;
155 GtkMenuShell *menu_shell;
156 GtkWidget *menu_item;
159 if (event->type != GDK_BUTTON_PRESS)
162 + priv = GTK_MENU_SHELL_GET_PRIVATE (widget);
164 menu_shell = GTK_MENU_SHELL (widget);
165 + menu_item = gtk_menu_shell_get_item (menu_shell, (GdkEvent*) event);
167 + if (menu_shell->active && menu_item &&
168 + (menu_shell->active_menu_item == menu_item) &&
169 + _gtk_menu_item_is_selectable (menu_item) &&
170 + GTK_MENU_ITEM (menu_item)->submenu != NULL &&
171 + !GTK_WIDGET_VISIBLE (GTK_MENU_ITEM (menu_item)->submenu))
173 + /* Hildon : We want to be able to activate submenu items. */
174 + gtk_menu_shell_activate_item (menu_shell, menu_item, FALSE);
176 + priv->activated_submenu = TRUE;
179 if (menu_shell->parent_menu_shell)
181 @@ -491,30 +587,29 @@
183 menu_shell->button = event->button;
185 - menu_item = gtk_menu_shell_get_item (menu_shell, (GdkEvent *)event);
187 if (menu_item && _gtk_menu_item_is_selectable (menu_item))
189 - if ((menu_item->parent == widget) &&
190 - (menu_item != menu_shell->active_menu_item))
192 - if (GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement == GTK_TOP_BOTTOM)
194 - menu_shell->activate_time = event->time;
198 + if ((menu_item->parent == widget) &&
199 + (menu_item != menu_shell->active_menu_item))
201 + if (GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement == GTK_TOP_BOTTOM)
203 + menu_shell->activate_time = event->time;
206 - gtk_menu_shell_select_item (menu_shell, menu_item);
209 + gtk_menu_shell_select_item (menu_shell, menu_item);
215 widget = gtk_get_event_widget ((GdkEvent*) event);
216 if (widget == GTK_WIDGET (menu_shell))
218 - gtk_menu_shell_deactivate (menu_shell);
219 - g_signal_emit (menu_shell, menu_shell_signals[SELECTION_DONE], 0);
222 + gtk_menu_shell_deactivate (menu_shell);
223 + g_signal_emit (menu_shell, menu_shell_signals[SELECTION_DONE], 0);
228 @@ -524,13 +619,20 @@
229 gtk_menu_shell_button_release (GtkWidget *widget,
230 GdkEventButton *event)
232 + GtkMenuShellPrivate *priv;
233 GtkMenuShell *menu_shell;
234 GtkWidget *menu_item;
236 + gboolean activated_submenu;
238 g_return_val_if_fail (GTK_IS_MENU_SHELL (widget), FALSE);
239 g_return_val_if_fail (event != NULL, FALSE);
241 + priv = GTK_MENU_SHELL_GET_PRIVATE (widget);
243 + activated_submenu = priv->activated_submenu;
244 + priv->activated_submenu = FALSE;
246 menu_shell = GTK_MENU_SHELL (widget);
247 if (menu_shell->active)
249 @@ -556,11 +658,11 @@
250 gtk_menu_shell_activate_item (menu_shell, menu_item, TRUE);
253 - else if (GTK_MENU_SHELL_GET_CLASS (menu_shell)->submenu_placement != GTK_TOP_BOTTOM)
255 - gtk_menu_item_select (GTK_MENU_ITEM (menu_item));
258 + else if (!activated_submenu)
260 + /* popdown the submenu if we didn't pop it up in this click */
261 + _gtk_menu_item_popdown_submenu (menu_item);
264 else if (menu_item &&
265 !_gtk_menu_item_is_selectable (menu_item) &&
266 @@ -630,12 +732,14 @@
267 gtk_menu_shell_enter_notify (GtkWidget *widget,
268 GdkEventCrossing *event)
270 + GtkMenuShellPrivate *priv;
271 GtkMenuShell *menu_shell;
272 GtkWidget *menu_item;
274 g_return_val_if_fail (GTK_IS_MENU_SHELL (widget), FALSE);
275 g_return_val_if_fail (event != NULL, FALSE);
277 + priv = GTK_MENU_SHELL_GET_PRIVATE (widget);
278 menu_shell = GTK_MENU_SHELL (widget);
280 if (menu_shell->active)
282 (GTK_WIDGET_STATE (menu_item) != GTK_STATE_PRELIGHT))
284 gtk_menu_shell_select_item (menu_shell, menu_item);
286 + /* If the pen is down, and there is a submenu that is not
287 + * yet visible, activate it */
288 + if ((event->state & GDK_BUTTON1_MASK) &&
289 + GTK_MENU_ITEM (menu_item)->submenu != NULL &&
290 + !GTK_WIDGET_VISIBLE (GTK_MENU_ITEM (menu_item)->submenu))
292 + gtk_menu_shell_activate_item (menu_shell, menu_item, FALSE);
294 + priv->activated_submenu = TRUE;
298 else if (menu_shell->parent_menu_shell)
299 @@ -887,8 +1002,14 @@
300 /* This allows the bizarre radio buttons-with-submenus-display-history
303 + /* Hildon modification. We probably won't have those
304 + * bizarre radio buttons-with-submenus so we don't
305 + * need this. Also, this functionality interferes with
306 + * other functionality. */
308 if (GTK_MENU_ITEM (menu_shell->active_menu_item)->submenu)
309 gtk_widget_activate (menu_shell->active_menu_item);
316 g_object_ref (menu_shell);
319 + /* We don't want to deactivate if we're activating
320 + * a submenu item. */
321 + if ((deactivate) && (GTK_MENU_ITEM (menu_item)->submenu == NULL))
323 GtkMenuShell *parent_menu_shell = menu_shell;
325 @@ -965,29 +1088,30 @@
329 + /*Hildon: selection no longer wraps around at the
330 + *bottom of the menu*/
333 - while (node != start_node &&
334 - (!node || !_gtk_menu_item_is_selectable (node->data)))
335 + while (node && node != start_node &&
336 + !_gtk_menu_item_is_selectable (node->data))
339 - node = menu_shell->children;
346 + /*Hildon: selection no longer wraps around at the top
350 - while (node != start_node &&
351 - (!node || !_gtk_menu_item_is_selectable (node->data)))
352 + while (node && node != start_node &&
353 + !_gtk_menu_item_is_selectable (node->data))
356 - node = g_list_last (menu_shell->children);
362 + /*note: gtk_menu_shell_select_item won't select non-selectable items*/
364 gtk_menu_shell_select_item (menu_shell, node->data);
366 @@ -1119,6 +1243,16 @@
369 case GTK_MENU_DIR_PARENT:
371 + if(!parent_menu_shell || GTK_IS_MENU_BAR(parent_menu_shell))
374 + /* hildon-modification - menu should be closed when returning from submenu.
375 + * WARNING: This function is from GtkMenu, which normally
376 + * shouldn't be called from GtkMenuShell, but currently
377 + * there are no better alternatives. */
378 + gtk_menu_popdown (GTK_MENU (menu_shell));
380 if (parent_menu_shell)
382 if (GTK_MENU_SHELL_GET_CLASS (parent_menu_shell)->submenu_placement ==
383 @@ -1151,10 +1285,14 @@
384 _gtk_menu_item_is_selectable (menu_shell->active_menu_item) &&
385 GTK_MENU_ITEM (menu_shell->active_menu_item)->submenu)
387 + /* Hildon-modification -- submenu is not opened automatically but needs to be explicitly opened*/
388 + g_signal_emit (G_OBJECT (menu_shell), menu_shell_signals[ACTIVATE_CURRENT], 0, (gint) FALSE);
390 if (gtk_menu_shell_select_submenu_first (menu_shell))
395 /* Try to find a menu running the opposite direction */
396 while (parent_menu_shell &&
397 (GTK_MENU_SHELL_GET_CLASS (parent_menu_shell)->submenu_placement ==
398 @@ -1173,6 +1311,7 @@
399 gtk_menu_shell_move_selected (parent_menu_shell, 1);
400 gtk_menu_shell_select_submenu_first (parent_menu_shell);
405 case GTK_MENU_DIR_PREV:
406 @@ -1197,8 +1336,8 @@
407 gtk_real_menu_shell_activate_current (GtkMenuShell *menu_shell,
410 - if (menu_shell->active_menu_item &&
411 - _gtk_menu_item_is_selectable (menu_shell->active_menu_item))
412 + if (menu_shell->active_menu_item)/* &&
413 + _gtk_menu_item_is_selectable (menu_shell->active_menu_item)) */
416 if (GTK_MENU_ITEM (menu_shell->active_menu_item)->submenu == NULL)
417 @@ -1390,4 +1529,73 @@
419 gtk_menu_shell_reset_key_hash (menu_shell);
422 + * gtk_menu_shell_get_take_focus:
423 + * @menu: a #GtkMenuShell
425 + * @returns: %TRUE if the menu_shell will take the keyboard focus on popup.
430 +gtk_menu_shell_get_take_focus (GtkMenuShell *menu_shell)
432 + GtkMenuShellPrivate *priv;
434 + g_return_val_if_fail (GTK_IS_MENU_SHELL (menu_shell), FALSE);
436 + priv = GTK_MENU_SHELL_GET_PRIVATE (menu_shell);
438 + return priv->take_focus;
442 + * gtk_menu_shell_set_take_focus:
443 + * @menu: a #GtkMenuShell
444 + * @take_focus: %TRUE if the menu_shell should take the keyboard focus on popup.
446 + * If @take_focus is %TRUE (the default) the menu will take the keyboard focus
447 + * so that it will receive all keyboard events which is needed to enable
448 + * keyboard navigation in menus.
450 + * Setting @take_focus to %FALSE is useful only for special applications
451 + * like virtual keyboard implementations which should not take keyboard
454 + * The @take_focus state of a menu or menu bar is automatically propagated
455 + * to submenus whenever a submenu is popped up, so you don't have to worry
456 + * about recursively setting it for your entire menu hierarchy. Only when
457 + * programmatically picking a submenu and popping it up manually, the
458 + * @take_focus property of the submenu needs to be set explicitely.
460 + * Note that setting it to %FALSE has side-effects:
462 + * If the focus is in some other app, it keeps the focus and keynav in
463 + * the menu doesn't work. Consequently, keynav on the menu will only
464 + * work if the focus is on some toplevel owned by the onscreen keyboard.
466 + * To avoid confusing the user, menus with @take_focus set to %FALSE
467 + * should not display mnemonics or accelerators, since it cannot be
468 + * guaranteed that they will work.
470 + * See also gdk_keyboard_grab()
475 +gtk_menu_shell_set_take_focus (GtkMenuShell *menu_shell,
476 + gboolean take_focus)
478 + GtkMenuShellPrivate *priv;
480 + g_return_if_fail (GTK_IS_MENU_SHELL (menu_shell));
482 + priv = GTK_MENU_SHELL_GET_PRIVATE (menu_shell);
484 + if (priv->take_focus != take_focus)
486 + priv->take_focus = take_focus;
487 + g_object_notify (G_OBJECT (menu_shell), "take-focus");