1 --- gtk+-2.6.4/gtk/gtkmenu.c 2005-03-01 08:28:56.000000000 +0200
2 +++ gtk+-2.6.4/gtk/gtkmenu.c 2005-04-06 16:19:36.921925376 +0300
4 * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
7 +/* Modified for Nokia Oyj during 2002-2005. See CHANGES file for list
11 #define GTK_MENU_INTERNALS
14 #include <string.h> /* memset */
17 #include "gdk/gdkkeysyms.h"
19 #include "gtkaccellabel.h"
21 #include "gtkvscrollbar.h"
22 #include "gtksettings.h"
24 +#include "gtkcombobox.h"
26 +/* Hildon : We need this to figure out if menu should have
28 +#include "gtkmenubar.h"
30 #define MENU_ITEM_CLASS(w) GTK_MENU_ITEM_GET_CLASS (w)
33 * extends below the submenu
37 + * Urgh, nasty thing to hard-code things like these :p
38 + * One should really do some rewriting here...
41 #define MENU_SCROLL_STEP1 8
42 #define MENU_SCROLL_STEP2 15
43 -#define MENU_SCROLL_ARROW_HEIGHT 16
44 -#define MENU_SCROLL_FAST_ZONE 8
45 +#define MENU_SCROLL_ARROW_HEIGHT 20 /* This used to be: 23; This hard-coding should be
46 + * changed. Add arrow_height style property into
47 + * commongtkrc and read it from there everywhere
48 + * where a reference to MENU_SCROLL_ARROW_HEIGHT
50 + * If these changes are made, please modify also
53 +#define MENU_SCROLL_FAST_ZONE MENU_SCROLL_ARROW_HEIGHT /* Was originally 8 */
54 #define MENU_SCROLL_TIMEOUT1 50
55 #define MENU_SCROLL_TIMEOUT2 20
57 #define ATTACH_INFO_KEY "gtk-menu-child-attach-info-key"
58 #define ATTACHED_MENUS "gtk-attached-menus"
61 +#define HILDON_MENU_NAME_SHARP "menu_with_corners"
63 +/* needed to allow different themeing for first level menus */
64 +#define HILDON_MENU_NAME_ROUND_FIRST_LEVEL "menu_without_corners_first_level"
65 +#define HILDON_MENU_NAME_ROUND "menu_without_corners"
66 +#define HILDON_MENU_NAME_FORCE_SHARP "menu_force_with_corners"
67 +#define HILDON_MENU_NAME_FORCE_ROUND "menu_force_without_corners"
69 +/* maximum sizes for menus when attached to comboboxes */
70 +#define HILDON_MENU_COMBO_MAX_WIDTH 406
71 +#define HILDON_MENU_COMBO_MIN_WIDTH 66
72 +#define HILDON_MENU_COMBO_MAX_HEIGHT 305
73 +#define HILDON_MENU_COMBO_MIN_HEIGHT 70
75 typedef struct _GtkMenuAttachData GtkMenuAttachData;
76 typedef struct _GtkMenuPrivate GtkMenuPrivate;
84 + GtkStateType lower_arrow_state;
85 + GtkStateType upper_arrow_state;
87 + /* For context menu behavior */
88 + gboolean context_menu;
89 + int popup_pointer_x;
90 + int popup_pointer_y;
103 static void gtk_menu_handle_scrolling (GtkMenu *menu,
109 static void gtk_menu_set_tearoff_hints (GtkMenu *menu,
111 static void gtk_menu_style_set (GtkWidget *widget,
114 static void _gtk_menu_refresh_accel_paths (GtkMenu *menu,
115 gboolean group_changed);
116 +static gboolean gtk_menu_check_name (GtkWidget *widget);
118 +static void _gtk_menu_close_current (GtkMenu *menu);
120 static GtkMenuShellClass *parent_class = NULL;
121 static const gchar attach_data_key[] = "gtk-menu-attach-data";
123 widget_class->hide_all = gtk_menu_hide_all;
124 widget_class->enter_notify_event = gtk_menu_enter_notify;
125 widget_class->leave_notify_event = gtk_menu_leave_notify;
126 - widget_class->motion_notify_event = gtk_menu_motion_notify;
127 widget_class->style_set = gtk_menu_style_set;
128 widget_class->focus = gtk_menu_focus;
129 widget_class->can_activate_accel = gtk_menu_real_can_activate_accel;
131 _gtk_marshal_VOID__ENUM,
133 GTK_TYPE_SCROLL_TYPE);
135 + menu_signals[CLOSE_CURRENT] =
136 + _gtk_binding_signal_new ("close_current",
137 + G_OBJECT_CLASS_TYPE (object_class),
138 + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
139 + G_CALLBACK (_gtk_menu_close_current),
141 + _gtk_marshal_VOID__VOID,
144 g_object_class_install_property (gobject_class,
149 binding_set = gtk_binding_set_by_class (class);
150 + /* Hildon : We moved handling of escape-key here because we need it to
151 + * work like closing a submenu, not closing all the menus. */
152 + gtk_binding_entry_add_signal (binding_set,
154 + "close_current", 0);
155 gtk_binding_entry_add_signal (binding_set,
159 DEFAULT_POPDOWN_DELAY,
162 + /* Hildon addition : border width was
163 + replaced with horizontal-padding and
164 + vertical-padding (which already is an style
165 + property for GtkMenu). */
166 + gtk_widget_class_install_style_property (widget_class,
167 + g_param_spec_int ("horizontal-padding",
168 + P_("Horizontal Padding"),
169 + P_("Extra space at the left and right edges of the menu"),
173 + G_PARAM_READABLE));
175 + gtk_widget_class_install_style_property (widget_class,
176 + g_param_spec_boolean ("double_arrows",
177 + P_("Double Arrows"),
178 + P_("When scrolling, always show both arrows."),
180 + G_PARAM_READABLE));
184 @@ -884,13 +967,14 @@
185 menu->toggle_size = 0;
187 menu->toplevel = g_object_connect (g_object_new (GTK_TYPE_WINDOW,
188 - "type", GTK_WINDOW_POPUP,
191 + "type", GTK_WINDOW_POPUP,
194 "signal::event", gtk_menu_window_event, menu,
195 "signal::size_request", gtk_menu_window_size_request, menu,
196 "signal::destroy", gtk_widget_destroyed, &menu->toplevel,
199 gtk_window_set_resizable (GTK_WINDOW (menu->toplevel), FALSE);
200 gtk_window_set_mnemonic_modifier (GTK_WINDOW (menu->toplevel), 0);
202 @@ -919,6 +1003,15 @@
203 menu->lower_arrow_visible = FALSE;
204 menu->upper_arrow_prelight = FALSE;
205 menu->lower_arrow_prelight = FALSE;
208 + priv->upper_arrow_state = GTK_STATE_NORMAL;
209 + priv->lower_arrow_state = GTK_STATE_NORMAL;
211 + priv->context_menu = FALSE;
212 + priv->popup_pointer_x = -1;
213 + priv->popup_pointer_y = -1;
216 priv->have_layout = FALSE;
218 @@ -1220,7 +1313,8 @@
221 popup_grab_on_window (GdkWindow *window,
222 - guint32 activate_time)
223 + guint32 activate_time,
224 + gboolean grab_keyboard)
226 if ((gdk_pointer_grab (window, TRUE,
227 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
228 @@ -1228,7 +1322,8 @@
229 GDK_POINTER_MOTION_MASK,
230 NULL, NULL, activate_time) == 0))
232 - if (gdk_keyboard_grab (window, TRUE,
233 + if (!grab_keyboard ||
234 + gdk_keyboard_grab (window, TRUE,
238 @@ -1282,6 +1377,7 @@
240 GdkEvent *current_event;
241 GtkMenuShell *menu_shell;
242 + gboolean grab_keyboard;
243 GtkMenuPrivate *priv = gtk_menu_get_private (menu);
245 g_return_if_fail (GTK_IS_MENU (menu));
246 @@ -1333,10 +1429,28 @@
247 * probably could just leave the grab on the other window, with a
248 * little reorganization of the code in gtkmenu*).
251 + grab_keyboard = gtk_menu_shell_get_take_focus (menu_shell);
252 + gtk_window_set_accept_focus (GTK_WINDOW (menu->toplevel), grab_keyboard);
254 if (xgrab_shell && xgrab_shell != widget)
256 - if (popup_grab_on_window (xgrab_shell->window, activate_time))
257 + if (popup_grab_on_window (xgrab_shell->window, activate_time, grab_keyboard))
258 GTK_MENU_SHELL (xgrab_shell)->have_xgrab = TRUE;
261 + * Check wheter parent is GtkMenuBar. If so,
262 + * then we need sharp upper corners for this menu.
264 + if (gtk_menu_check_name (widget))
266 + if (GTK_IS_MENU_BAR (parent_menu_shell))
267 + gtk_widget_set_name (widget, HILDON_MENU_NAME_SHARP);
268 + else if (GTK_IS_MENU (parent_menu_shell))
269 + gtk_widget_set_name( widget, HILDON_MENU_NAME_ROUND);
271 + gtk_widget_set_name (widget, HILDON_MENU_NAME_ROUND_FIRST_LEVEL);
276 @@ -1344,8 +1458,14 @@
278 xgrab_shell = widget;
279 transfer_window = menu_grab_transfer_window_get (menu);
280 - if (popup_grab_on_window (transfer_window, activate_time))
281 + if (popup_grab_on_window (transfer_window, activate_time, grab_keyboard))
282 GTK_MENU_SHELL (xgrab_shell)->have_xgrab = TRUE;
285 + * We want this menu to have round corners (Used by default)
287 + if (gtk_menu_check_name (widget))
288 + gtk_widget_set_name (widget, HILDON_MENU_NAME_ROUND_FIRST_LEVEL);
291 if (!GTK_MENU_SHELL (xgrab_shell)->have_xgrab)
292 @@ -1409,6 +1529,23 @@
294 /* Position the menu, possibly changing the size request
296 + if (GTK_IS_COMBO_BOX (gtk_menu_get_attach_widget (menu)))
298 + /* Hildon - limit the size if the menu is attached to a ComboBox */
299 + GtkRequisition req;
300 + gint width, height;
302 + gtk_widget_set_size_request (widget, -1, -1);
303 + gtk_widget_size_request (widget, &req);
305 + width = MAX (MIN (req.width, HILDON_MENU_COMBO_MAX_WIDTH),
306 + HILDON_MENU_COMBO_MIN_WIDTH);
307 + height = MAX (MIN (req.height, HILDON_MENU_COMBO_MAX_HEIGHT),
308 + HILDON_MENU_COMBO_MIN_HEIGHT);
310 + gtk_widget_set_size_request (widget, width, height);
313 gtk_menu_position (menu);
315 /* Compute the size of the toplevel and realize it so we
316 @@ -1430,13 +1567,29 @@
318 gtk_menu_scroll_to (menu, menu->scroll_offset);
320 + if (priv->context_menu)
322 + /* Save position of the pointer during popup */
323 + /* currently not-multihead safe */
325 + GdkDisplay *display;
327 + screen = gtk_widget_get_screen (widget);
328 + display = gdk_screen_get_display (screen);
330 + gdk_display_get_pointer (display, NULL,
331 + &priv->popup_pointer_x,
332 + &priv->popup_pointer_y,
336 /* Once everything is set up correctly, map the toplevel window on
339 gtk_widget_show (menu->toplevel);
341 if (xgrab_shell == widget)
342 - popup_grab_on_window (widget->window, activate_time); /* Should always succeed */
343 + popup_grab_on_window (widget->window, activate_time, grab_keyboard); /* Should always succeed */
344 gtk_grab_add (GTK_WIDGET (menu));
347 @@ -1996,6 +2149,7 @@
350 guint vertical_padding;
351 + guint horizontal_padding;
353 g_return_if_fail (GTK_IS_MENU (widget));
355 @@ -2025,9 +2179,10 @@
357 gtk_widget_style_get (GTK_WIDGET (menu),
358 "vertical-padding", &vertical_padding,
359 + "horizontal-padding", &horizontal_padding,
362 - attributes.x = border_width + widget->style->xthickness;
363 + attributes.x = border_width + widget->style->xthickness + horizontal_padding;
364 attributes.y = border_width + widget->style->ythickness + vertical_padding;
365 attributes.width = MAX (1, widget->allocation.width - attributes.x * 2);
366 attributes.height = MAX (1, widget->allocation.height - attributes.y * 2);
367 @@ -2040,11 +2195,14 @@
368 if (menu->lower_arrow_visible)
369 attributes.height -= MENU_SCROLL_ARROW_HEIGHT;
371 + attributes.window_type = GDK_WINDOW_CHILD;
373 menu->view_window = gdk_window_new (widget->window, &attributes, attributes_mask);
374 gdk_window_set_user_data (menu->view_window, menu);
378 + attributes.width = MAX (1, widget->requisition.width - (border_width + widget->style->xthickness + horizontal_padding) * 2);
379 attributes.height = MAX (1, widget->requisition.height - (border_width + widget->style->ythickness + vertical_padding) * 2);
381 menu->bin_window = gdk_window_new (menu->view_window, &attributes, attributes_mask);
382 @@ -2164,6 +2322,10 @@
383 guint vertical_padding;
384 GtkRequisition child_requisition;
385 GtkMenuPrivate *priv;
386 + guint horizontal_padding;
388 + GdkRectangle monitor;
391 g_return_if_fail (GTK_IS_MENU (widget));
392 g_return_if_fail (requisition != NULL);
393 @@ -2182,6 +2344,16 @@
394 priv->heights = g_new0 (guint, gtk_menu_get_n_rows (menu));
395 priv->heights_length = gtk_menu_get_n_rows (menu);
397 +/* Hildon addition to find out the monitor width */
398 + screen = gtk_widget_get_screen (widget);
399 + if (widget->window != NULL)
400 + monitor_num = gdk_screen_get_monitor_at_window (screen, widget->window);
403 + if (monitor_num < 0)
405 + gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor);
407 children = menu_shell->children;
410 @@ -2223,15 +2395,18 @@
412 requisition->width += max_toggle_size + max_accel_width;
413 requisition->width *= gtk_menu_get_n_columns (menu);
414 - requisition->width += (GTK_CONTAINER (menu)->border_width +
415 - widget->style->xthickness) * 2;
417 gtk_widget_style_get (GTK_WIDGET (menu),
418 + "horizontal-padding", &horizontal_padding,
419 "vertical-padding", &vertical_padding,
421 + requisition->width += (GTK_CONTAINER (menu)->border_width + horizontal_padding +
422 + widget->style->xthickness) * 2;
423 requisition->height += (GTK_CONTAINER (menu)->border_width + vertical_padding +
424 widget->style->ythickness) * 2;
426 +/* Hildon addition to not make the menu too wide for the screen. */
427 + requisition->width = MIN (requisition->width, monitor.width);
428 menu->toggle_size = max_toggle_size;
430 /* Don't resize the tearoff if it is not active, because it won't redraw (it is only a background pixmap).
431 @@ -2253,6 +2428,7 @@
435 + guint horizontal_padding;
436 guint vertical_padding;
438 g_return_if_fail (GTK_IS_MENU (widget));
439 @@ -2266,10 +2442,11 @@
440 gtk_widget_get_child_requisition (GTK_WIDGET (menu), &child_requisition);
442 gtk_widget_style_get (GTK_WIDGET (menu),
443 + "horizontal-padding", &horizontal_padding,
444 "vertical-padding", &vertical_padding,
447 - x = GTK_CONTAINER (menu)->border_width + widget->style->xthickness;
448 + x = GTK_CONTAINER (menu)->border_width + widget->style->xthickness + horizontal_padding;
449 y = GTK_CONTAINER (menu)->border_width + widget->style->ythickness + vertical_padding;
451 width = MAX (1, allocation->width - x * 2);
452 @@ -2407,27 +2584,32 @@
453 GdkEventExpose *event)
456 - gint width, height;
457 - gint border_x, border_y;
458 - guint vertical_padding;
460 g_return_if_fail (GTK_IS_MENU (widget));
462 menu = GTK_MENU (widget);
464 - gtk_widget_style_get (GTK_WIDGET (menu),
465 - "vertical-padding", &vertical_padding,
468 - border_x = GTK_CONTAINER (widget)->border_width + widget->style->xthickness;
469 - border_y = GTK_CONTAINER (widget)->border_width + widget->style->ythickness + vertical_padding;
470 - gdk_drawable_get_size (widget->window, &width, &height);
472 if (event->window == widget->window)
474 + gint width, height;
475 + gint border_x, border_y;
476 + guint vertical_padding;
477 + guint horizontal_padding;
478 + GtkMenuPrivate *priv;
479 gint arrow_space = MENU_SCROLL_ARROW_HEIGHT - 2 * widget->style->ythickness;
480 gint arrow_size = 0.7 * arrow_space;
482 + priv = gtk_menu_get_private (menu);
484 + gtk_widget_style_get (GTK_WIDGET (menu),
485 + "vertical-padding", &vertical_padding,
486 + "horizontal-padding", &horizontal_padding,
489 + border_x = GTK_CONTAINER (widget)->border_width + widget->style->xthickness + horizontal_padding;
490 + border_y = GTK_CONTAINER (widget)->border_width + widget->style->ythickness + vertical_padding;
491 + gdk_drawable_get_size (widget->window, &width, &height);
493 gtk_paint_box (widget->style,
496 @@ -2436,21 +2618,9 @@
498 if (menu->upper_arrow_visible && !menu->tearoff_active)
500 - gtk_paint_box (widget->style,
502 - menu->upper_arrow_prelight ?
503 - GTK_STATE_PRELIGHT : GTK_STATE_NORMAL,
505 - NULL, widget, "menu",
508 - width - 2 * border_x,
509 - MENU_SCROLL_ARROW_HEIGHT);
511 gtk_paint_arrow (widget->style,
513 - menu->upper_arrow_prelight ?
514 - GTK_STATE_PRELIGHT : GTK_STATE_NORMAL,
515 + priv->upper_arrow_state,
517 NULL, widget, "menu_scroll_arrow_up",
519 @@ -2462,21 +2632,9 @@
521 if (menu->lower_arrow_visible && !menu->tearoff_active)
523 - gtk_paint_box (widget->style,
525 - menu->lower_arrow_prelight ?
526 - GTK_STATE_PRELIGHT : GTK_STATE_NORMAL,
528 - NULL, widget, "menu",
530 - height - border_y - MENU_SCROLL_ARROW_HEIGHT,
531 - width - 2*border_x,
532 - MENU_SCROLL_ARROW_HEIGHT);
534 gtk_paint_arrow (widget->style,
536 - menu->lower_arrow_prelight ?
537 - GTK_STATE_PRELIGHT : GTK_STATE_NORMAL,
538 + priv->lower_arrow_state,
540 NULL, widget, "menu_scroll_arrow_down",
542 @@ -2516,18 +2674,82 @@
543 GTK_WIDGET_CLASS (parent_class)->show (widget);
547 +find_active_menu_item (GdkEventButton *event)
549 + GtkWidget *menu_item;
551 + menu_item = gtk_get_event_widget ((GdkEvent*) event);
552 + while (menu_item && !GTK_IS_MENU_ITEM (menu_item))
553 + menu_item = menu_item->parent;
559 +pointer_in_menu_tree (GtkWidget *widget)
561 + GtkMenuShell *mshell;
562 + int width, height, x, y;
564 + mshell = GTK_MENU_SHELL (widget);
566 + gdk_window_get_pointer (widget->window, &x, &y, NULL);
567 + gdk_drawable_get_size (widget->window, &width, &height);
569 + if ((x <= width) && (x >= 0) && (y <= height) && (y >= 0))
572 + if ((mshell->parent_menu_shell != NULL) &&
573 + GTK_IS_MENU (mshell->parent_menu_shell))
574 + return pointer_in_menu_tree (mshell->parent_menu_shell);
580 +distance_traveled (GtkWidget *widget)
582 + GtkMenuPrivate *priv;
584 + GdkDisplay *display;
587 + priv = gtk_menu_get_private (GTK_MENU (widget));
589 + screen = gtk_widget_get_screen (widget);
590 + display = gdk_screen_get_display (screen);
592 + gdk_display_get_pointer (display, NULL, &x, &y, NULL);
594 + dx = (priv->popup_pointer_x - x);
595 + dy = (priv->popup_pointer_y - y);
597 + return abs ((int) sqrt ((double) (dx * dx + dy * dy)));
601 gtk_menu_button_press (GtkWidget *widget,
602 - GdkEventButton *event)
603 + GdkEventButton *event)
605 - /* Don't pop down the menu for releases over scroll arrows
607 - if (GTK_IS_MENU (widget))
608 + GtkWidget *menu_item;
610 + menu_item = find_active_menu_item (event);
611 + if (menu_item == NULL)
613 GtkMenu *menu = GTK_MENU (widget);
615 - if (menu->upper_arrow_prelight || menu->lower_arrow_prelight)
617 + if (menu->upper_arrow_prelight || menu->lower_arrow_prelight)
619 + gtk_menu_handle_scrolling (GTK_MENU (widget), event->x_root, event->y_root, TRUE, FALSE);
624 + /* Don't pass down to menu shell if a non-menuitem part
625 + * of the menu was clicked. */
626 + if (pointer_in_menu_tree (widget))
630 return GTK_WIDGET_CLASS (parent_class)->button_press_event (widget, event);
631 @@ -2537,14 +2759,44 @@
632 gtk_menu_button_release (GtkWidget *widget,
633 GdkEventButton *event)
635 - /* Don't pop down the menu for releases over scroll arrows
637 - if (GTK_IS_MENU (widget))
638 + GtkMenuPrivate *priv;
639 + GtkWidget *menu_item;
641 + priv = gtk_menu_get_private (GTK_MENU (widget));
643 + menu_item = find_active_menu_item (event);
644 + if (menu_item == NULL)
646 GtkMenu *menu = GTK_MENU (widget);
648 - if (menu->upper_arrow_prelight || menu->lower_arrow_prelight)
650 + if (menu->upper_arrow_prelight || menu->lower_arrow_prelight)
652 + gtk_menu_handle_scrolling (GTK_MENU (widget), event->x_root, event->y_root, FALSE, FALSE);
657 + if (priv->context_menu &&
658 + (priv->popup_pointer_x >= 0) &&
659 + (priv->popup_pointer_y >= 0))
663 + distance = distance_traveled (widget);
665 + priv->popup_pointer_x = -1;
666 + priv->popup_pointer_y = -1;
668 + /* Don't popdown if we traveled less than 20px since popup point,
669 + * as per the specs. */
674 + /* Don't pass down to menu shell if a non-menuitem part
675 + * of the menu was clicked. */
676 + if (pointer_in_menu_tree (widget))
680 return GTK_WIDGET_CLASS (parent_class)->button_release_event (widget, event);
681 @@ -2765,7 +3017,7 @@
684 if (GTK_IS_MENU (widget))
685 - gtk_menu_handle_scrolling (GTK_MENU (widget), event->x_root, event->y_root, TRUE);
686 + gtk_menu_handle_scrolling (GTK_MENU (widget), event->x_root, event->y_root, TRUE, TRUE);
688 /* We received the event for one of two reasons:
690 @@ -2779,7 +3031,27 @@
691 menu_item = gtk_get_event_widget ((GdkEvent*) event);
692 if (!menu_item || !GTK_IS_MENU_ITEM (menu_item) ||
693 !GTK_IS_MENU (menu_item->parent))
696 + GtkMenuPrivate *priv;
698 + priv = gtk_menu_get_private (GTK_MENU (widget));
700 + if (priv->context_menu)
702 + /* Context menu mode. If we dragged out of the menu,
703 + * close the menu, as by the specs. */
704 + if (!pointer_in_menu_tree (widget) &&
705 + (distance_traveled (widget) >= 20) &&
706 + (event->state & GDK_BUTTON1_MASK))
708 + gtk_menu_deactivate (GTK_MENU_SHELL (widget));
717 menu_shell = GTK_MENU_SHELL (menu_item->parent);
718 menu = GTK_MENU (menu_shell);
719 @@ -2795,6 +3067,11 @@
721 if (gtk_menu_navigating_submenu (menu, event->x_root, event->y_root))
724 + * Close the submenus that are two levels down from the currently selected.
725 + * This ensures that the focus is correct all the time.*/
726 + if (GTK_MENU_ITEM(menu_item)->submenu != NULL)
727 + gtk_menu_shell_deselect (GTK_MENU_SHELL(&(GTK_MENU(GTK_MENU_ITEM(menu_item)->submenu)->menu_shell)));
729 /* Make sure we pop down if we enter a non-selectable menu item, so we
730 * don't show a submenu when the cursor is outside the stay-up triangle.
731 @@ -2828,6 +3105,7 @@
732 send_event->crossing.y_root = event->y_root;
733 send_event->crossing.x = event->x;
734 send_event->crossing.y = event->y;
735 + send_event->crossing.state = event->state;
737 /* We send the event to 'widget', the currently active menu,
738 * instead of 'menu', the menu that the pointer is in. This
739 @@ -2852,17 +3130,24 @@
742 gint view_width, view_height;
743 + gboolean double_arrows;
745 widget = GTK_WIDGET (menu);
746 offset = menu->scroll_offset + step;
748 + /* get double_arrows style property */
749 + gtk_widget_style_get (widget,
750 + "double_arrows", &double_arrows,
753 /* If we scroll upward and the non-visible top part
754 * is smaller than the scroll arrow it would be
755 * pretty stupid to show the arrow and taking more
756 * screen space than just scrolling to the top.
758 - if ((step < 0) && (offset < MENU_SCROLL_ARROW_HEIGHT))
760 + if (!double_arrows)
761 + if ((step < 0) && (offset < MENU_SCROLL_ARROW_HEIGHT))
764 /* Don't scroll over the top if we weren't before: */
765 if ((menu->scroll_offset >= 0) && (offset < 0))
766 @@ -2874,6 +3159,12 @@
767 if (menu->scroll_offset > 0)
768 view_height -= MENU_SCROLL_ARROW_HEIGHT;
770 + /* When both arrows are always shown, reduce
771 + * view height even more.
774 + view_height -= MENU_SCROLL_ARROW_HEIGHT;
776 if ((menu->scroll_offset + view_height <= widget->requisition.height) &&
777 (offset + view_height > widget->requisition.height))
778 offset = widget->requisition.height - view_height;
779 @@ -2922,18 +3213,21 @@
780 gtk_menu_handle_scrolling (GtkMenu *menu,
787 GtkMenuShell *menu_shell;
788 + GtkMenuPrivate *priv;
793 gboolean scroll_fast = FALSE;
794 guint vertical_padding;
798 + priv = gtk_menu_get_private (menu);
800 menu_shell = GTK_MENU_SHELL (menu);
802 gdk_drawable_get_size (GTK_WIDGET (menu)->window, &width, &height);
803 @@ -2946,10 +3240,11 @@
804 GTK_WIDGET (menu)->style->ythickness + vertical_padding;
806 gdk_window_get_position (menu->toplevel->window, &top_x, &top_y);
810 gdk_window_get_position (GTK_WIDGET (menu)->window, &win_x, &win_y);
815 if (menu->upper_arrow_visible && !menu->tearoff_active)
818 @@ -2957,35 +3252,49 @@
820 rect.height = MENU_SCROLL_ARROW_HEIGHT + border;
823 + menu->upper_arrow_prelight = FALSE;
824 if ((x >= rect.x) && (x < rect.x + rect.width) &&
825 (y >= rect.y) && (y < rect.y + rect.height))
828 - scroll_fast = (y < rect.y + MENU_SCROLL_FAST_ZONE);
831 - if (enter && in_arrow &&
832 - (!menu->upper_arrow_prelight || menu->scroll_fast != scroll_fast))
834 - menu->upper_arrow_prelight = TRUE;
835 - menu->scroll_fast = scroll_fast;
836 - gdk_window_invalidate_rect (GTK_WIDGET (menu)->window, &rect, FALSE);
838 - /* Deselect the active item so that any submenus are poped down */
839 - gtk_menu_shell_deselect (menu_shell);
840 + menu->upper_arrow_prelight = TRUE;
842 - gtk_menu_remove_scroll_timeout (menu);
843 - menu->scroll_step = (scroll_fast) ? -MENU_SCROLL_STEP2 : -MENU_SCROLL_STEP1;
844 - menu->timeout_id = g_timeout_add ((scroll_fast) ? MENU_SCROLL_TIMEOUT2 : MENU_SCROLL_TIMEOUT1,
845 - gtk_menu_scroll_timeout,
848 - else if (!enter && !in_arrow && menu->upper_arrow_prelight)
849 + if (priv->upper_arrow_state != GTK_STATE_INSENSITIVE)
851 - gdk_window_invalidate_rect (GTK_WIDGET (menu)->window, &rect, FALSE);
853 - gtk_menu_stop_scrolling (menu);
854 + if (enter && menu->upper_arrow_prelight &&
855 + (menu->timeout_id == 0 || menu->scroll_fast != scroll_fast))
857 + menu->scroll_fast = scroll_fast;
859 + /* Deselect the active item so that any submenus are poped down */
860 + gtk_menu_shell_deselect (menu_shell);
862 + gtk_menu_remove_scroll_timeout (menu);
863 + menu->scroll_step = (scroll_fast) ? -MENU_SCROLL_STEP2 : -MENU_SCROLL_STEP1;
867 + /* Only do stuff on click. */
868 + GtkSettings *settings;
871 + settings = gtk_settings_get_default ();
872 + g_object_get (settings, "gtk-update-timeout", &timeout, NULL);
874 + menu->timeout_id = g_timeout_add (timeout / 2, gtk_menu_scroll_timeout, menu);
876 + priv->upper_arrow_state = GTK_STATE_ACTIVE;
879 + gdk_window_invalidate_rect (GTK_WIDGET (menu)->window, &rect, FALSE);
883 + gtk_menu_stop_scrolling (menu);
885 + priv->upper_arrow_state = menu->upper_arrow_prelight ?
886 + GTK_STATE_PRELIGHT : GTK_STATE_NORMAL;
888 + gdk_window_invalidate_rect (GTK_WIDGET (menu)->window, &rect, FALSE);
893 @@ -2996,36 +3305,50 @@
895 rect.height = MENU_SCROLL_ARROW_HEIGHT + border;
898 + menu->lower_arrow_prelight = FALSE;
899 if ((x >= rect.x) && (x < rect.x + rect.width) &&
900 (y >= rect.y) && (y < rect.y + rect.height))
903 - scroll_fast = (y > rect.y + rect.height - MENU_SCROLL_FAST_ZONE);
905 + menu->lower_arrow_prelight = TRUE;
907 - if (enter && in_arrow &&
908 - (!menu->lower_arrow_prelight || menu->scroll_fast != scroll_fast))
909 + if (priv->lower_arrow_state != GTK_STATE_INSENSITIVE)
911 - menu->lower_arrow_prelight = TRUE;
912 - menu->scroll_fast = scroll_fast;
913 - gdk_window_invalidate_rect (GTK_WIDGET (menu)->window, &rect, FALSE);
914 + if (enter && menu->lower_arrow_prelight &&
915 + (menu->timeout_id == 0 || menu->scroll_fast != scroll_fast))
917 + menu->scroll_fast = scroll_fast;
919 - /* Deselect the active item so that any submenus are poped down */
920 - gtk_menu_shell_deselect (menu_shell);
921 + /* Deselect the active item so that any submenus are poped down */
922 + gtk_menu_shell_deselect (menu_shell);
924 - gtk_menu_remove_scroll_timeout (menu);
925 - menu->scroll_step = (scroll_fast) ? MENU_SCROLL_STEP2 : MENU_SCROLL_STEP1;
926 - menu->timeout_id = g_timeout_add ((scroll_fast) ? MENU_SCROLL_TIMEOUT2 : MENU_SCROLL_TIMEOUT1,
927 - gtk_menu_scroll_timeout,
930 - else if (!enter && !in_arrow && menu->lower_arrow_prelight)
932 - gdk_window_invalidate_rect (GTK_WIDGET (menu)->window, &rect, FALSE);
934 - gtk_menu_stop_scrolling (menu);
936 + gtk_menu_remove_scroll_timeout (menu);
937 + menu->scroll_step = (scroll_fast) ? MENU_SCROLL_STEP2 : MENU_SCROLL_STEP1;
941 + /* Only do stuff on click. */
942 + GtkSettings *settings;
945 + settings = gtk_settings_get_default ();
946 + g_object_get (settings, "gtk-update-timeout", &timeout, NULL);
948 + menu->timeout_id = g_timeout_add (timeout / 2, gtk_menu_scroll_timeout, menu);
950 + priv->lower_arrow_state = GTK_STATE_ACTIVE;
953 + gdk_window_invalidate_rect (GTK_WIDGET (menu)->window, &rect, FALSE);
957 + gtk_menu_stop_scrolling (menu);
959 + priv->lower_arrow_state = menu->lower_arrow_prelight ?
960 + GTK_STATE_PRELIGHT : GTK_STATE_NORMAL;
962 + gdk_window_invalidate_rect (GTK_WIDGET (menu)->window, &rect, FALSE);
968 @@ -3041,7 +3364,7 @@
969 GtkMenuShell *menu_shell = GTK_MENU_SHELL (widget);
971 if (!menu_shell->ignore_enter)
972 - gtk_menu_handle_scrolling (GTK_MENU (widget), event->x_root, event->y_root, TRUE);
973 + gtk_menu_handle_scrolling (GTK_MENU (widget), event->x_root, event->y_root, TRUE, TRUE);
976 if (menu_item && GTK_IS_MENU_ITEM (menu_item))
977 @@ -3106,7 +3429,7 @@
978 if (gtk_menu_navigating_submenu (menu, event->x_root, event->y_root))
981 - gtk_menu_handle_scrolling (menu, event->x_root, event->y_root, FALSE);
982 + gtk_menu_handle_scrolling (menu, event->x_root, event->y_root, FALSE, TRUE);
984 event_widget = gtk_get_event_widget ((GdkEvent*) event);
986 @@ -3611,7 +3934,13 @@
987 requisition.width, requisition.height);
990 - menu->scroll_offset = scroll_offset;
991 + /* Hildon hack for menu in comboboxes:
992 + * in case the menu in attached to a ComboBox, the scroll_offset is
993 + * calculated in the positioning function so we dont't overwrite it
994 + * with the value calculated above (in this function) */
995 + if ( !GTK_IS_COMBO_BOX(gtk_menu_get_attach_widget(menu)) )
996 + menu->scroll_offset = scroll_offset;
1001 @@ -3628,9 +3957,6 @@
1002 gtk_menu_stop_scrolling (GtkMenu *menu)
1004 gtk_menu_remove_scroll_timeout (menu);
1006 - menu->upper_arrow_prelight = FALSE;
1007 - menu->lower_arrow_prelight = FALSE;
1011 @@ -3644,6 +3970,8 @@
1012 gboolean last_visible;
1014 guint vertical_padding;
1015 + guint horizontal_padding;
1016 + gboolean double_arrows;
1018 widget = GTK_WIDGET (menu);
1020 @@ -3663,19 +3991,93 @@
1022 gtk_widget_style_get (GTK_WIDGET (menu),
1023 "vertical-padding", &vertical_padding,
1024 + "horizontal-padding", &horizontal_padding,
1025 + "double_arrows", &double_arrows,
1028 border_width = GTK_CONTAINER (menu)->border_width;
1029 - view_width -= (border_width + widget->style->xthickness) * 2;
1030 + view_width -= (border_width + widget->style->xthickness + horizontal_padding) * 2;
1031 view_height -= (border_width + widget->style->ythickness + vertical_padding) * 2;
1032 menu_height = widget->requisition.height -
1033 (border_width + widget->style->ythickness + vertical_padding) * 2;
1035 - x = border_width + widget->style->xthickness;
1036 + x = border_width + widget->style->xthickness + horizontal_padding;
1037 y = border_width + widget->style->ythickness + vertical_padding;
1039 + if (double_arrows && !menu->tearoff_active && (view_height < menu_height))
1041 + GtkMenuPrivate *priv;
1042 + GtkStateType upper_arrow_previous_state, lower_arrow_previous_state;
1044 + priv = gtk_menu_get_private (menu);
1046 + upper_arrow_previous_state = priv->upper_arrow_state;
1047 + lower_arrow_previous_state = priv->lower_arrow_state;
1049 + if (!menu->upper_arrow_visible || !menu->lower_arrow_visible)
1050 + gtk_widget_queue_draw (GTK_WIDGET (menu));
1052 + view_height -= 2*MENU_SCROLL_ARROW_HEIGHT;
1053 + y += MENU_SCROLL_ARROW_HEIGHT;
1055 + menu->upper_arrow_visible = menu->lower_arrow_visible = TRUE;
1056 + if (priv->upper_arrow_state == GTK_STATE_INSENSITIVE)
1058 + priv->upper_arrow_state = menu->upper_arrow_prelight ?
1059 + GTK_STATE_PRELIGHT : GTK_STATE_NORMAL;
1061 + if (priv->lower_arrow_state == GTK_STATE_INSENSITIVE)
1063 + priv->lower_arrow_state = menu->lower_arrow_prelight ?
1064 + GTK_STATE_PRELIGHT : GTK_STATE_NORMAL;
1070 + priv->upper_arrow_state = GTK_STATE_INSENSITIVE;
1072 + if (offset >= menu_height - view_height)
1074 + offset = menu_height - view_height;
1075 + priv->lower_arrow_state = GTK_STATE_INSENSITIVE;
1078 + if ((priv->upper_arrow_state != upper_arrow_previous_state) ||
1079 + (priv->lower_arrow_state != lower_arrow_previous_state))
1080 + gtk_widget_queue_draw (GTK_WIDGET (menu));
1082 + if (upper_arrow_previous_state != GTK_STATE_INSENSITIVE &&
1083 + priv->upper_arrow_state == GTK_STATE_INSENSITIVE)
1085 + /* If we hid the upper arrow, possibly remove timeout */
1086 + if (menu->scroll_step < 0)
1088 + gtk_menu_stop_scrolling (menu);
1089 + gtk_widget_queue_draw (GTK_WIDGET (menu));
1093 + if (lower_arrow_previous_state != GTK_STATE_INSENSITIVE &&
1094 + priv->lower_arrow_state == GTK_STATE_INSENSITIVE)
1096 + /* If we hid the lower arrow, possibly remove timeout */
1097 + if (menu->scroll_step > 0)
1099 + gtk_menu_stop_scrolling (menu);
1100 + gtk_widget_queue_draw (GTK_WIDGET (menu));
1105 if (!menu->tearoff_active)
1110 + if (offset >= menu_height - view_height)
1111 + offset = menu_height - view_height;
1113 last_visible = menu->upper_arrow_visible;
1114 menu->upper_arrow_visible = offset > 0;
1116 @@ -3685,8 +4087,6 @@
1117 if ( (last_visible != menu->upper_arrow_visible) &&
1118 !menu->upper_arrow_visible)
1120 - menu->upper_arrow_prelight = FALSE;
1122 /* If we hid the upper arrow, possibly remove timeout */
1123 if (menu->scroll_step < 0)
1125 @@ -3704,8 +4104,6 @@
1126 if ( (last_visible != menu->lower_arrow_visible) &&
1127 !menu->lower_arrow_visible)
1129 - menu->lower_arrow_prelight = FALSE;
1131 /* If we hid the lower arrow, possibly remove timeout */
1132 if (menu->scroll_step > 0)
1134 @@ -3792,12 +4190,14 @@
1135 &child_offset, &child_height, &last_child))
1137 guint vertical_padding;
1138 + gboolean double_arrows;
1140 y = menu->scroll_offset;
1141 gdk_drawable_get_size (GTK_WIDGET (menu)->window, &width, &height);
1143 gtk_widget_style_get (GTK_WIDGET (menu),
1144 "vertical-padding", &vertical_padding,
1145 + "double_arrows", &double_arrows,
1148 height -= 2*GTK_CONTAINER (menu)->border_width + 2*GTK_WIDGET (menu)->style->ythickness + 2*vertical_padding;
1149 @@ -3820,11 +4220,11 @@
1150 if (child_offset + child_height > y + height - arrow_height)
1153 - if (!last_child && !menu->tearoff_active)
1154 + if ((!last_child && !menu->tearoff_active) || (double_arrows))
1155 arrow_height += MENU_SCROLL_ARROW_HEIGHT;
1157 y = child_offset + child_height - height + arrow_height;
1158 - if ((y > 0) && !menu->tearoff_active)
1159 + if (((y > 0) && !menu->tearoff_active) || (double_arrows))
1161 /* Need upper arrow */
1162 arrow_height += MENU_SCROLL_ARROW_HEIGHT;
1163 @@ -4374,3 +4774,60 @@
1167 +/* Little help function for making some sanity tests on this menu.
1168 + * Checks that given widget really is a menu and that it has no name
1169 + * assigned to it yet.
1170 + * Names used to do hildon theming:
1171 + * HILDON_MENU_NAME_SHARP for menu with sharp upper corners
1172 + * HILDON_MENU_NAME_ROUND for menu with round corners
1175 +gtk_menu_check_name (GtkWidget *widget)
1177 + gboolean legal_name = FALSE;
1178 + gchar **tmp = NULL;
1179 + const gchar *name = NULL;
1180 + static gchar *menu_names[] = { "GtkMenu",
1181 + HILDON_MENU_NAME_SHARP,
1182 + HILDON_MENU_NAME_ROUND,
1183 + HILDON_MENU_NAME_ROUND_FIRST_LEVEL,
1185 + if (GTK_IS_MENU (widget) &&
1186 + (name = gtk_widget_get_name (widget)))
1188 + if (!g_ascii_strcasecmp (name, HILDON_MENU_NAME_FORCE_SHARP) || !g_ascii_strcasecmp (name, HILDON_MENU_NAME_FORCE_ROUND))
1190 + for (tmp = menu_names; *tmp; tmp++)
1191 + if (!g_ascii_strcasecmp (name, *tmp ))
1193 + legal_name = TRUE;
1198 + return legal_name;
1201 +/* A function called when esc-key is pressed. */
1203 +_gtk_menu_close_current (GtkMenu * menu)
1205 + GtkMenuShell * shell = GTK_MENU_SHELL (menu);
1207 + /* Check is a submenu of current menu item is visible.
1208 + * If it is, close that first. */
1209 + if (shell->active_menu_item && (GTK_MENU_ITEM (shell->active_menu_item)->submenu) && GTK_WIDGET_VISIBLE (GTK_MENU_ITEM (shell->active_menu_item)->submenu))
1210 + gtk_menu_popdown (GTK_MENU (GTK_MENU_ITEM (shell->active_menu_item)->submenu));
1212 + gtk_menu_popdown (menu);
1216 +/* Hildon function to make context menus behave according to spec */
1218 +_gtk_menu_enable_context_menu_behavior (GtkMenu *menu)
1220 + GtkMenuPrivate *priv = gtk_menu_get_private (menu);
1222 + priv->context_menu = TRUE;