diff options
Diffstat (limited to 'packages/gtk+/gtk+-2.6.4-1.osso7/gtkmenu.c.diff')
-rw-r--r-- | packages/gtk+/gtk+-2.6.4-1.osso7/gtkmenu.c.diff | 1223 |
1 files changed, 1223 insertions, 0 deletions
diff --git a/packages/gtk+/gtk+-2.6.4-1.osso7/gtkmenu.c.diff b/packages/gtk+/gtk+-2.6.4-1.osso7/gtkmenu.c.diff index e69de29bb2..16fac1b155 100644 --- a/packages/gtk+/gtk+-2.6.4-1.osso7/gtkmenu.c.diff +++ b/packages/gtk+/gtk+-2.6.4-1.osso7/gtkmenu.c.diff @@ -0,0 +1,1223 @@ +--- gtk+-2.6.4/gtk/gtkmenu.c 2005-03-01 08:28:56.000000000 +0200 ++++ gtk+-2.6.4/gtk/gtkmenu.c 2005-04-06 16:19:36.921925376 +0300 +@@ -24,10 +24,16 @@ + * GTK+ at ftp://ftp.gtk.org/pub/gtk/. + */ + ++/* Modified for Nokia Oyj during 2002-2005. See CHANGES file for list ++ * of changes. ++ */ ++ + #define GTK_MENU_INTERNALS + + #include <config.h> + #include <string.h> /* memset */ ++#include <math.h> ++#include <stdlib.h> + #include "gdk/gdkkeysyms.h" + #include "gtkalias.h" + #include "gtkaccellabel.h" +@@ -44,7 +50,11 @@ + #include "gtkvscrollbar.h" + #include "gtksettings.h" + #include "gtkintl.h" ++#include "gtkcombobox.h" + ++/* Hildon : We need this to figure out if menu should have ++ * corners etc. */ ++#include "gtkmenubar.h" + + #define MENU_ITEM_CLASS(w) GTK_MENU_ITEM_GET_CLASS (w) + +@@ -55,16 +65,43 @@ + * extends below the submenu + */ + ++/* HILDON: ++ * Urgh, nasty thing to hard-code things like these :p ++ * One should really do some rewriting here... ++ */ ++ + #define MENU_SCROLL_STEP1 8 + #define MENU_SCROLL_STEP2 15 +-#define MENU_SCROLL_ARROW_HEIGHT 16 +-#define MENU_SCROLL_FAST_ZONE 8 ++#define MENU_SCROLL_ARROW_HEIGHT 20 /* This used to be: 23; This hard-coding should be ++ * changed. Add arrow_height style property into ++ * commongtkrc and read it from there everywhere ++ * where a reference to MENU_SCROLL_ARROW_HEIGHT ++ * is made. ++ * If these changes are made, please modify also ++ * gtkcombobox.c. ++ */ ++#define MENU_SCROLL_FAST_ZONE MENU_SCROLL_ARROW_HEIGHT /* Was originally 8 */ + #define MENU_SCROLL_TIMEOUT1 50 + #define MENU_SCROLL_TIMEOUT2 20 + + #define ATTACH_INFO_KEY "gtk-menu-child-attach-info-key" + #define ATTACHED_MENUS "gtk-attached-menus" + ++/* HILDON: */ ++#define HILDON_MENU_NAME_SHARP "menu_with_corners" ++ ++/* needed to allow different themeing for first level menus */ ++#define HILDON_MENU_NAME_ROUND_FIRST_LEVEL "menu_without_corners_first_level" ++#define HILDON_MENU_NAME_ROUND "menu_without_corners" ++#define HILDON_MENU_NAME_FORCE_SHARP "menu_force_with_corners" ++#define HILDON_MENU_NAME_FORCE_ROUND "menu_force_without_corners" ++ ++/* maximum sizes for menus when attached to comboboxes */ ++#define HILDON_MENU_COMBO_MAX_WIDTH 406 ++#define HILDON_MENU_COMBO_MIN_WIDTH 66 ++#define HILDON_MENU_COMBO_MAX_HEIGHT 305 ++#define HILDON_MENU_COMBO_MIN_HEIGHT 70 ++ + typedef struct _GtkMenuAttachData GtkMenuAttachData; + typedef struct _GtkMenuPrivate GtkMenuPrivate; + +@@ -92,6 +129,15 @@ + gboolean have_layout; + gint n_rows; + gint n_columns; ++ ++ /* Arrow states */ ++ GtkStateType lower_arrow_state; ++ GtkStateType upper_arrow_state; ++ ++ /* For context menu behavior */ ++ gboolean context_menu; ++ int popup_pointer_x; ++ int popup_pointer_y; + }; + + typedef struct +@@ -108,6 +154,7 @@ + + enum { + MOVE_SCROLL, ++ CLOSE_CURRENT, + LAST_SIGNAL + }; + +@@ -191,7 +238,8 @@ + static void gtk_menu_handle_scrolling (GtkMenu *menu, + gint event_x, + gint event_y, +- gboolean enter); ++ gboolean enter, ++ gboolean motion); + static void gtk_menu_set_tearoff_hints (GtkMenu *menu, + gint width); + static void gtk_menu_style_set (GtkWidget *widget, +@@ -232,6 +280,9 @@ + guint signal_id); + static void _gtk_menu_refresh_accel_paths (GtkMenu *menu, + gboolean group_changed); ++static gboolean gtk_menu_check_name (GtkWidget *widget); ++ ++static void _gtk_menu_close_current (GtkMenu *menu); + + static GtkMenuShellClass *parent_class = NULL; + static const gchar attach_data_key[] = "gtk-menu-attach-data"; +@@ -496,7 +547,6 @@ + widget_class->hide_all = gtk_menu_hide_all; + widget_class->enter_notify_event = gtk_menu_enter_notify; + widget_class->leave_notify_event = gtk_menu_leave_notify; +- widget_class->motion_notify_event = gtk_menu_motion_notify; + widget_class->style_set = gtk_menu_style_set; + widget_class->focus = gtk_menu_focus; + widget_class->can_activate_accel = gtk_menu_real_can_activate_accel; +@@ -521,6 +571,15 @@ + _gtk_marshal_VOID__ENUM, + G_TYPE_NONE, 1, + GTK_TYPE_SCROLL_TYPE); ++ ++ menu_signals[CLOSE_CURRENT] = ++ _gtk_binding_signal_new ("close_current", ++ G_OBJECT_CLASS_TYPE (object_class), ++ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, ++ G_CALLBACK (_gtk_menu_close_current), ++ NULL, NULL, ++ _gtk_marshal_VOID__VOID, ++ G_TYPE_NONE, 0); + + g_object_class_install_property (gobject_class, + PROP_TEAROFF_TITLE, +@@ -606,6 +665,11 @@ + G_PARAM_READWRITE)); + + binding_set = gtk_binding_set_by_class (class); ++ /* Hildon : We moved handling of escape-key here because we need it to ++ * work like closing a submenu, not closing all the menus. */ ++ gtk_binding_entry_add_signal (binding_set, ++ GDK_Escape, 0, ++ "close_current", 0); + gtk_binding_entry_add_signal (binding_set, + GDK_Up, 0, + "move_current", 1, +@@ -709,6 +773,25 @@ + DEFAULT_POPDOWN_DELAY, + G_PARAM_READWRITE)); + ++ /* Hildon addition : border width was ++ replaced with horizontal-padding and ++ vertical-padding (which already is an style ++ property for GtkMenu). */ ++ gtk_widget_class_install_style_property (widget_class, ++ g_param_spec_int ("horizontal-padding", ++ P_("Horizontal Padding"), ++ P_("Extra space at the left and right edges of the menu"), ++ 0, ++ G_MAXINT, ++ 0, /* 1, */ ++ G_PARAM_READABLE)); ++ ++ gtk_widget_class_install_style_property (widget_class, ++ g_param_spec_boolean ("double_arrows", ++ P_("Double Arrows"), ++ P_("When scrolling, always show both arrows."), ++ FALSE, ++ G_PARAM_READABLE)); + } + + +@@ -884,13 +967,14 @@ + menu->toggle_size = 0; + + menu->toplevel = g_object_connect (g_object_new (GTK_TYPE_WINDOW, +- "type", GTK_WINDOW_POPUP, +- "child", menu, +- NULL), ++ "type", GTK_WINDOW_POPUP, ++ "child", menu, ++ NULL), + "signal::event", gtk_menu_window_event, menu, + "signal::size_request", gtk_menu_window_size_request, menu, + "signal::destroy", gtk_widget_destroyed, &menu->toplevel, + NULL); ++ + gtk_window_set_resizable (GTK_WINDOW (menu->toplevel), FALSE); + gtk_window_set_mnemonic_modifier (GTK_WINDOW (menu->toplevel), 0); + +@@ -919,6 +1003,15 @@ + menu->lower_arrow_visible = FALSE; + menu->upper_arrow_prelight = FALSE; + menu->lower_arrow_prelight = FALSE; ++ ++ /* <Hildon> */ ++ priv->upper_arrow_state = GTK_STATE_NORMAL; ++ priv->lower_arrow_state = GTK_STATE_NORMAL; ++ ++ priv->context_menu = FALSE; ++ priv->popup_pointer_x = -1; ++ priv->popup_pointer_y = -1; ++ /* </hildon */ + + priv->have_layout = FALSE; + } +@@ -1220,7 +1313,8 @@ + + static gboolean + popup_grab_on_window (GdkWindow *window, +- guint32 activate_time) ++ guint32 activate_time, ++ gboolean grab_keyboard) + { + if ((gdk_pointer_grab (window, TRUE, + GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | +@@ -1228,7 +1322,8 @@ + GDK_POINTER_MOTION_MASK, + NULL, NULL, activate_time) == 0)) + { +- if (gdk_keyboard_grab (window, TRUE, ++ if (!grab_keyboard || ++ gdk_keyboard_grab (window, TRUE, + activate_time) == 0) + return TRUE; + else +@@ -1282,6 +1377,7 @@ + GtkWidget *parent; + GdkEvent *current_event; + GtkMenuShell *menu_shell; ++ gboolean grab_keyboard; + GtkMenuPrivate *priv = gtk_menu_get_private (menu); + + g_return_if_fail (GTK_IS_MENU (menu)); +@@ -1333,10 +1429,28 @@ + * probably could just leave the grab on the other window, with a + * little reorganization of the code in gtkmenu*). + */ ++ ++ grab_keyboard = gtk_menu_shell_get_take_focus (menu_shell); ++ gtk_window_set_accept_focus (GTK_WINDOW (menu->toplevel), grab_keyboard); ++ + if (xgrab_shell && xgrab_shell != widget) + { +- if (popup_grab_on_window (xgrab_shell->window, activate_time)) ++ if (popup_grab_on_window (xgrab_shell->window, activate_time, grab_keyboard)) + GTK_MENU_SHELL (xgrab_shell)->have_xgrab = TRUE; ++ ++ /* HILDON: ++ * Check wheter parent is GtkMenuBar. If so, ++ * then we need sharp upper corners for this menu. ++ */ ++ if (gtk_menu_check_name (widget)) ++ { ++ if (GTK_IS_MENU_BAR (parent_menu_shell)) ++ gtk_widget_set_name (widget, HILDON_MENU_NAME_SHARP); ++ else if (GTK_IS_MENU (parent_menu_shell)) ++ gtk_widget_set_name( widget, HILDON_MENU_NAME_ROUND); ++ else ++ gtk_widget_set_name (widget, HILDON_MENU_NAME_ROUND_FIRST_LEVEL); ++ } + } + else + { +@@ -1344,8 +1458,14 @@ + + xgrab_shell = widget; + transfer_window = menu_grab_transfer_window_get (menu); +- if (popup_grab_on_window (transfer_window, activate_time)) ++ if (popup_grab_on_window (transfer_window, activate_time, grab_keyboard)) + GTK_MENU_SHELL (xgrab_shell)->have_xgrab = TRUE; ++ ++ /* HILDON: ++ * We want this menu to have round corners (Used by default) ++ */ ++ if (gtk_menu_check_name (widget)) ++ gtk_widget_set_name (widget, HILDON_MENU_NAME_ROUND_FIRST_LEVEL); + } + + if (!GTK_MENU_SHELL (xgrab_shell)->have_xgrab) +@@ -1409,6 +1529,23 @@ + + /* Position the menu, possibly changing the size request + */ ++ if (GTK_IS_COMBO_BOX (gtk_menu_get_attach_widget (menu))) ++ { ++ /* Hildon - limit the size if the menu is attached to a ComboBox */ ++ GtkRequisition req; ++ gint width, height; ++ ++ gtk_widget_set_size_request (widget, -1, -1); ++ gtk_widget_size_request (widget, &req); ++ ++ width = MAX (MIN (req.width, HILDON_MENU_COMBO_MAX_WIDTH), ++ HILDON_MENU_COMBO_MIN_WIDTH); ++ height = MAX (MIN (req.height, HILDON_MENU_COMBO_MAX_HEIGHT), ++ HILDON_MENU_COMBO_MIN_HEIGHT); ++ ++ gtk_widget_set_size_request (widget, width, height); ++ } ++ + gtk_menu_position (menu); + + /* Compute the size of the toplevel and realize it so we +@@ -1430,13 +1567,29 @@ + + gtk_menu_scroll_to (menu, menu->scroll_offset); + ++ if (priv->context_menu) ++ { ++ /* Save position of the pointer during popup */ ++ /* currently not-multihead safe */ ++ GdkScreen *screen; ++ GdkDisplay *display; ++ ++ screen = gtk_widget_get_screen (widget); ++ display = gdk_screen_get_display (screen); ++ ++ gdk_display_get_pointer (display, NULL, ++ &priv->popup_pointer_x, ++ &priv->popup_pointer_y, ++ NULL); ++ } ++ + /* Once everything is set up correctly, map the toplevel window on + the screen. + */ + gtk_widget_show (menu->toplevel); + + if (xgrab_shell == widget) +- popup_grab_on_window (widget->window, activate_time); /* Should always succeed */ ++ popup_grab_on_window (widget->window, activate_time, grab_keyboard); /* Should always succeed */ + gtk_grab_add (GTK_WIDGET (menu)); + } + +@@ -1996,6 +2149,7 @@ + GtkWidget *child; + GList *children; + guint vertical_padding; ++ guint horizontal_padding; + + g_return_if_fail (GTK_IS_MENU (widget)); + +@@ -2025,9 +2179,10 @@ + + gtk_widget_style_get (GTK_WIDGET (menu), + "vertical-padding", &vertical_padding, ++ "horizontal-padding", &horizontal_padding, + NULL); + +- attributes.x = border_width + widget->style->xthickness; ++ attributes.x = border_width + widget->style->xthickness + horizontal_padding; + attributes.y = border_width + widget->style->ythickness + vertical_padding; + attributes.width = MAX (1, widget->allocation.width - attributes.x * 2); + attributes.height = MAX (1, widget->allocation.height - attributes.y * 2); +@@ -2040,11 +2195,14 @@ + if (menu->lower_arrow_visible) + attributes.height -= MENU_SCROLL_ARROW_HEIGHT; + ++ attributes.window_type = GDK_WINDOW_CHILD; ++ + menu->view_window = gdk_window_new (widget->window, &attributes, attributes_mask); + gdk_window_set_user_data (menu->view_window, menu); + + attributes.x = 0; + attributes.y = 0; ++ attributes.width = MAX (1, widget->requisition.width - (border_width + widget->style->xthickness + horizontal_padding) * 2); + attributes.height = MAX (1, widget->requisition.height - (border_width + widget->style->ythickness + vertical_padding) * 2); + + menu->bin_window = gdk_window_new (menu->view_window, &attributes, attributes_mask); +@@ -2164,6 +2322,10 @@ + guint vertical_padding; + GtkRequisition child_requisition; + GtkMenuPrivate *priv; ++ guint horizontal_padding; ++ GdkScreen *screen; ++ GdkRectangle monitor; ++ gint monitor_num; + + g_return_if_fail (GTK_IS_MENU (widget)); + g_return_if_fail (requisition != NULL); +@@ -2182,6 +2344,16 @@ + priv->heights = g_new0 (guint, gtk_menu_get_n_rows (menu)); + priv->heights_length = gtk_menu_get_n_rows (menu); + ++/* Hildon addition to find out the monitor width */ ++ screen = gtk_widget_get_screen (widget); ++ if (widget->window != NULL) ++ monitor_num = gdk_screen_get_monitor_at_window (screen, widget->window); ++ else ++ monitor_num = 0; ++ if (monitor_num < 0) ++ monitor_num = 0; ++ gdk_screen_get_monitor_geometry (screen, monitor_num, &monitor); ++ + children = menu_shell->children; + while (children) + { +@@ -2223,15 +2395,18 @@ + + requisition->width += max_toggle_size + max_accel_width; + requisition->width *= gtk_menu_get_n_columns (menu); +- requisition->width += (GTK_CONTAINER (menu)->border_width + +- widget->style->xthickness) * 2; + + gtk_widget_style_get (GTK_WIDGET (menu), ++ "horizontal-padding", &horizontal_padding, + "vertical-padding", &vertical_padding, + NULL); ++ requisition->width += (GTK_CONTAINER (menu)->border_width + horizontal_padding + ++ widget->style->xthickness) * 2; + requisition->height += (GTK_CONTAINER (menu)->border_width + vertical_padding + + widget->style->ythickness) * 2; + ++/* Hildon addition to not make the menu too wide for the screen. */ ++ requisition->width = MIN (requisition->width, monitor.width); + menu->toggle_size = max_toggle_size; + + /* Don't resize the tearoff if it is not active, because it won't redraw (it is only a background pixmap). +@@ -2253,6 +2428,7 @@ + GList *children; + gint x, y; + gint width, height; ++ guint horizontal_padding; + guint vertical_padding; + + g_return_if_fail (GTK_IS_MENU (widget)); +@@ -2266,10 +2442,11 @@ + gtk_widget_get_child_requisition (GTK_WIDGET (menu), &child_requisition); + + gtk_widget_style_get (GTK_WIDGET (menu), ++ "horizontal-padding", &horizontal_padding, + "vertical-padding", &vertical_padding, + NULL); + +- x = GTK_CONTAINER (menu)->border_width + widget->style->xthickness; ++ x = GTK_CONTAINER (menu)->border_width + widget->style->xthickness + horizontal_padding; + y = GTK_CONTAINER (menu)->border_width + widget->style->ythickness + vertical_padding; + + width = MAX (1, allocation->width - x * 2); +@@ -2407,27 +2584,32 @@ + GdkEventExpose *event) + { + GtkMenu *menu; +- gint width, height; +- gint border_x, border_y; +- guint vertical_padding; + + g_return_if_fail (GTK_IS_MENU (widget)); + + menu = GTK_MENU (widget); + +- gtk_widget_style_get (GTK_WIDGET (menu), +- "vertical-padding", &vertical_padding, +- NULL); +- +- border_x = GTK_CONTAINER (widget)->border_width + widget->style->xthickness; +- border_y = GTK_CONTAINER (widget)->border_width + widget->style->ythickness + vertical_padding; +- gdk_drawable_get_size (widget->window, &width, &height); +- + if (event->window == widget->window) + { ++ gint width, height; ++ gint border_x, border_y; ++ guint vertical_padding; ++ guint horizontal_padding; ++ GtkMenuPrivate *priv; + gint arrow_space = MENU_SCROLL_ARROW_HEIGHT - 2 * widget->style->ythickness; + gint arrow_size = 0.7 * arrow_space; + ++ priv = gtk_menu_get_private (menu); ++ ++ gtk_widget_style_get (GTK_WIDGET (menu), ++ "vertical-padding", &vertical_padding, ++ "horizontal-padding", &horizontal_padding, ++ NULL); ++ ++ border_x = GTK_CONTAINER (widget)->border_width + widget->style->xthickness + horizontal_padding; ++ border_y = GTK_CONTAINER (widget)->border_width + widget->style->ythickness + vertical_padding; ++ gdk_drawable_get_size (widget->window, &width, &height); ++ + gtk_paint_box (widget->style, + widget->window, + GTK_STATE_NORMAL, +@@ -2436,21 +2618,9 @@ + 0, 0, -1, -1); + if (menu->upper_arrow_visible && !menu->tearoff_active) + { +- gtk_paint_box (widget->style, +- widget->window, +- menu->upper_arrow_prelight ? +- GTK_STATE_PRELIGHT : GTK_STATE_NORMAL, +- GTK_SHADOW_OUT, +- NULL, widget, "menu", +- border_x, +- border_y, +- width - 2 * border_x, +- MENU_SCROLL_ARROW_HEIGHT); +- + gtk_paint_arrow (widget->style, + widget->window, +- menu->upper_arrow_prelight ? +- GTK_STATE_PRELIGHT : GTK_STATE_NORMAL, ++ priv->upper_arrow_state, + GTK_SHADOW_OUT, + NULL, widget, "menu_scroll_arrow_up", + GTK_ARROW_UP, +@@ -2462,21 +2632,9 @@ + + if (menu->lower_arrow_visible && !menu->tearoff_active) + { +- gtk_paint_box (widget->style, +- widget->window, +- menu->lower_arrow_prelight ? +- GTK_STATE_PRELIGHT : GTK_STATE_NORMAL, +- GTK_SHADOW_OUT, +- NULL, widget, "menu", +- border_x, +- height - border_y - MENU_SCROLL_ARROW_HEIGHT, +- width - 2*border_x, +- MENU_SCROLL_ARROW_HEIGHT); +- + gtk_paint_arrow (widget->style, + widget->window, +- menu->lower_arrow_prelight ? +- GTK_STATE_PRELIGHT : GTK_STATE_NORMAL, ++ priv->lower_arrow_state, + GTK_SHADOW_OUT, + NULL, widget, "menu_scroll_arrow_down", + GTK_ARROW_DOWN, +@@ -2516,18 +2674,82 @@ + GTK_WIDGET_CLASS (parent_class)->show (widget); + } + ++static GtkWidget * ++find_active_menu_item (GdkEventButton *event) ++{ ++ GtkWidget *menu_item; ++ ++ menu_item = gtk_get_event_widget ((GdkEvent*) event); ++ while (menu_item && !GTK_IS_MENU_ITEM (menu_item)) ++ menu_item = menu_item->parent; ++ ++ return menu_item; ++} ++ ++static gboolean ++pointer_in_menu_tree (GtkWidget *widget) ++{ ++ GtkMenuShell *mshell; ++ int width, height, x, y; ++ ++ mshell = GTK_MENU_SHELL (widget); ++ ++ gdk_window_get_pointer (widget->window, &x, &y, NULL); ++ gdk_drawable_get_size (widget->window, &width, &height); ++ ++ if ((x <= width) && (x >= 0) && (y <= height) && (y >= 0)) ++ return TRUE; ++ ++ if ((mshell->parent_menu_shell != NULL) && ++ GTK_IS_MENU (mshell->parent_menu_shell)) ++ return pointer_in_menu_tree (mshell->parent_menu_shell); ++ ++ return FALSE; ++} ++ ++static int ++distance_traveled (GtkWidget *widget) ++{ ++ GtkMenuPrivate *priv; ++ GdkScreen *screen; ++ GdkDisplay *display; ++ int x, y, dx, dy; ++ ++ priv = gtk_menu_get_private (GTK_MENU (widget)); ++ ++ screen = gtk_widget_get_screen (widget); ++ display = gdk_screen_get_display (screen); ++ ++ gdk_display_get_pointer (display, NULL, &x, &y, NULL); ++ ++ dx = (priv->popup_pointer_x - x); ++ dy = (priv->popup_pointer_y - y); ++ ++ return abs ((int) sqrt ((double) (dx * dx + dy * dy))); ++} ++ + static gboolean + gtk_menu_button_press (GtkWidget *widget, +- GdkEventButton *event) ++ GdkEventButton *event) + { +- /* Don't pop down the menu for releases over scroll arrows +- */ +- if (GTK_IS_MENU (widget)) ++ GtkWidget *menu_item; ++ ++ menu_item = find_active_menu_item (event); ++ if (menu_item == NULL) + { + GtkMenu *menu = GTK_MENU (widget); + +- if (menu->upper_arrow_prelight || menu->lower_arrow_prelight) +- return TRUE; ++ if (menu->upper_arrow_prelight || menu->lower_arrow_prelight) ++ { ++ gtk_menu_handle_scrolling (GTK_MENU (widget), event->x_root, event->y_root, TRUE, FALSE); ++ ++ return TRUE; ++ } ++ ++ /* Don't pass down to menu shell if a non-menuitem part ++ * of the menu was clicked. */ ++ if (pointer_in_menu_tree (widget)) ++ return TRUE; + } + + return GTK_WIDGET_CLASS (parent_class)->button_press_event (widget, event); +@@ -2537,14 +2759,44 @@ + gtk_menu_button_release (GtkWidget *widget, + GdkEventButton *event) + { +- /* Don't pop down the menu for releases over scroll arrows +- */ +- if (GTK_IS_MENU (widget)) ++ GtkMenuPrivate *priv; ++ GtkWidget *menu_item; ++ ++ priv = gtk_menu_get_private (GTK_MENU (widget)); ++ ++ menu_item = find_active_menu_item (event); ++ if (menu_item == NULL) + { + GtkMenu *menu = GTK_MENU (widget); + +- if (menu->upper_arrow_prelight || menu->lower_arrow_prelight) +- return TRUE; ++ if (menu->upper_arrow_prelight || menu->lower_arrow_prelight) ++ { ++ gtk_menu_handle_scrolling (GTK_MENU (widget), event->x_root, event->y_root, FALSE, FALSE); ++ ++ return TRUE; ++ } ++ ++ if (priv->context_menu && ++ (priv->popup_pointer_x >= 0) && ++ (priv->popup_pointer_y >= 0)) ++ { ++ int distance; ++ ++ distance = distance_traveled (widget); ++ ++ priv->popup_pointer_x = -1; ++ priv->popup_pointer_y = -1; ++ ++ /* Don't popdown if we traveled less than 20px since popup point, ++ * as per the specs. */ ++ if (distance < 20) ++ return TRUE; ++ } ++ ++ /* Don't pass down to menu shell if a non-menuitem part ++ * of the menu was clicked. */ ++ if (pointer_in_menu_tree (widget)) ++ return TRUE; + } + + return GTK_WIDGET_CLASS (parent_class)->button_release_event (widget, event); +@@ -2765,7 +3017,7 @@ + gboolean need_enter; + + if (GTK_IS_MENU (widget)) +- gtk_menu_handle_scrolling (GTK_MENU (widget), event->x_root, event->y_root, TRUE); ++ gtk_menu_handle_scrolling (GTK_MENU (widget), event->x_root, event->y_root, TRUE, TRUE); + + /* We received the event for one of two reasons: + * +@@ -2779,7 +3031,27 @@ + menu_item = gtk_get_event_widget ((GdkEvent*) event); + if (!menu_item || !GTK_IS_MENU_ITEM (menu_item) || + !GTK_IS_MENU (menu_item->parent)) +- return FALSE; ++ { ++ GtkMenuPrivate *priv; ++ ++ priv = gtk_menu_get_private (GTK_MENU (widget)); ++ ++ if (priv->context_menu) ++ { ++ /* Context menu mode. If we dragged out of the menu, ++ * close the menu, as by the specs. */ ++ if (!pointer_in_menu_tree (widget) && ++ (distance_traveled (widget) >= 20) && ++ (event->state & GDK_BUTTON1_MASK)) ++ { ++ gtk_menu_deactivate (GTK_MENU_SHELL (widget)); ++ ++ return TRUE; ++ } ++ } ++ ++ return FALSE; ++ } + + menu_shell = GTK_MENU_SHELL (menu_item->parent); + menu = GTK_MENU (menu_shell); +@@ -2795,6 +3067,11 @@ + */ + if (gtk_menu_navigating_submenu (menu, event->x_root, event->y_root)) + return TRUE; ++/* HILDON MOD. ++ * Close the submenus that are two levels down from the currently selected. ++ * This ensures that the focus is correct all the time.*/ ++ if (GTK_MENU_ITEM(menu_item)->submenu != NULL) ++ gtk_menu_shell_deselect (GTK_MENU_SHELL(&(GTK_MENU(GTK_MENU_ITEM(menu_item)->submenu)->menu_shell))); + + /* Make sure we pop down if we enter a non-selectable menu item, so we + * don't show a submenu when the cursor is outside the stay-up triangle. +@@ -2828,6 +3105,7 @@ + send_event->crossing.y_root = event->y_root; + send_event->crossing.x = event->x; + send_event->crossing.y = event->y; ++ send_event->crossing.state = event->state; + + /* We send the event to 'widget', the currently active menu, + * instead of 'menu', the menu that the pointer is in. This +@@ -2852,17 +3130,24 @@ + GtkWidget *widget; + gint offset; + gint view_width, view_height; ++ gboolean double_arrows; + + widget = GTK_WIDGET (menu); + offset = menu->scroll_offset + step; + ++ /* get double_arrows style property */ ++ gtk_widget_style_get (widget, ++ "double_arrows", &double_arrows, ++ NULL); ++ + /* If we scroll upward and the non-visible top part + * is smaller than the scroll arrow it would be + * pretty stupid to show the arrow and taking more + * screen space than just scrolling to the top. + */ +- if ((step < 0) && (offset < MENU_SCROLL_ARROW_HEIGHT)) +- offset = 0; ++ if (!double_arrows) ++ if ((step < 0) && (offset < MENU_SCROLL_ARROW_HEIGHT)) ++ offset = 0; + + /* Don't scroll over the top if we weren't before: */ + if ((menu->scroll_offset >= 0) && (offset < 0)) +@@ -2874,6 +3159,12 @@ + if (menu->scroll_offset > 0) + view_height -= MENU_SCROLL_ARROW_HEIGHT; + ++ /* When both arrows are always shown, reduce ++ * view height even more. ++ */ ++ if (double_arrows) ++ view_height -= MENU_SCROLL_ARROW_HEIGHT; ++ + if ((menu->scroll_offset + view_height <= widget->requisition.height) && + (offset + view_height > widget->requisition.height)) + offset = widget->requisition.height - view_height; +@@ -2922,18 +3213,21 @@ + gtk_menu_handle_scrolling (GtkMenu *menu, + gint x, + gint y, +- gboolean enter) ++ gboolean enter, ++ gboolean motion) + { + GtkMenuShell *menu_shell; ++ GtkMenuPrivate *priv; + gint width, height; + gint border; + GdkRectangle rect; +- gboolean in_arrow; + gboolean scroll_fast = FALSE; + guint vertical_padding; + gint top_x, top_y; + gint win_x, win_y; + ++ priv = gtk_menu_get_private (menu); ++ + menu_shell = GTK_MENU_SHELL (menu); + + gdk_drawable_get_size (GTK_WIDGET (menu)->window, &width, &height); +@@ -2946,10 +3240,11 @@ + GTK_WIDGET (menu)->style->ythickness + vertical_padding; + + gdk_window_get_position (menu->toplevel->window, &top_x, &top_y); ++ x -= top_x; ++ y -= top_y; ++ + gdk_window_get_position (GTK_WIDGET (menu)->window, &win_x, &win_y); +- win_x += top_x; +- win_y += top_y; +- ++ + if (menu->upper_arrow_visible && !menu->tearoff_active) + { + rect.x = win_x; +@@ -2957,35 +3252,49 @@ + rect.width = width; + rect.height = MENU_SCROLL_ARROW_HEIGHT + border; + +- in_arrow = FALSE; ++ menu->upper_arrow_prelight = FALSE; + if ((x >= rect.x) && (x < rect.x + rect.width) && + (y >= rect.y) && (y < rect.y + rect.height)) +- { +- in_arrow = TRUE; +- scroll_fast = (y < rect.y + MENU_SCROLL_FAST_ZONE); +- } +- +- if (enter && in_arrow && +- (!menu->upper_arrow_prelight || menu->scroll_fast != scroll_fast)) +- { +- menu->upper_arrow_prelight = TRUE; +- menu->scroll_fast = scroll_fast; +- gdk_window_invalidate_rect (GTK_WIDGET (menu)->window, &rect, FALSE); +- +- /* Deselect the active item so that any submenus are poped down */ +- gtk_menu_shell_deselect (menu_shell); ++ menu->upper_arrow_prelight = TRUE; + +- gtk_menu_remove_scroll_timeout (menu); +- menu->scroll_step = (scroll_fast) ? -MENU_SCROLL_STEP2 : -MENU_SCROLL_STEP1; +- menu->timeout_id = g_timeout_add ((scroll_fast) ? MENU_SCROLL_TIMEOUT2 : MENU_SCROLL_TIMEOUT1, +- gtk_menu_scroll_timeout, +- menu); +- } +- else if (!enter && !in_arrow && menu->upper_arrow_prelight) ++ if (priv->upper_arrow_state != GTK_STATE_INSENSITIVE) + { +- gdk_window_invalidate_rect (GTK_WIDGET (menu)->window, &rect, FALSE); +- +- gtk_menu_stop_scrolling (menu); ++ if (enter && menu->upper_arrow_prelight && ++ (menu->timeout_id == 0 || menu->scroll_fast != scroll_fast)) ++ { ++ menu->scroll_fast = scroll_fast; ++ ++ /* Deselect the active item so that any submenus are poped down */ ++ gtk_menu_shell_deselect (menu_shell); ++ ++ gtk_menu_remove_scroll_timeout (menu); ++ menu->scroll_step = (scroll_fast) ? -MENU_SCROLL_STEP2 : -MENU_SCROLL_STEP1; ++ ++ if (!motion) ++ { ++ /* Only do stuff on click. */ ++ GtkSettings *settings; ++ guint timeout; ++ ++ settings = gtk_settings_get_default (); ++ g_object_get (settings, "gtk-update-timeout", &timeout, NULL); ++ ++ menu->timeout_id = g_timeout_add (timeout / 2, gtk_menu_scroll_timeout, menu); ++ ++ priv->upper_arrow_state = GTK_STATE_ACTIVE; ++ } ++ ++ gdk_window_invalidate_rect (GTK_WIDGET (menu)->window, &rect, FALSE); ++ } ++ else if (!enter) ++ { ++ gtk_menu_stop_scrolling (menu); ++ ++ priv->upper_arrow_state = menu->upper_arrow_prelight ? ++ GTK_STATE_PRELIGHT : GTK_STATE_NORMAL; ++ ++ gdk_window_invalidate_rect (GTK_WIDGET (menu)->window, &rect, FALSE); ++ } + } + } + +@@ -2996,36 +3305,50 @@ + rect.width = width; + rect.height = MENU_SCROLL_ARROW_HEIGHT + border; + +- in_arrow = FALSE; ++ menu->lower_arrow_prelight = FALSE; + if ((x >= rect.x) && (x < rect.x + rect.width) && + (y >= rect.y) && (y < rect.y + rect.height)) +- { +- in_arrow = TRUE; +- scroll_fast = (y > rect.y + rect.height - MENU_SCROLL_FAST_ZONE); +- } ++ menu->lower_arrow_prelight = TRUE; + +- if (enter && in_arrow && +- (!menu->lower_arrow_prelight || menu->scroll_fast != scroll_fast)) ++ if (priv->lower_arrow_state != GTK_STATE_INSENSITIVE) + { +- menu->lower_arrow_prelight = TRUE; +- menu->scroll_fast = scroll_fast; +- gdk_window_invalidate_rect (GTK_WIDGET (menu)->window, &rect, FALSE); ++ if (enter && menu->lower_arrow_prelight && ++ (menu->timeout_id == 0 || menu->scroll_fast != scroll_fast)) ++ { ++ menu->scroll_fast = scroll_fast; + +- /* Deselect the active item so that any submenus are poped down */ +- gtk_menu_shell_deselect (menu_shell); ++ /* Deselect the active item so that any submenus are poped down */ ++ gtk_menu_shell_deselect (menu_shell); + +- gtk_menu_remove_scroll_timeout (menu); +- menu->scroll_step = (scroll_fast) ? MENU_SCROLL_STEP2 : MENU_SCROLL_STEP1; +- menu->timeout_id = g_timeout_add ((scroll_fast) ? MENU_SCROLL_TIMEOUT2 : MENU_SCROLL_TIMEOUT1, +- gtk_menu_scroll_timeout, +- menu); +- } +- else if (!enter && !in_arrow && menu->lower_arrow_prelight) +- { +- gdk_window_invalidate_rect (GTK_WIDGET (menu)->window, &rect, FALSE); +- +- gtk_menu_stop_scrolling (menu); +- } ++ gtk_menu_remove_scroll_timeout (menu); ++ menu->scroll_step = (scroll_fast) ? MENU_SCROLL_STEP2 : MENU_SCROLL_STEP1; ++ ++ if (!motion) ++ { ++ /* Only do stuff on click. */ ++ GtkSettings *settings; ++ guint timeout; ++ ++ settings = gtk_settings_get_default (); ++ g_object_get (settings, "gtk-update-timeout", &timeout, NULL); ++ ++ menu->timeout_id = g_timeout_add (timeout / 2, gtk_menu_scroll_timeout, menu); ++ ++ priv->lower_arrow_state = GTK_STATE_ACTIVE; ++ } ++ ++ gdk_window_invalidate_rect (GTK_WIDGET (menu)->window, &rect, FALSE); ++ } ++ else if (!enter) ++ { ++ gtk_menu_stop_scrolling (menu); ++ ++ priv->lower_arrow_state = menu->lower_arrow_prelight ? ++ GTK_STATE_PRELIGHT : GTK_STATE_NORMAL; ++ ++ gdk_window_invalidate_rect (GTK_WIDGET (menu)->window, &rect, FALSE); ++ } ++ } + } + } + +@@ -3041,7 +3364,7 @@ + GtkMenuShell *menu_shell = GTK_MENU_SHELL (widget); + + if (!menu_shell->ignore_enter) +- gtk_menu_handle_scrolling (GTK_MENU (widget), event->x_root, event->y_root, TRUE); ++ gtk_menu_handle_scrolling (GTK_MENU (widget), event->x_root, event->y_root, TRUE, TRUE); + } + + if (menu_item && GTK_IS_MENU_ITEM (menu_item)) +@@ -3106,7 +3429,7 @@ + if (gtk_menu_navigating_submenu (menu, event->x_root, event->y_root)) + return TRUE; + +- gtk_menu_handle_scrolling (menu, event->x_root, event->y_root, FALSE); ++ gtk_menu_handle_scrolling (menu, event->x_root, event->y_root, FALSE, TRUE); + + event_widget = gtk_get_event_widget ((GdkEvent*) event); + +@@ -3611,7 +3934,13 @@ + requisition.width, requisition.height); + } + +- menu->scroll_offset = scroll_offset; ++ /* Hildon hack for menu in comboboxes: ++ * in case the menu in attached to a ComboBox, the scroll_offset is ++ * calculated in the positioning function so we dont't overwrite it ++ * with the value calculated above (in this function) */ ++ if ( !GTK_IS_COMBO_BOX(gtk_menu_get_attach_widget(menu)) ) ++ menu->scroll_offset = scroll_offset; ++ + } + + static void +@@ -3628,9 +3957,6 @@ + gtk_menu_stop_scrolling (GtkMenu *menu) + { + gtk_menu_remove_scroll_timeout (menu); +- +- menu->upper_arrow_prelight = FALSE; +- menu->lower_arrow_prelight = FALSE; + } + + static void +@@ -3644,6 +3970,8 @@ + gboolean last_visible; + gint menu_height; + guint vertical_padding; ++ guint horizontal_padding; ++ gboolean double_arrows; + + widget = GTK_WIDGET (menu); + +@@ -3663,19 +3991,93 @@ + + gtk_widget_style_get (GTK_WIDGET (menu), + "vertical-padding", &vertical_padding, ++ "horizontal-padding", &horizontal_padding, ++ "double_arrows", &double_arrows, + NULL); + + border_width = GTK_CONTAINER (menu)->border_width; +- view_width -= (border_width + widget->style->xthickness) * 2; ++ view_width -= (border_width + widget->style->xthickness + horizontal_padding) * 2; + view_height -= (border_width + widget->style->ythickness + vertical_padding) * 2; + menu_height = widget->requisition.height - + (border_width + widget->style->ythickness + vertical_padding) * 2; + +- x = border_width + widget->style->xthickness; ++ x = border_width + widget->style->xthickness + horizontal_padding; + y = border_width + widget->style->ythickness + vertical_padding; + ++ if (double_arrows && !menu->tearoff_active && (view_height < menu_height)) ++ { ++ GtkMenuPrivate *priv; ++ GtkStateType upper_arrow_previous_state, lower_arrow_previous_state; ++ ++ priv = gtk_menu_get_private (menu); ++ ++ upper_arrow_previous_state = priv->upper_arrow_state; ++ lower_arrow_previous_state = priv->lower_arrow_state; ++ ++ if (!menu->upper_arrow_visible || !menu->lower_arrow_visible) ++ gtk_widget_queue_draw (GTK_WIDGET (menu)); ++ ++ view_height -= 2*MENU_SCROLL_ARROW_HEIGHT; ++ y += MENU_SCROLL_ARROW_HEIGHT; ++ ++ menu->upper_arrow_visible = menu->lower_arrow_visible = TRUE; ++ if (priv->upper_arrow_state == GTK_STATE_INSENSITIVE) ++ { ++ priv->upper_arrow_state = menu->upper_arrow_prelight ? ++ GTK_STATE_PRELIGHT : GTK_STATE_NORMAL; ++ } ++ if (priv->lower_arrow_state == GTK_STATE_INSENSITIVE) ++ { ++ priv->lower_arrow_state = menu->lower_arrow_prelight ? ++ GTK_STATE_PRELIGHT : GTK_STATE_NORMAL; ++ } ++ ++ if (offset <= 0) ++ { ++ offset = 0; ++ priv->upper_arrow_state = GTK_STATE_INSENSITIVE; ++ } ++ if (offset >= menu_height - view_height) ++ { ++ offset = menu_height - view_height; ++ priv->lower_arrow_state = GTK_STATE_INSENSITIVE; ++ } ++ ++ if ((priv->upper_arrow_state != upper_arrow_previous_state) || ++ (priv->lower_arrow_state != lower_arrow_previous_state)) ++ gtk_widget_queue_draw (GTK_WIDGET (menu)); ++ ++ if (upper_arrow_previous_state != GTK_STATE_INSENSITIVE && ++ priv->upper_arrow_state == GTK_STATE_INSENSITIVE) ++ { ++ /* If we hid the upper arrow, possibly remove timeout */ ++ if (menu->scroll_step < 0) ++ { ++ gtk_menu_stop_scrolling (menu); ++ gtk_widget_queue_draw (GTK_WIDGET (menu)); ++ } ++ } ++ ++ if (lower_arrow_previous_state != GTK_STATE_INSENSITIVE && ++ priv->lower_arrow_state == GTK_STATE_INSENSITIVE) ++ { ++ /* If we hid the lower arrow, possibly remove timeout */ ++ if (menu->scroll_step > 0) ++ { ++ gtk_menu_stop_scrolling (menu); ++ gtk_widget_queue_draw (GTK_WIDGET (menu)); ++ } ++ } ++ } ++ else + if (!menu->tearoff_active) + { ++ if (offset <= 0) ++ offset = 0; ++ ++ if (offset >= menu_height - view_height) ++ offset = menu_height - view_height; ++ + last_visible = menu->upper_arrow_visible; + menu->upper_arrow_visible = offset > 0; + +@@ -3685,8 +4087,6 @@ + if ( (last_visible != menu->upper_arrow_visible) && + !menu->upper_arrow_visible) + { +- menu->upper_arrow_prelight = FALSE; +- + /* If we hid the upper arrow, possibly remove timeout */ + if (menu->scroll_step < 0) + { +@@ -3704,8 +4104,6 @@ + if ( (last_visible != menu->lower_arrow_visible) && + !menu->lower_arrow_visible) + { +- menu->lower_arrow_prelight = FALSE; +- + /* If we hid the lower arrow, possibly remove timeout */ + if (menu->scroll_step > 0) + { +@@ -3792,12 +4190,14 @@ + &child_offset, &child_height, &last_child)) + { + guint vertical_padding; ++ gboolean double_arrows; + + y = menu->scroll_offset; + gdk_drawable_get_size (GTK_WIDGET (menu)->window, &width, &height); + + gtk_widget_style_get (GTK_WIDGET (menu), + "vertical-padding", &vertical_padding, ++ "double_arrows", &double_arrows, + NULL); + + height -= 2*GTK_CONTAINER (menu)->border_width + 2*GTK_WIDGET (menu)->style->ythickness + 2*vertical_padding; +@@ -3820,11 +4220,11 @@ + if (child_offset + child_height > y + height - arrow_height) + { + arrow_height = 0; +- if (!last_child && !menu->tearoff_active) ++ if ((!last_child && !menu->tearoff_active) || (double_arrows)) + arrow_height += MENU_SCROLL_ARROW_HEIGHT; + + y = child_offset + child_height - height + arrow_height; +- if ((y > 0) && !menu->tearoff_active) ++ if (((y > 0) && !menu->tearoff_active) || (double_arrows)) + { + /* Need upper arrow */ + arrow_height += MENU_SCROLL_ARROW_HEIGHT; +@@ -4374,3 +4774,60 @@ + return list; + } + ++/* Little help function for making some sanity tests on this menu. ++ * Checks that given widget really is a menu and that it has no name ++ * assigned to it yet. ++ * Names used to do hildon theming: ++ * HILDON_MENU_NAME_SHARP for menu with sharp upper corners ++ * HILDON_MENU_NAME_ROUND for menu with round corners ++ */ ++static gboolean ++gtk_menu_check_name (GtkWidget *widget) ++{ ++ gboolean legal_name = FALSE; ++ gchar **tmp = NULL; ++ const gchar *name = NULL; ++ static gchar *menu_names[] = { "GtkMenu", ++ HILDON_MENU_NAME_SHARP, ++ HILDON_MENU_NAME_ROUND, ++ HILDON_MENU_NAME_ROUND_FIRST_LEVEL, ++ NULL }; ++ if (GTK_IS_MENU (widget) && ++ (name = gtk_widget_get_name (widget))) ++ { ++ if (!g_ascii_strcasecmp (name, HILDON_MENU_NAME_FORCE_SHARP) || !g_ascii_strcasecmp (name, HILDON_MENU_NAME_FORCE_ROUND)) ++ return FALSE; ++ for (tmp = menu_names; *tmp; tmp++) ++ if (!g_ascii_strcasecmp (name, *tmp )) ++ { ++ legal_name = TRUE; ++ break; ++ } ++ } ++ ++ return legal_name; ++} ++ ++/* A function called when esc-key is pressed. */ ++static void ++_gtk_menu_close_current (GtkMenu * menu) ++{ ++ GtkMenuShell * shell = GTK_MENU_SHELL (menu); ++ ++ /* Check is a submenu of current menu item is visible. ++ * If it is, close that first. */ ++ if (shell->active_menu_item && (GTK_MENU_ITEM (shell->active_menu_item)->submenu) && GTK_WIDGET_VISIBLE (GTK_MENU_ITEM (shell->active_menu_item)->submenu)) ++ gtk_menu_popdown (GTK_MENU (GTK_MENU_ITEM (shell->active_menu_item)->submenu)); ++ else ++ gtk_menu_popdown (menu); ++ ++} ++ ++/* Hildon function to make context menus behave according to spec */ ++void ++_gtk_menu_enable_context_menu_behavior (GtkMenu *menu) ++{ ++ GtkMenuPrivate *priv = gtk_menu_get_private (menu); ++ ++ priv->context_menu = TRUE; ++} |