X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;f=gtk%2Fgtkmenu.c;h=79bde09ab0686f4fd643d437724058a47320fe66;hb=5bbbc47a4c306653e8347f7afb85a940a503f755;hp=dada3298b612bee93630955345fa073a64b4b4c6;hpb=9d0febc9a64a5bfb0fcfc3a88de4757f6c1ff090;p=~andy%2Fgtk diff --git a/gtk/gtkmenu.c b/gtk/gtkmenu.c index dada3298b..79bde09ab 100644 --- a/gtk/gtkmenu.c +++ b/gtk/gtkmenu.c @@ -35,7 +35,7 @@ * #GtkMenuItem in a #GtkMenuBar or popped up by activating a * #GtkMenuItem in another #GtkMenu. * - * A #GtkMenu can also be popped up by activating a #GtkOptionMenu. + * A #GtkMenu can also be popped up by activating a #GtkComboBox. * Other composite widgets such as the #GtkNotebook can pop up a * #GtkMenu as well. * @@ -95,6 +95,7 @@ #include "gtkaccellabel.h" #include "gtkaccelmap.h" +#include "gtkadjustment.h" #include "gtkbindings.h" #include "gtkcheckmenuitem.h" #include "gtkmain.h" @@ -107,9 +108,12 @@ #include "gtkscrollbar.h" #include "gtksettings.h" #include "gtkprivate.h" +#include "gtkwidgetpath.h" #include "gtkwidgetprivate.h" +#include "gtkdnd.h" #include "gtkintl.h" #include "gtktypebuiltins.h" +#include "gtkwidgetprivate.h" #include "deprecated/gtktearoffmenuitem.h" @@ -225,12 +229,13 @@ static void gtk_menu_scroll_to (GtkMenu *menu, gint offset); static void gtk_menu_grab_notify (GtkWidget *widget, gboolean was_grabbed); +static gboolean gtk_menu_captured_event (GtkWidget *widget, + GdkEvent *event); + static void gtk_menu_stop_scrolling (GtkMenu *menu); static void gtk_menu_remove_scroll_timeout (GtkMenu *menu); static gboolean gtk_menu_scroll_timeout (gpointer data); -static gboolean gtk_menu_scroll_timeout_initial (gpointer data); -static void gtk_menu_start_scrolling (GtkMenu *menu); static void gtk_menu_scroll_item_visible (GtkMenuShell *menu_shell, GtkWidget *menu_item); @@ -636,15 +641,6 @@ gtk_menu_class_init (GtkMenuClass *class) -1, G_MAXINT, -1, GTK_PARAM_READWRITE)); - gtk_widget_class_install_style_property (widget_class, - g_param_spec_int ("vertical-padding", - P_("Vertical Padding"), - P_("Extra space at the top and bottom of the menu"), - 0, - G_MAXINT, - 1, - GTK_PARAM_READABLE)); - /** * GtkMenu:reserve-toggle-size: * @@ -666,6 +662,15 @@ gtk_menu_class_init (GtkMenuClass *class) TRUE, GTK_PARAM_READWRITE)); + /** + * GtkMenu:horizontal-padding: + * + * Extra space at the left and right edges of the menu. + * + * Deprecated: 3.8: use the standard padding CSS property (through objects + * like #GtkStyleContext and #GtkCssProvider); the value of this style + * property is ignored. + */ gtk_widget_class_install_style_property (widget_class, g_param_spec_int ("horizontal-padding", P_("Horizontal Padding"), @@ -673,7 +678,27 @@ gtk_menu_class_init (GtkMenuClass *class) 0, G_MAXINT, 0, - GTK_PARAM_READABLE)); + GTK_PARAM_READABLE | + G_PARAM_DEPRECATED)); + + /** + * GtkMenu:vertical-padding: + * + * Extra space at the top and bottom of the menu. + * + * Deprecated: 3.8: use the standard padding CSS property (through objects + * like #GtkStyleContext and #GtkCssProvider); the value of this style + * property is ignored. + */ + gtk_widget_class_install_style_property (widget_class, + g_param_spec_int ("vertical-padding", + P_("Vertical Padding"), + P_("Extra space at the top and bottom of the menu"), + 0, + G_MAXINT, + 1, + GTK_PARAM_READABLE | + G_PARAM_DEPRECATED)); gtk_widget_class_install_style_property (widget_class, g_param_spec_int ("vertical-offset", @@ -748,7 +773,7 @@ gtk_menu_class_init (GtkMenuClass *class) GTK_PARAM_READWRITE)); /** - * GtkMenu:arrow-scaling + * GtkMenu:arrow-scaling: * * Arbitrary constant to scale down the size of the scroll arrow. * @@ -1018,6 +1043,15 @@ gtk_menu_window_event (GtkWidget *window, case GDK_KEY_RELEASE: handled = gtk_widget_event (menu, event); break; + case GDK_WINDOW_STATE: + /* Window for the menu has been closed by the display server or by GDK. + * Update the internal state as if the user had clicked outside the + * menu + */ + if (event->window_state.new_window_state & GDK_WINDOW_STATE_WITHDRAWN && + event->window_state.changed_mask & GDK_WINDOW_STATE_WITHDRAWN) + gtk_menu_shell_deactivate (GTK_MENU_SHELL(menu)); + break; default: break; } @@ -1055,9 +1089,12 @@ gtk_menu_init (GtkMenu *menu) priv->needs_destruction_ref = TRUE; priv->monitor_num = -1; + priv->drag_start_y = -1; context = gtk_widget_get_style_context (GTK_WIDGET (menu)); gtk_style_context_add_class (context, GTK_STYLE_CLASS_MENU); + + _gtk_widget_set_captured_event_handler (GTK_WIDGET (menu), gtk_menu_captured_event); } static void @@ -1197,12 +1234,11 @@ gtk_menu_attach_to_widget (GtkMenu *menu, g_object_set_data_full (G_OBJECT (attach_widget), I_(ATTACHED_MENUS), list, (GDestroyNotify) g_list_free); - if (gtk_widget_get_state_flags (GTK_WIDGET (menu)) != 0) - gtk_widget_set_state_flags (GTK_WIDGET (menu), 0, TRUE); - /* Attach the widget to the toplevel window. */ gtk_window_set_attached_to (GTK_WINDOW (menu->priv->toplevel), attach_widget); + _gtk_widget_update_parent_muxer (GTK_WIDGET (menu)); + /* Fallback title for menu comes from attach widget */ gtk_menu_update_title (menu); @@ -1277,9 +1313,12 @@ gtk_menu_detach (GtkMenu *menu) g_slice_free (GtkMenuAttachData, data); + _gtk_widget_update_parent_muxer (GTK_WIDGET (menu)); + /* Fallback title for menu comes from attach widget */ gtk_menu_update_title (menu); + g_object_notify (G_OBJECT (menu), "attach-widget"); g_object_unref (menu); } @@ -1392,6 +1431,7 @@ popup_grab_on_window (GdkWindow *window, if (pointer && gdk_device_grab (pointer, window, GDK_OWNERSHIP_WINDOW, TRUE, + GDK_SMOOTH_SCROLL_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_POINTER_MOTION_MASK, @@ -1442,7 +1482,6 @@ popup_grab_on_window (GdkWindow *window, * be used instead. * * Since: 3.0 - * Rename to: gtk_menu_popup */ void gtk_menu_popup_for_device (GtkMenu *menu, @@ -1463,7 +1502,7 @@ gtk_menu_popup_for_device (GtkMenu *menu, GtkMenuShell *menu_shell; gboolean grab_keyboard; GtkWidget *parent_toplevel; - GdkDevice *keyboard, *pointer; + GdkDevice *keyboard, *pointer, *source_device = NULL; g_return_if_fail (GTK_IS_MENU (menu)); g_return_if_fail (device == NULL || GDK_IS_DEVICE (device)); @@ -1600,6 +1639,7 @@ gtk_menu_popup_for_device (GtkMenu *menu, (current_event->type != GDK_ENTER_NOTIFY)) menu_shell->priv->ignore_enter = TRUE; + source_device = gdk_event_get_source_device (current_event); gdk_event_free (current_event); } else @@ -1669,17 +1709,9 @@ gtk_menu_popup_for_device (GtkMenu *menu, gtk_menu_scroll_to (menu, priv->scroll_offset); /* if no item is selected, select the first one */ - if (!menu_shell->priv->active_menu_item) - { - gboolean touchscreen_mode; - - g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu)), - "gtk-touchscreen-mode", &touchscreen_mode, - NULL); - - if (touchscreen_mode) - gtk_menu_shell_select_first (menu_shell, TRUE); - } + if (!menu_shell->priv->active_menu_item && + source_device && gdk_device_get_source (source_device) == GDK_SOURCE_TOUCHSCREEN) + gtk_menu_shell_select_first (menu_shell, TRUE); /* Once everything is set up correctly, map the toplevel */ gtk_widget_show (priv->toplevel); @@ -1703,13 +1735,13 @@ gtk_menu_popup_for_device (GtkMenu *menu, } /** - * gtk_menu_popup: (skip) + * gtk_menu_popup: * @menu: a #GtkMenu * @parent_menu_shell: (allow-none): the menu shell containing the * triggering menu item, or %NULL * @parent_menu_item: (allow-none): the menu item whose activation * triggered the popup, or %NULL - * @func: (allow-none): a user supplied function used to position + * @func: (scope async) (allow-none): a user supplied function used to position * the menu, or %NULL * @data: user supplied data to be passed to @func. * @button: the mouse button which was pressed to initiate the event. @@ -1853,7 +1885,7 @@ gtk_menu_popdown (GtkMenu *menu) * @menu: a #GtkMenu * * Returns the selected menu item from the menu. This is used by the - * #GtkOptionMenu. + * #GtkComboBox. * * Returns: (transfer none): the #GtkMenuItem that was last selected * in the menu. If a selection has not yet been made, the @@ -1898,7 +1930,7 @@ gtk_menu_get_active (GtkMenu *menu) * from 0 to n-1 * * Selects the specified menu item within the menu. This is used by - * the #GtkOptionMenu and should not be used by anyone else. + * the #GtkComboBox and should not be used by anyone else. */ void gtk_menu_set_active (GtkMenu *menu, @@ -1990,7 +2022,7 @@ gtk_menu_real_can_activate_accel (GtkWidget *widget, } /** - * gtk_menu_set_accel_path + * gtk_menu_set_accel_path: * @menu: a valid #GtkMenu * @accel_path: (allow-none): a valid accelerator path * @@ -2033,7 +2065,7 @@ gtk_menu_set_accel_path (GtkMenu *menu, } /** - * gtk_menu_get_accel_path + * gtk_menu_get_accel_path: * @menu: a valid #GtkMenu * * Retrieves the accelerator path set on the menu. @@ -2495,8 +2527,6 @@ gtk_menu_realize (GtkWidget *widget) gint border_width; GtkWidget *child; GList *children; - guint vertical_padding; - guint horizontal_padding; GtkBorder arrow_border, padding; g_return_if_fail (GTK_IS_MENU (widget)); @@ -2521,25 +2551,20 @@ gtk_menu_realize (GtkWidget *widget) window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask); gtk_widget_set_window (widget, window); - gdk_window_set_user_data (window, widget); + gtk_widget_register_window (widget, window); get_menu_padding (widget, &padding); border_width = gtk_container_get_border_width (GTK_CONTAINER (widget)); context = gtk_widget_get_style_context (widget); - gtk_widget_style_get (GTK_WIDGET (menu), - "vertical-padding", &vertical_padding, - "horizontal-padding", &horizontal_padding, - NULL); - gtk_widget_get_allocation (widget, &allocation); - attributes.x = border_width + padding.left + horizontal_padding; - attributes.y = border_width + padding.top + vertical_padding; + attributes.x = border_width + padding.left; + attributes.y = border_width + padding.top; attributes.width = allocation.width - - (2 * (border_width + horizontal_padding)) - padding.left - padding.right; + (2 * border_width) - padding.left - padding.right; attributes.height = allocation.height - - (2 * (border_width + vertical_padding)) - padding.top - padding.bottom; + (2 * border_width) - padding.top - padding.bottom; get_arrows_border (menu, &arrow_border); attributes.y += arrow_border.top; @@ -2551,15 +2576,15 @@ gtk_menu_realize (GtkWidget *widget) priv->view_window = gdk_window_new (window, &attributes, attributes_mask); - gdk_window_set_user_data (priv->view_window, menu); + gtk_widget_register_window (widget, priv->view_window); gtk_widget_get_allocation (widget, &allocation); attributes.x = 0; attributes.y = 0; - attributes.width = allocation.width + (2 * (border_width + horizontal_padding)) + + attributes.width = allocation.width + (2 * border_width) + padding.left + padding.right; - attributes.height = priv->requested_height - (2 * (border_width + vertical_padding)) + + attributes.height = priv->requested_height - (2 * border_width) + padding.top + padding.bottom; attributes.width = MAX (1, attributes.width); @@ -2567,7 +2592,7 @@ gtk_menu_realize (GtkWidget *widget) priv->bin_window = gdk_window_new (priv->view_window, &attributes, attributes_mask); - gdk_window_set_user_data (priv->bin_window, menu); + gtk_widget_register_window (widget, priv->bin_window); children = GTK_MENU_SHELL (menu)->priv->children; while (children) @@ -2623,7 +2648,7 @@ menu_grab_transfer_window_get (GtkMenu *menu) window = gdk_window_new (gtk_widget_get_root_window (GTK_WIDGET (menu)), &attributes, attributes_mask); - gdk_window_set_user_data (window, menu); + gtk_widget_register_window (GTK_WIDGET (menu), window); gdk_window_show (window); @@ -2639,7 +2664,7 @@ menu_grab_transfer_window_destroy (GtkMenu *menu) GdkWindow *window = g_object_get_data (G_OBJECT (menu), "gtk-menu-transfer-window"); if (window) { - gdk_window_set_user_data (window, NULL); + gtk_widget_unregister_window (GTK_WIDGET (menu), window); gdk_window_destroy (window); g_object_set_data (G_OBJECT (menu), I_("gtk-menu-transfer-window"), NULL); } @@ -2653,11 +2678,11 @@ gtk_menu_unrealize (GtkWidget *widget) menu_grab_transfer_window_destroy (menu); - gdk_window_set_user_data (priv->view_window, NULL); + gtk_widget_unregister_window (widget, priv->view_window); gdk_window_destroy (priv->view_window); priv->view_window = NULL; - gdk_window_set_user_data (priv->bin_window, NULL); + gtk_widget_unregister_window (widget, priv->bin_window); gdk_window_destroy (priv->bin_window); priv->bin_window = NULL; @@ -2675,7 +2700,6 @@ calculate_line_heights (GtkMenu *menu, GtkMenuShell *menu_shell; GtkWidget *child, *widget; GList *children; - guint horizontal_padding; guint border_width; guint n_columns; gint n_heights; @@ -2693,13 +2717,10 @@ calculate_line_heights (GtkMenu *menu, n_columns = gtk_menu_get_n_columns (menu); avail_width = for_width - (2 * priv->toggle_size + priv->accel_size) * n_columns; - gtk_widget_style_get (GTK_WIDGET (menu), - "horizontal-padding", &horizontal_padding, - NULL); get_menu_padding (widget, &padding); border_width = gtk_container_get_border_width (GTK_CONTAINER (menu)); - avail_width -= (border_width + horizontal_padding) * 2 + padding.left + padding.right; + avail_width -= (border_width) * 2 + padding.left + padding.right; for (children = menu_shell->priv->children; children; children = children->next) { @@ -2755,8 +2776,6 @@ gtk_menu_size_allocate (GtkWidget *widget, gint x, y, i; gint width, height; guint border_width; - guint vertical_padding; - guint horizontal_padding; GtkBorder padding; g_return_if_fail (GTK_IS_MENU (widget)); @@ -2768,11 +2787,6 @@ gtk_menu_size_allocate (GtkWidget *widget, gtk_widget_set_allocation (widget, allocation); - gtk_widget_style_get (GTK_WIDGET (menu), - "vertical-padding", &vertical_padding, - "horizontal-padding", &horizontal_padding, - NULL); - get_menu_padding (widget, &padding); border_width = gtk_container_get_border_width (GTK_CONTAINER (menu)); @@ -2783,16 +2797,15 @@ gtk_menu_size_allocate (GtkWidget *widget, NULL); /* refresh our cached height request */ - priv->requested_height = (2 * (border_width + vertical_padding)) + - padding.top + padding.bottom; + priv->requested_height = (2 * border_width) + padding.top + padding.bottom; for (i = 0; i < priv->heights_length; i++) priv->requested_height += priv->heights[i]; - x = border_width + padding.left + horizontal_padding; - y = border_width + padding.top + vertical_padding; - width = allocation->width - (2 * (border_width + horizontal_padding)) - + x = border_width + padding.left; + y = border_width + padding.top; + width = allocation->width - (2 * border_width) - padding.left - padding.right; - height = allocation->height - (2 * (border_width + vertical_padding)) - + height = allocation->height - (2 * border_width) - padding.top - padding.bottom; if (menu_shell->priv->active) @@ -2922,22 +2935,18 @@ get_arrows_visible_area (GtkMenu *menu, GtkArrowPlacement arrow_placement; GtkWidget *widget = GTK_WIDGET (menu); guint border_width; - guint vertical_padding; - guint horizontal_padding; gint scroll_arrow_height; GtkBorder menu_padding; gtk_widget_style_get (widget, - "vertical-padding", &vertical_padding, - "horizontal-padding", &horizontal_padding, "scroll-arrow-vlength", &scroll_arrow_height, "arrow-placement", &arrow_placement, NULL); get_menu_padding (widget, &menu_padding); border_width = gtk_container_get_border_width (GTK_CONTAINER (widget)); - border->x = border_width + menu_padding.left + horizontal_padding; - border->y = border_width + menu_padding.top + vertical_padding; + border->x = border_width + menu_padding.left; + border->y = border_width + menu_padding.top; border->width = gdk_window_get_width (gtk_widget_get_window (widget)); border->height = gdk_window_get_height (gtk_widget_get_window (widget)); @@ -3122,7 +3131,6 @@ gtk_menu_get_preferred_width (GtkWidget *widget, GList *children; guint max_toggle_size; guint max_accel_width; - guint horizontal_padding; guint border_width; gint child_min, child_nat; gint min_width, nat_width; @@ -3181,19 +3189,19 @@ gtk_menu_get_preferred_width (GtkWidget *widget, !priv->no_toggle_size) { GtkStyleContext *context; - GtkWidgetPath *menu_path, *check_path; + GtkWidgetPath *check_path; guint toggle_spacing; guint indicator_size; - context = gtk_widget_get_style_context (widget); - menu_path = gtk_widget_path_copy (gtk_style_context_get_path (context)); + context = gtk_style_context_new (); /* Create a GtkCheckMenuItem path, only to query indicator spacing */ - check_path = gtk_widget_path_copy (menu_path); + check_path = _gtk_widget_create_path (widget); gtk_widget_path_append_type (check_path, GTK_TYPE_CHECK_MENU_ITEM); gtk_style_context_set_path (context, check_path); gtk_widget_path_free (check_path); + gtk_style_context_set_screen (context, gtk_widget_get_screen (widget)); gtk_style_context_get_style (context, "toggle-spacing", &toggle_spacing, @@ -3202,9 +3210,7 @@ gtk_menu_get_preferred_width (GtkWidget *widget, max_toggle_size = indicator_size + toggle_spacing; - /* Restore real widget path */ - gtk_style_context_set_path (context, menu_path); - gtk_widget_path_free (menu_path); + g_object_unref (context); } min_width += 2 * max_toggle_size + max_accel_width; @@ -3213,16 +3219,10 @@ gtk_menu_get_preferred_width (GtkWidget *widget, nat_width += 2 * max_toggle_size + max_accel_width; nat_width *= gtk_menu_get_n_columns (menu); - gtk_widget_style_get (GTK_WIDGET (menu), - "horizontal-padding", &horizontal_padding, - NULL); - get_menu_padding (widget, &padding); border_width = gtk_container_get_border_width (GTK_CONTAINER (menu)); - min_width += (2 * (border_width + horizontal_padding)) + - padding.left + padding.right; - nat_width += (2 * (border_width + horizontal_padding)) + - padding.left + padding.right; + min_width += (2 * border_width) + padding.left + padding.right; + nat_width += (2 * border_width) + padding.left + padding.right; priv->toggle_size = max_toggle_size; priv->accel_size = max_accel_width; @@ -3264,16 +3264,14 @@ gtk_menu_get_preferred_height_for_width (GtkWidget *widget, GtkMenu *menu = GTK_MENU (widget); GtkMenuPrivate *priv = menu->priv; guint *min_heights, *nat_heights; - guint vertical_padding, border_width; + guint border_width; gint n_heights, i; gint min_height, nat_height; - gtk_widget_style_get (widget, "vertical-padding", &vertical_padding, NULL); border_width = gtk_container_get_border_width (GTK_CONTAINER (menu)); get_menu_padding (widget, &padding); - min_height = nat_height = (border_width + vertical_padding) * 2 + - padding.top + padding.bottom; + min_height = nat_height = (2 * border_width) + padding.top + padding.bottom; n_heights = calculate_line_heights (menu, for_size, &min_heights, &nat_heights); @@ -3314,34 +3312,6 @@ gtk_menu_get_preferred_height_for_width (GtkWidget *widget, g_free (nat_heights); } - - -static gboolean -gtk_menu_button_scroll (GtkMenu *menu, - GdkEventButton *event) -{ - GtkMenuPrivate *priv = menu->priv; - - if (priv->upper_arrow_prelight || priv->lower_arrow_prelight) - { - gboolean touchscreen_mode; - - g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu)), - "gtk-touchscreen-mode", &touchscreen_mode, - NULL); - - if (touchscreen_mode) - gtk_menu_handle_scrolling (menu, - event->x_root, event->y_root, - event->type == GDK_BUTTON_PRESS, - FALSE); - - return TRUE; - } - - return FALSE; -} - static gboolean pointer_in_menu_window (GtkWidget *widget, gdouble x_root, @@ -3378,13 +3348,16 @@ static gboolean gtk_menu_button_press (GtkWidget *widget, GdkEventButton *event) { + GdkDevice *source_device; + GtkWidget *event_widget; + GtkMenu *menu; + if (event->type != GDK_BUTTON_PRESS) return FALSE; - /* Don't pass down to menu shell for presses over scroll arrows - */ - if (gtk_menu_button_scroll (GTK_MENU (widget), event)) - return TRUE; + source_device = gdk_event_get_source_device ((GdkEvent *) event); + event_widget = gtk_get_event_widget ((GdkEvent *) event); + menu = GTK_MENU (widget); /* Don't pass down to menu shell if a non-menuitem part of the menu * was clicked. The check for the event_widget being a GtkMenuShell @@ -3393,10 +3366,16 @@ gtk_menu_button_press (GtkWidget *widget, * the menu or on its border are delivered relative to * menu_shell->window. */ - if (GTK_IS_MENU_SHELL (gtk_get_event_widget ((GdkEvent *) event)) && + if (GTK_IS_MENU_SHELL (event_widget) && pointer_in_menu_window (widget, event->x_root, event->y_root)) return TRUE; + if (GTK_IS_MENU_ITEM (event_widget) && + gdk_device_get_source (source_device) == GDK_SOURCE_TOUCHSCREEN && + GTK_MENU_ITEM (event_widget)->priv->submenu != NULL && + !gtk_widget_is_drawable (GTK_MENU_ITEM (event_widget)->priv->submenu)) + menu->priv->ignore_button_release = TRUE; + return GTK_WIDGET_CLASS (gtk_menu_parent_class)->button_press_event (widget, event); } @@ -3415,11 +3394,6 @@ gtk_menu_button_release (GtkWidget *widget, if (event->type != GDK_BUTTON_RELEASE) return FALSE; - /* Don't pass down to menu shell for releases over scroll arrows - */ - if (gtk_menu_button_scroll (GTK_MENU (widget), event)) - return TRUE; - /* Don't pass down to menu shell if a non-menuitem part of the menu * was clicked (see comment in button_press()). */ @@ -3663,10 +3637,14 @@ gtk_menu_motion_notify (GtkWidget *widget, GtkMenu *menu; GtkMenuShell *menu_shell; GtkWidget *parent; + GdkDevice *source_device; gboolean need_enter; - if (GTK_IS_MENU (widget)) + source_device = gdk_event_get_source_device ((GdkEvent *) event); + + if (GTK_IS_MENU (widget) && + gdk_device_get_source (source_device) != GDK_SOURCE_TOUCHSCREEN) { GtkMenuPrivate *priv = GTK_MENU(widget)->priv; @@ -3830,90 +3808,17 @@ gtk_menu_scroll_by (GtkMenu *menu, gtk_menu_scroll_to (menu, offset); } -static void -gtk_menu_do_timeout_scroll (GtkMenu *menu, - gboolean touchscreen_mode) -{ - GtkMenuPrivate *priv = menu->priv; - gboolean upper_visible; - gboolean lower_visible; - - upper_visible = priv->upper_arrow_visible; - lower_visible = priv->lower_arrow_visible; - - gtk_menu_scroll_by (menu, priv->scroll_step); - - if (touchscreen_mode && - (upper_visible != priv->upper_arrow_visible || - lower_visible != priv->lower_arrow_visible)) - { - /* We are about to hide a scroll arrow while the mouse is pressed, - * this would cause the uncovered menu item to be activated on button - * release. Therefore we need to ignore button release here - */ - GTK_MENU_SHELL (menu)->priv->ignore_enter = TRUE; - priv->ignore_button_release = TRUE; - } -} - static gboolean gtk_menu_scroll_timeout (gpointer data) { GtkMenu *menu; - gboolean touchscreen_mode; menu = GTK_MENU (data); - - g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu)), - "gtk-touchscreen-mode", &touchscreen_mode, - NULL); - - gtk_menu_do_timeout_scroll (menu, touchscreen_mode); + gtk_menu_scroll_by (menu, menu->priv->scroll_step); return TRUE; } -static gboolean -gtk_menu_scroll_timeout_initial (gpointer data) -{ - GtkMenu *menu; - guint timeout; - gboolean touchscreen_mode; - - menu = GTK_MENU (data); - - g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu)), - "gtk-timeout-repeat", &timeout, - "gtk-touchscreen-mode", &touchscreen_mode, - NULL); - - gtk_menu_do_timeout_scroll (menu, touchscreen_mode); - - gtk_menu_remove_scroll_timeout (menu); - - menu->priv->scroll_timeout = - gdk_threads_add_timeout (timeout, gtk_menu_scroll_timeout, menu); - - return FALSE; -} - -static void -gtk_menu_start_scrolling (GtkMenu *menu) -{ - guint timeout; - gboolean touchscreen_mode; - - g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu)), - "gtk-timeout-repeat", &timeout, - "gtk-touchscreen-mode", &touchscreen_mode, - NULL); - - gtk_menu_do_timeout_scroll (menu, touchscreen_mode); - - menu->priv->scroll_timeout = - gdk_threads_add_timeout (timeout, gtk_menu_scroll_timeout_initial, menu); -} - static gboolean gtk_menu_scroll (GtkWidget *widget, GdkEventScroll *event) @@ -3930,6 +3835,9 @@ gtk_menu_scroll (GtkWidget *widget, case GDK_SCROLL_UP: gtk_menu_scroll_by (menu, - MENU_SCROLL_STEP2); break; + case GDK_SCROLL_SMOOTH: + gtk_menu_scroll_by (menu, event->delta_y); + break; } return TRUE; @@ -3945,7 +3853,6 @@ get_arrows_sensitive_area (GtkMenu *menu, GdkWindow *window; gint width, height; guint border; - guint vertical_padding; gint win_x, win_y; gint scroll_arrow_height; GtkBorder padding; @@ -3955,12 +3862,11 @@ get_arrows_sensitive_area (GtkMenu *menu, height = gdk_window_get_height (window); gtk_widget_style_get (widget, - "vertical-padding", &vertical_padding, "scroll-arrow-vlength", &scroll_arrow_height, "arrow-placement", &arrow_placement, NULL); - border = gtk_container_get_border_width (GTK_CONTAINER (menu)) + vertical_padding; + border = gtk_container_get_border_width (GTK_CONTAINER (menu)); get_menu_padding (widget, &padding); gdk_window_get_position (window, &win_x, &win_y); @@ -4037,14 +3943,9 @@ gtk_menu_handle_scrolling (GtkMenu *menu, gboolean in_arrow; gboolean scroll_fast = FALSE; gint top_x, top_y; - gboolean touchscreen_mode; menu_shell = GTK_MENU_SHELL (menu); - g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu)), - "gtk-touchscreen-mode", &touchscreen_mode, - NULL); - gdk_window_get_position (gtk_widget_get_window (priv->toplevel), &top_x, &top_y); x -= top_x; @@ -4062,82 +3963,44 @@ gtk_menu_handle_scrolling (GtkMenu *menu, in_arrow = TRUE; } - if (touchscreen_mode) - priv->upper_arrow_prelight = in_arrow; - if ((priv->upper_arrow_state & GTK_STATE_FLAG_INSENSITIVE) == 0) { gboolean arrow_pressed = FALSE; if (priv->upper_arrow_visible && !priv->tearoff_active) { - if (touchscreen_mode) + scroll_fast = (y < rect.y + MENU_SCROLL_FAST_ZONE); + + if (enter && in_arrow && + (!priv->upper_arrow_prelight || + priv->scroll_fast != scroll_fast)) { - if (enter && priv->upper_arrow_prelight) - { - if (priv->scroll_timeout == 0) - { - /* Deselect the active item so that - * any submenus are popped down - */ - gtk_menu_shell_deselect (menu_shell); - - gtk_menu_remove_scroll_timeout (menu); - priv->scroll_step = -MENU_SCROLL_STEP2; /* always fast */ - - if (!motion) - { - /* Only do stuff on click. */ - gtk_menu_start_scrolling (menu); - arrow_pressed = TRUE; - } - } - else - { - arrow_pressed = TRUE; - } - } - else if (!enter) - { - gtk_menu_stop_scrolling (menu); - } + priv->upper_arrow_prelight = TRUE; + priv->scroll_fast = scroll_fast; + + /* Deselect the active item so that + * any submenus are popped down + */ + gtk_menu_shell_deselect (menu_shell); + + gtk_menu_remove_scroll_timeout (menu); + priv->scroll_step = scroll_fast + ? -MENU_SCROLL_STEP2 + : -MENU_SCROLL_STEP1; + + priv->scroll_timeout = + gdk_threads_add_timeout (scroll_fast + ? MENU_SCROLL_TIMEOUT2 + : MENU_SCROLL_TIMEOUT1, + gtk_menu_scroll_timeout, menu); } - else /* !touchscreen_mode */ + else if (!enter && !in_arrow && priv->upper_arrow_prelight) { - scroll_fast = (y < rect.y + MENU_SCROLL_FAST_ZONE); - - if (enter && in_arrow && - (!priv->upper_arrow_prelight || - priv->scroll_fast != scroll_fast)) - { - priv->upper_arrow_prelight = TRUE; - priv->scroll_fast = scroll_fast; - - /* Deselect the active item so that - * any submenus are popped down - */ - gtk_menu_shell_deselect (menu_shell); - - gtk_menu_remove_scroll_timeout (menu); - priv->scroll_step = scroll_fast - ? -MENU_SCROLL_STEP2 - : -MENU_SCROLL_STEP1; - - priv->scroll_timeout = - gdk_threads_add_timeout (scroll_fast - ? MENU_SCROLL_TIMEOUT2 - : MENU_SCROLL_TIMEOUT1, - gtk_menu_scroll_timeout, menu); - } - else if (!enter && !in_arrow && priv->upper_arrow_prelight) - { - gtk_menu_stop_scrolling (menu); - } + gtk_menu_stop_scrolling (menu); } } - /* gtk_menu_start_scrolling() might have hit the top of the - * menu, so check if the button isn't insensitive before + /* check if the button isn't insensitive before * changing it to something else. */ if ((priv->upper_arrow_state & GTK_STATE_FLAG_INSENSITIVE) == 0) @@ -4172,82 +4035,44 @@ gtk_menu_handle_scrolling (GtkMenu *menu, in_arrow = TRUE; } - if (touchscreen_mode) - priv->lower_arrow_prelight = in_arrow; - if ((priv->lower_arrow_state & GTK_STATE_FLAG_INSENSITIVE) == 0) { gboolean arrow_pressed = FALSE; if (priv->lower_arrow_visible && !priv->tearoff_active) { - if (touchscreen_mode) + scroll_fast = (y > rect.y + rect.height - MENU_SCROLL_FAST_ZONE); + + if (enter && in_arrow && + (!priv->lower_arrow_prelight || + priv->scroll_fast != scroll_fast)) { - if (enter && priv->lower_arrow_prelight) - { - if (priv->scroll_timeout == 0) - { - /* Deselect the active item so that - * any submenus are popped down - */ - gtk_menu_shell_deselect (menu_shell); - - gtk_menu_remove_scroll_timeout (menu); - priv->scroll_step = MENU_SCROLL_STEP2; /* always fast */ - - if (!motion) - { - /* Only do stuff on click. */ - gtk_menu_start_scrolling (menu); - arrow_pressed = TRUE; - } - } - else - { - arrow_pressed = TRUE; - } - } - else if (!enter) - { - gtk_menu_stop_scrolling (menu); - } + priv->lower_arrow_prelight = TRUE; + priv->scroll_fast = scroll_fast; + + /* Deselect the active item so that + * any submenus are popped down + */ + gtk_menu_shell_deselect (menu_shell); + + gtk_menu_remove_scroll_timeout (menu); + priv->scroll_step = scroll_fast + ? MENU_SCROLL_STEP2 + : MENU_SCROLL_STEP1; + + priv->scroll_timeout = + gdk_threads_add_timeout (scroll_fast + ? MENU_SCROLL_TIMEOUT2 + : MENU_SCROLL_TIMEOUT1, + gtk_menu_scroll_timeout, menu); } - else /* !touchscreen_mode */ + else if (!enter && !in_arrow && priv->lower_arrow_prelight) { - scroll_fast = (y > rect.y + rect.height - MENU_SCROLL_FAST_ZONE); - - if (enter && in_arrow && - (!priv->lower_arrow_prelight || - priv->scroll_fast != scroll_fast)) - { - priv->lower_arrow_prelight = TRUE; - priv->scroll_fast = scroll_fast; - - /* Deselect the active item so that - * any submenus are popped down - */ - gtk_menu_shell_deselect (menu_shell); - - gtk_menu_remove_scroll_timeout (menu); - priv->scroll_step = scroll_fast - ? MENU_SCROLL_STEP2 - : MENU_SCROLL_STEP1; - - priv->scroll_timeout = - gdk_threads_add_timeout (scroll_fast - ? MENU_SCROLL_TIMEOUT2 - : MENU_SCROLL_TIMEOUT1, - gtk_menu_scroll_timeout, menu); - } - else if (!enter && !in_arrow && priv->lower_arrow_prelight) - { - gtk_menu_stop_scrolling (menu); - } + gtk_menu_stop_scrolling (menu); } } - /* gtk_menu_start_scrolling() might have hit the bottom of the - * menu, so check if the button isn't insensitive before + /* check if the button isn't insensitive before * changing it to something else. */ if ((priv->lower_arrow_state & GTK_STATE_FLAG_INSENSITIVE) == 0) @@ -4277,19 +4102,18 @@ gtk_menu_enter_notify (GtkWidget *widget, { GtkWidget *menu_item; GtkWidget *parent; - gboolean touchscreen_mode; + GdkDevice *source_device; if (event->mode == GDK_CROSSING_GTK_GRAB || event->mode == GDK_CROSSING_GTK_UNGRAB || event->mode == GDK_CROSSING_STATE_CHANGED) return TRUE; - g_object_get (gtk_widget_get_settings (widget), - "gtk-touchscreen-mode", &touchscreen_mode, - NULL); - + source_device = gdk_event_get_source_device ((GdkEvent *) event); menu_item = gtk_get_event_widget ((GdkEvent*) event); - if (GTK_IS_MENU (widget)) + + if (GTK_IS_MENU (widget) && + gdk_device_get_source (source_device) != GDK_SOURCE_TOUCHSCREEN) { GtkMenuShell *menu_shell = GTK_MENU_SHELL (widget); @@ -4298,7 +4122,8 @@ gtk_menu_enter_notify (GtkWidget *widget, event->x_root, event->y_root, TRUE, TRUE); } - if (!touchscreen_mode && GTK_IS_MENU_ITEM (menu_item)) + if (gdk_device_get_source (source_device) != GDK_SOURCE_TOUCHSCREEN && + GTK_IS_MENU_ITEM (menu_item)) { GtkWidget *menu = gtk_widget_get_parent (menu_item); @@ -4355,6 +4180,7 @@ gtk_menu_leave_notify (GtkWidget *widget, GtkMenu *menu; GtkMenuItem *menu_item; GtkWidget *event_widget; + GdkDevice *source_device; if (event->mode == GDK_CROSSING_GTK_GRAB || event->mode == GDK_CROSSING_GTK_UNGRAB || @@ -4367,7 +4193,10 @@ gtk_menu_leave_notify (GtkWidget *widget, 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, TRUE); + source_device = gdk_event_get_source_device ((GdkEvent *) event); + + if (gdk_device_get_source (source_device) != GDK_SOURCE_TOUCHSCREEN) + gtk_menu_handle_scrolling (menu, event->x_root, event->y_root, FALSE, TRUE); event_widget = gtk_get_event_widget ((GdkEvent*) event); @@ -4402,6 +4231,144 @@ gtk_menu_leave_notify (GtkWidget *widget, return GTK_WIDGET_CLASS (gtk_menu_parent_class)->leave_notify_event (widget, event); } +static gboolean +pointer_on_menu_widget (GtkMenu *menu, + gdouble x_root, + gdouble y_root) +{ + GtkMenuPrivate *priv = menu->priv; + GtkAllocation allocation; + gint window_x, window_y; + + gtk_widget_get_allocation (GTK_WIDGET (menu), &allocation); + gdk_window_get_position (gtk_widget_get_window (priv->toplevel), + &window_x, &window_y); + + if (x_root >= window_x && x_root < window_x + allocation.width && + y_root >= window_y && y_root < window_y + allocation.height) + return TRUE; + + return FALSE; +} + +static gboolean +gtk_menu_captured_event (GtkWidget *widget, + GdkEvent *event) +{ + GdkDevice *source_device; + gboolean retval = FALSE; + GtkMenuPrivate *priv; + GtkMenu *menu; + gdouble x_root, y_root; + guint button; + GdkModifierType state; + + menu = GTK_MENU (widget); + priv = menu->priv; + + if (!priv->upper_arrow_visible && !priv->lower_arrow_visible && priv->drag_start_y < 0) + return retval; + + source_device = gdk_event_get_source_device (event); + gdk_event_get_root_coords (event, &x_root, &y_root); + + switch (event->type) + { + case GDK_TOUCH_BEGIN: + case GDK_BUTTON_PRESS: + if ((!gdk_event_get_button (event, &button) || button == 1) && + gdk_device_get_source (source_device) == GDK_SOURCE_TOUCHSCREEN && + pointer_on_menu_widget (menu, x_root, y_root)) + { + priv->drag_start_y = event->button.y_root; + priv->initial_drag_offset = priv->scroll_offset; + priv->drag_scroll_started = FALSE; + } + else + priv->drag_start_y = -1; + + priv->drag_already_pressed = TRUE; + break; + case GDK_TOUCH_END: + case GDK_BUTTON_RELEASE: + if (priv->drag_scroll_started) + { + priv->drag_scroll_started = FALSE; + priv->drag_start_y = -1; + priv->drag_already_pressed = FALSE; + retval = TRUE; + } + break; + case GDK_TOUCH_UPDATE: + case GDK_MOTION_NOTIFY: + if ((!gdk_event_get_state (event, &state) || (state & GDK_BUTTON1_MASK) != 0) && + gdk_device_get_source (source_device) == GDK_SOURCE_TOUCHSCREEN) + { + if (!priv->drag_already_pressed) + { + if (pointer_on_menu_widget (menu, x_root, y_root)) + { + priv->drag_start_y = y_root; + priv->initial_drag_offset = priv->scroll_offset; + priv->drag_scroll_started = FALSE; + } + else + priv->drag_start_y = -1; + + priv->drag_already_pressed = TRUE; + } + + if (priv->drag_start_y < 0 && !priv->drag_scroll_started) + break; + + if (priv->drag_scroll_started) + { + gint offset, view_height; + GtkBorder arrow_border; + gdouble y_diff; + + y_diff = y_root - priv->drag_start_y; + offset = priv->initial_drag_offset - y_diff; + + view_height = gdk_window_get_height (gtk_widget_get_window (widget)); + get_arrows_border (menu, &arrow_border); + + if (priv->upper_arrow_visible) + view_height -= arrow_border.top; + + if (priv->lower_arrow_visible) + view_height -= arrow_border.bottom; + + offset = CLAMP (offset, + MIN (priv->scroll_offset, 0), + MAX (priv->scroll_offset, priv->requested_height - view_height)); + + gtk_menu_scroll_to (menu, offset); + + retval = TRUE; + } + else if (gtk_drag_check_threshold (widget, + 0, priv->drag_start_y, + 0, y_root)) + { + priv->drag_scroll_started = TRUE; + gtk_menu_shell_deselect (GTK_MENU_SHELL (menu)); + retval = TRUE; + } + } + break; + case GDK_ENTER_NOTIFY: + case GDK_LEAVE_NOTIFY: + if (priv->drag_scroll_started) + retval = TRUE; + break; + default: + break; + } + + return retval; +} + static void gtk_menu_stop_navigating_submenu (GtkMenu *menu) { @@ -4844,19 +4811,10 @@ static void gtk_menu_stop_scrolling (GtkMenu *menu) { GtkMenuPrivate *priv = menu->priv; - gboolean touchscreen_mode; gtk_menu_remove_scroll_timeout (menu); - - g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu)), - "gtk-touchscreen-mode", &touchscreen_mode, - NULL); - - if (!touchscreen_mode) - { - priv->upper_arrow_prelight = FALSE; - priv->lower_arrow_prelight = FALSE; - } + priv->upper_arrow_prelight = FALSE; + priv->lower_arrow_prelight = FALSE; } static void @@ -4870,8 +4828,6 @@ gtk_menu_scroll_to (GtkMenu *menu, gint view_width, view_height; gint border_width; gint menu_height; - guint vertical_padding; - guint horizontal_padding; gboolean double_arrows; widget = GTK_WIDGET (menu); @@ -4883,23 +4839,17 @@ gtk_menu_scroll_to (GtkMenu *menu, view_width = gtk_widget_get_allocated_width (widget); view_height = gtk_widget_get_allocated_height (widget); - gtk_widget_style_get (GTK_WIDGET (menu), - "vertical-padding", &vertical_padding, - "horizontal-padding", &horizontal_padding, - NULL); - get_menu_padding (widget, &padding); double_arrows = get_double_arrows (menu); border_width = gtk_container_get_border_width (GTK_CONTAINER (menu)); - view_width -= (2 * (border_width + horizontal_padding)) + padding.left + padding.right; - view_height -= (2 * (border_width + vertical_padding)) + padding.top + padding.bottom; - menu_height = priv->requested_height - (2 * (border_width + vertical_padding)) - - padding.top - padding.bottom; + view_width -= (2 * border_width) + padding.left + padding.right; + view_height -= (2 * border_width) + padding.top + padding.bottom; + menu_height = priv->requested_height - (2 * border_width) - padding.top - padding.bottom; - x = border_width + padding.left + horizontal_padding; - y = border_width + padding.top + vertical_padding; + x = border_width + padding.left; + y = border_width + padding.top; if (double_arrows && !priv->tearoff_active) { @@ -5030,10 +4980,10 @@ gtk_menu_scroll_to (GtkMenu *menu, /* Scroll the menu: */ if (gtk_widget_get_realized (widget)) - gdk_window_move (priv->bin_window, 0, -offset); - - if (gtk_widget_get_realized (widget)) - gdk_window_move_resize (priv->view_window, x, y, view_width, view_height); + { + gdk_window_move (priv->bin_window, 0, -offset); + gdk_window_move_resize (priv->view_window, x, y, view_width, view_height); + } priv->scroll_offset = offset; } @@ -5096,23 +5046,18 @@ gtk_menu_scroll_item_visible (GtkMenuShell *menu_shell, if (compute_child_offset (menu, menu_item, &child_offset, &child_height, &last_child)) { - guint vertical_padding; gboolean double_arrows; GtkBorder padding; y = priv->scroll_offset; height = gdk_window_get_height (gtk_widget_get_window (widget)); - gtk_widget_style_get (widget, - "vertical-padding", &vertical_padding, - NULL); - double_arrows = get_double_arrows (menu); get_menu_padding (widget, &padding); height -= 2 * gtk_container_get_border_width (GTK_CONTAINER (menu)) + - padding.top + padding.bottom + - 2 * vertical_padding; + padding.top + padding.bottom; + if (child_offset < y) { /* Ignore the enter event we might get if the pointer @@ -5560,15 +5505,13 @@ static gint get_menu_height (GtkMenu *menu) { GtkMenuPrivate *priv = menu->priv; - GtkAllocation allocation; GtkWidget *widget = GTK_WIDGET (menu); GtkBorder padding; gint height; - gtk_widget_get_allocation (widget, &allocation); get_menu_padding (widget, &padding); - height = allocation.height; + height = priv->requested_height; height -= (gtk_container_get_border_width (GTK_CONTAINER (widget)) * 2) + padding.top + padding.bottom; @@ -5632,6 +5575,7 @@ gtk_menu_real_move_scroll (GtkMenu *menu, GtkWidget *new_child; gboolean new_upper_arrow_visible = priv->upper_arrow_visible && !priv->tearoff_active; GtkBorder arrow_border; + get_arrows_border (menu, &arrow_border); if (priv->scroll_offset != old_offset) @@ -5648,13 +5592,11 @@ gtk_menu_real_move_scroll (GtkMenu *menu, case GTK_SCROLL_START: /* Ignore the enter event we might get if the pointer is on the menu */ menu_shell->priv->ignore_enter = TRUE; - gtk_menu_scroll_to (menu, 0); gtk_menu_shell_select_first (menu_shell, TRUE); break; case GTK_SCROLL_END: /* Ignore the enter event we might get if the pointer is on the menu */ menu_shell->priv->ignore_enter = TRUE; - gtk_menu_scroll_to (menu, end_position - page_size); _gtk_menu_shell_select_last (menu_shell, TRUE); break; default: @@ -5662,7 +5604,6 @@ gtk_menu_real_move_scroll (GtkMenu *menu, } } - /** * gtk_menu_set_monitor: * @menu: a #GtkMenu @@ -5739,11 +5680,13 @@ static void gtk_menu_grab_notify (GtkWidget *widget, gboolean was_grabbed) { + GtkMenu *menu; GtkWidget *toplevel; GtkWindowGroup *group; GtkWidget *grab; GdkDevice *pointer; + menu = GTK_MENU (widget); pointer = _gtk_menu_shell_get_grab_device (GTK_MENU_SHELL (widget)); if (!pointer || @@ -5760,6 +5703,8 @@ gtk_menu_grab_notify (GtkWidget *widget, if (GTK_MENU_SHELL (widget)->priv->active && !GTK_IS_MENU_SHELL (grab)) gtk_menu_shell_cancel (GTK_MENU_SHELL (widget)); + + menu->priv->drag_scroll_started = FALSE; } /** @@ -5809,3 +5754,32 @@ gtk_menu_get_reserve_toggle_size (GtkMenu *menu) return !menu->priv->no_toggle_size; } + +/** + * gtk_menu_new_from_model: + * @model: a #GMenuModel + * + * Creates a #GtkMenu and populates it with menu items and + * submenus according to @model. + * + * The created menu items are connected to actions found in the + * #GtkApplicationWindow to which the menu belongs - typically + * by means of being attached to a widget (see gtk_menu_attach_to_widget()) + * that is contained within the #GtkApplicationWindows widget hierarchy. + * + * Returns: a new #GtkMenu + * + * Since: 3.4 + */ +GtkWidget * +gtk_menu_new_from_model (GMenuModel *model) +{ + GtkWidget *menu; + + g_return_val_if_fail (G_IS_MENU_MODEL (model), NULL); + + menu = gtk_menu_new (); + gtk_menu_shell_bind_model (GTK_MENU_SHELL (menu), model, NULL, TRUE); + + return menu; +}