X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;f=gtk%2Fgtkmenu.c;h=a4d46051a41bd6b5c3e5949eaff21b897742f4c7;hb=92ededc9b4160898de6c2b920102716f359c3def;hp=9715b78fd83c819c2a2172c56840a7e9f848d2a7;hpb=404f4c5d40c2ebf31da41144154abbc27de642a5;p=~andy%2Fgtk diff --git a/gtk/gtkmenu.c b/gtk/gtkmenu.c index 9715b78fd..a4d46051a 100644 --- a/gtk/gtkmenu.c +++ b/gtk/gtkmenu.c @@ -24,7 +24,6 @@ * GTK+ at ftp://ftp.gtk.org/pub/gtk/. */ -#define GTK_MENU_INTERNALS #include "config.h" #include #include "gdk/gdkkeysyms.h" @@ -36,15 +35,15 @@ #include "gtkmain.h" #include "gtkmarshalers.h" #include "gtkmenu.h" +#include "gtkmenuprivate.h" #include "gtktearoffmenuitem.h" #include "gtkwindow.h" #include "gtkhbox.h" #include "gtkvscrollbar.h" #include "gtksettings.h" +#include "gtksizerequest.h" #include "gtkprivate.h" #include "gtkintl.h" -#include "gtkalias.h" - #define DEFAULT_POPUP_DELAY 225 #define DEFAULT_POPDOWN_DELAY 1000 @@ -78,9 +77,12 @@ struct _GtkMenuPrivate gint y; gboolean initially_pushed_in; + GDestroyNotify position_func_data_destroy; + /* info used for the table */ guint *heights; gint heights_length; + gint requested_height; gint monitor_num; @@ -88,12 +90,20 @@ struct _GtkMenuPrivate gint n_rows; gint n_columns; + guint accel_size; + gchar *title; /* Arrow states */ GtkStateType lower_arrow_state; GtkStateType upper_arrow_state; + /* navigation region */ + int navigation_x; + int navigation_y; + int navigation_width; + int navigation_height; + guint have_layout : 1; guint seen_item_enter : 1; guint have_position : 1; @@ -165,8 +175,6 @@ static void gtk_menu_get_child_property(GtkContainer *container, static void gtk_menu_destroy (GtkObject *object); static void gtk_menu_realize (GtkWidget *widget); static void gtk_menu_unrealize (GtkWidget *widget); -static void gtk_menu_size_request (GtkWidget *widget, - GtkRequisition *requisition); static void gtk_menu_size_allocate (GtkWidget *widget, GtkAllocation *allocation); static void gtk_menu_paint (GtkWidget *widget, @@ -254,6 +262,19 @@ static gboolean gtk_menu_real_can_activate_accel (GtkWidget *widget, static void _gtk_menu_refresh_accel_paths (GtkMenu *menu, gboolean group_changed); +static void gtk_menu_size_request_init (GtkSizeRequestIface *iface); +static void gtk_menu_get_width (GtkSizeRequest *widget, + gint *minimum_size, + gint *natural_size); +static void gtk_menu_get_height (GtkSizeRequest *widget, + gint *minimum_size, + gint *natural_size); +static void gtk_menu_get_height_for_width (GtkSizeRequest *widget, + gint for_size, + gint *minimum_size, + gint *natural_size); + + static const gchar attach_data_key[] = "gtk-menu-attach-data"; static guint menu_signals[LAST_SIGNAL] = { 0 }; @@ -264,7 +285,9 @@ gtk_menu_get_private (GtkMenu *menu) return G_TYPE_INSTANCE_GET_PRIVATE (menu, GTK_TYPE_MENU, GtkMenuPrivate); } -G_DEFINE_TYPE (GtkMenu, gtk_menu, GTK_TYPE_MENU_SHELL) +G_DEFINE_TYPE_WITH_CODE (GtkMenu, gtk_menu, GTK_TYPE_MENU_SHELL, + G_IMPLEMENT_INTERFACE (GTK_TYPE_SIZE_REQUEST, + gtk_menu_size_request_init)) static void menu_queue_resize (GtkMenu *menu) @@ -420,7 +443,7 @@ get_effective_child_attach (GtkWidget *child, int *t, int *b) { - GtkMenu *menu = GTK_MENU (child->parent); + GtkMenu *menu = GTK_MENU (gtk_widget_get_parent (child)); AttachInfo *ai; menu_ensure_layout (menu); @@ -455,7 +478,6 @@ gtk_menu_class_init (GtkMenuClass *class) widget_class->realize = gtk_menu_realize; widget_class->unrealize = gtk_menu_unrealize; - widget_class->size_request = gtk_menu_size_request; widget_class->size_allocate = gtk_menu_size_allocate; widget_class->show = gtk_menu_show; widget_class->expose_event = gtk_menu_expose; @@ -722,82 +744,82 @@ gtk_menu_class_init (GtkMenuClass *class) binding_set = gtk_binding_set_by_class (class); gtk_binding_entry_add_signal (binding_set, - GDK_Up, 0, + GDK_KEY_Up, 0, I_("move-current"), 1, GTK_TYPE_MENU_DIRECTION_TYPE, GTK_MENU_DIR_PREV); gtk_binding_entry_add_signal (binding_set, - GDK_KP_Up, 0, + GDK_KEY_KP_Up, 0, "move-current", 1, GTK_TYPE_MENU_DIRECTION_TYPE, GTK_MENU_DIR_PREV); gtk_binding_entry_add_signal (binding_set, - GDK_Down, 0, + GDK_KEY_Down, 0, "move-current", 1, GTK_TYPE_MENU_DIRECTION_TYPE, GTK_MENU_DIR_NEXT); gtk_binding_entry_add_signal (binding_set, - GDK_KP_Down, 0, + GDK_KEY_KP_Down, 0, "move-current", 1, GTK_TYPE_MENU_DIRECTION_TYPE, GTK_MENU_DIR_NEXT); gtk_binding_entry_add_signal (binding_set, - GDK_Left, 0, + GDK_KEY_Left, 0, "move-current", 1, GTK_TYPE_MENU_DIRECTION_TYPE, GTK_MENU_DIR_PARENT); gtk_binding_entry_add_signal (binding_set, - GDK_KP_Left, 0, + GDK_KEY_KP_Left, 0, "move-current", 1, GTK_TYPE_MENU_DIRECTION_TYPE, GTK_MENU_DIR_PARENT); gtk_binding_entry_add_signal (binding_set, - GDK_Right, 0, + GDK_KEY_Right, 0, "move-current", 1, GTK_TYPE_MENU_DIRECTION_TYPE, GTK_MENU_DIR_CHILD); gtk_binding_entry_add_signal (binding_set, - GDK_KP_Right, 0, + GDK_KEY_KP_Right, 0, "move-current", 1, GTK_TYPE_MENU_DIRECTION_TYPE, GTK_MENU_DIR_CHILD); gtk_binding_entry_add_signal (binding_set, - GDK_Home, 0, + GDK_KEY_Home, 0, "move-scroll", 1, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_START); gtk_binding_entry_add_signal (binding_set, - GDK_KP_Home, 0, + GDK_KEY_KP_Home, 0, "move-scroll", 1, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_START); gtk_binding_entry_add_signal (binding_set, - GDK_End, 0, + GDK_KEY_End, 0, "move-scroll", 1, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_END); gtk_binding_entry_add_signal (binding_set, - GDK_KP_End, 0, + GDK_KEY_KP_End, 0, "move-scroll", 1, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_END); gtk_binding_entry_add_signal (binding_set, - GDK_Page_Up, 0, + GDK_KEY_Page_Up, 0, "move-scroll", 1, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_UP); gtk_binding_entry_add_signal (binding_set, - GDK_KP_Page_Up, 0, + GDK_KEY_KP_Page_Up, 0, "move-scroll", 1, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_UP); gtk_binding_entry_add_signal (binding_set, - GDK_Page_Down, 0, + GDK_KEY_Page_Down, 0, "move-scroll", 1, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_DOWN); gtk_binding_entry_add_signal (binding_set, - GDK_KP_Page_Down, 0, + GDK_KEY_KP_Page_Down, 0, "move-scroll", 1, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_PAGE_DOWN); @@ -1007,28 +1029,6 @@ gtk_menu_window_event (GtkWidget *window, return handled; } -static void -gtk_menu_window_size_request (GtkWidget *window, - GtkRequisition *requisition, - GtkMenu *menu) -{ - GtkMenuPrivate *private = gtk_menu_get_private (menu); - - if (private->have_position) - { - GdkScreen *screen = gtk_widget_get_screen (window); - GdkRectangle monitor; - - gdk_screen_get_monitor_geometry (screen, private->monitor_num, &monitor); - - if (private->y + requisition->height > monitor.y + monitor.height) - requisition->height = monitor.y + monitor.height - private->y; - - if (private->y < monitor.y) - requisition->height -= monitor.y - private->y; - } -} - static void gtk_menu_init (GtkMenu *menu) { @@ -1046,7 +1046,6 @@ gtk_menu_init (GtkMenu *menu) "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); @@ -1139,6 +1138,13 @@ gtk_menu_destroy (GtkObject *object) priv->title = NULL; } + if (priv->position_func_data_destroy) + { + priv->position_func_data_destroy (menu->position_func_data); + menu->position_func_data = NULL; + priv->position_func_data_destroy = NULL; + } + GTK_OBJECT_CLASS (gtk_menu_parent_class)->destroy (object); } @@ -1226,6 +1232,8 @@ gtk_menu_attach_to_widget (GtkMenu *menu, /* Fallback title for menu comes from attach widget */ gtk_menu_update_title (menu); + + g_object_notify (G_OBJECT (menu), "attach-widget"); } GtkWidget* @@ -1344,33 +1352,34 @@ gtk_menu_tearoff_bg_copy (GtkMenu *menu) if (menu->torn_off) { GdkPixmap *pixmap; - GdkGC *gc; - GdkGCValues gc_values; + GdkWindow *window; + cairo_t *cr; menu->tearoff_active = FALSE; menu->saved_scroll_offset = menu->scroll_offset; - - gc_values.subwindow_mode = GDK_INCLUDE_INFERIORS; - gc = gdk_gc_new_with_values (widget->window, - &gc_values, GDK_GC_SUBWINDOW); - - gdk_drawable_get_size (menu->tearoff_window->window, &width, &height); - - pixmap = gdk_pixmap_new (menu->tearoff_window->window, + + window = gtk_widget_get_window (menu->tearoff_window); + + gdk_drawable_get_size (window, &width, &height); + + pixmap = gdk_pixmap_new (window, width, height, -1); - gdk_draw_drawable (pixmap, gc, - menu->tearoff_window->window, - 0, 0, 0, 0, -1, -1); - g_object_unref (gc); + cr = gdk_cairo_create (pixmap); + /* Let's hope that function never notices we're not passing it a pixmap */ + gdk_cairo_set_source_pixmap (cr, + window, + 0, 0); + cairo_paint (cr); + cairo_destroy (cr); gtk_widget_set_size_request (menu->tearoff_window, width, height); - gdk_window_set_back_pixmap (menu->tearoff_window->window, pixmap, FALSE); + gdk_window_set_back_pixmap (window, pixmap, FALSE); g_object_unref (pixmap); } } @@ -1409,18 +1418,24 @@ popup_grab_on_window (GdkWindow *window, * gtk_menu_popup_for_device: * @menu: a #GtkMenu. * @device: (allow-none): a #GdkDevice - * @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 the menu, or %NULL - * @data: (allow-none): user supplied data to be passed to @func. - * @button: the mouse button which was pressed to initiate the event. - * @activate_time: the time at which the activation event occurred. + * @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 the menu, + * or %NULL + * @data: (allow-none): user supplied data to be passed to @func + * @destroy: (allow-none): destroy notify for @data + * @button: the mouse button which was pressed to initiate the event + * @activate_time: the time at which the activation event occurred * - * Displays a menu and makes it available for selection. Applications can use - * this function to display context-sensitive menus, and will typically supply - * %NULL for the @parent_menu_shell, @parent_menu_item, @func and @data - * parameters. The default menu positioning function will position the menu - * at the current position of @device (or its corresponding pointer). + * Displays a menu and makes it available for selection. + * + * Applications can use this function to display context-sensitive menus, + * and will typically supply %NULL for the @parent_menu_shell, + * @parent_menu_item, @func, @data and @destroy parameters. The default + * menu positioning function will position the menu at the current position + * of @device (or its corresponding pointer). * * The @button parameter should be the mouse button pressed to initiate * the menu popup. If the menu popup was initiated by something other than @@ -1443,6 +1458,7 @@ gtk_menu_popup_for_device (GtkMenu *menu, GtkWidget *parent_menu_item, GtkMenuPositionFunc func, gpointer data, + GDestroyNotify destroy, guint button, guint32 activate_time) { @@ -1457,7 +1473,25 @@ gtk_menu_popup_for_device (GtkMenu *menu, GdkDevice *keyboard, *pointer; g_return_if_fail (GTK_IS_MENU (menu)); - g_return_if_fail (GDK_IS_DEVICE (device)); + g_return_if_fail (device == NULL || GDK_IS_DEVICE (device)); + + if (device == NULL) + device = gtk_get_current_event_device (); + + if (device == NULL) + { + GdkDisplay *display; + GdkDeviceManager *device_manager; + GList *devices; + + display = gtk_widget_get_display (GTK_WIDGET (menu)); + device_manager = gdk_display_get_device_manager (display); + devices = gdk_device_manager_list_devices (device_manager, GDK_DEVICE_TYPE_MASTER); + + device = devices->data; + + g_list_free (devices); + } widget = GTK_WIDGET (menu); menu_shell = GTK_MENU_SHELL (menu); @@ -1494,7 +1528,7 @@ gtk_menu_popup_for_device (GtkMenu *menu, viewable = FALSE; break; } - tmp = tmp->parent; + tmp = gtk_widget_get_parent (tmp); } if (viewable) @@ -1526,7 +1560,7 @@ gtk_menu_popup_for_device (GtkMenu *menu, if (xgrab_shell && xgrab_shell != widget) { - if (popup_grab_on_window (xgrab_shell->window, keyboard, pointer, activate_time)) + if (popup_grab_on_window (gtk_widget_get_window (xgrab_shell), keyboard, pointer, activate_time)) { _gtk_menu_shell_set_grab_device (GTK_MENU_SHELL (xgrab_shell), pointer); GTK_MENU_SHELL (xgrab_shell)->have_xgrab = TRUE; @@ -1602,6 +1636,7 @@ gtk_menu_popup_for_device (GtkMenu *menu, menu->parent_menu_item = parent_menu_item; menu->position_func = func; menu->position_func_data = data; + priv->position_func_data_destroy = destroy; menu_shell->activate_time = activate_time; /* We need to show the menu here rather in the init function because @@ -1621,7 +1656,10 @@ gtk_menu_popup_for_device (GtkMenu *menu, GtkRequisition tmp_request; GtkAllocation tmp_allocation = { 0, }; - gtk_widget_size_request (menu->toplevel, &tmp_request); + /* Instead of trusting the menu position function to queue a resize when the + * menu goes out of bounds, invalidate the cached size here. */ + gtk_widget_queue_resize (GTK_WIDGET (menu)); + gtk_size_request_get_size (GTK_SIZE_REQUEST (menu->toplevel), &tmp_request, NULL); tmp_allocation.width = tmp_request.width; tmp_allocation.height = tmp_request.height; @@ -1652,7 +1690,7 @@ gtk_menu_popup_for_device (GtkMenu *menu, gtk_widget_show (menu->toplevel); if (xgrab_shell == widget) - popup_grab_on_window (widget->window, keyboard, pointer, activate_time); /* Should always succeed */ + popup_grab_on_window (gtk_widget_get_window (widget), keyboard, pointer, activate_time); /* Should always succeed */ gtk_device_grab_add (GTK_WIDGET (menu), pointer, TRUE); @@ -1710,27 +1748,11 @@ gtk_menu_popup (GtkMenu *menu, g_return_if_fail (GTK_IS_MENU (menu)); - device = gtk_get_current_event_device (); - - if (!device) - { - GdkDisplay *display; - GdkDeviceManager *device_manager; - GList *devices; - - display = gtk_widget_get_display (GTK_WIDGET (menu)); - device_manager = gdk_display_get_device_manager (display); - devices = gdk_device_manager_list_devices (device_manager, GDK_DEVICE_TYPE_MASTER); - - device = devices->data; - - g_list_free (devices); - } - - gtk_menu_popup_for_device (menu, device, + gtk_menu_popup_for_device (menu, + NULL, parent_menu_shell, parent_menu_item, - func, data, + func, data, NULL, button, activate_time); } @@ -1777,7 +1799,7 @@ gtk_menu_popdown (GtkMenu *menu) { gtk_widget_set_size_request (menu->tearoff_window, -1, -1); - if (GTK_BIN (menu->toplevel)->child) + if (gtk_bin_get_child (GTK_BIN (menu->toplevel))) { gtk_menu_reparent (menu, menu->tearoff_hbox, TRUE); } @@ -1837,7 +1859,7 @@ gtk_menu_get_active (GtkMenu *menu) child = children->data; children = children->next; - if (GTK_BIN (child)->child) + if (gtk_bin_get_child (GTK_BIN (child))) break; child = NULL; } @@ -1863,7 +1885,7 @@ gtk_menu_set_active (GtkMenu *menu, if (tmp_list) { child = tmp_list->data; - if (GTK_BIN (child)->child) + if (gtk_bin_get_child (GTK_BIN (child))) { if (menu->old_active_menu_item) g_object_unref (menu->old_active_menu_item); @@ -2038,21 +2060,27 @@ gtk_menu_set_tearoff_hints (GtkMenu *menu, gint width) { GdkGeometry geometry_hints; - + GtkMenuPrivate *priv; + if (!menu->tearoff_window) return; + priv = gtk_menu_get_private (menu); + if (gtk_widget_get_visible (menu->tearoff_scrollbar)) { - gtk_widget_size_request (menu->tearoff_scrollbar, NULL); - width += menu->tearoff_scrollbar->requisition.width; + GtkRequisition requisition; + + gtk_size_request_get_size (GTK_SIZE_REQUEST (menu->tearoff_scrollbar), + &requisition, NULL); + width += requisition.width; } geometry_hints.min_width = width; geometry_hints.max_width = width; geometry_hints.min_height = 0; - geometry_hints.max_height = GTK_WIDGET (menu)->requisition.height; + geometry_hints.max_height = priv->requested_height; gtk_window_set_geometry_hints (GTK_WINDOW (menu->tearoff_window), NULL, @@ -2074,7 +2102,7 @@ gtk_menu_update_title (GtkMenu *menu) attach_widget = gtk_menu_get_attach_widget (menu); if (GTK_IS_MENU_ITEM (attach_widget)) { - GtkWidget *child = GTK_BIN (attach_widget)->child; + GtkWidget *child = gtk_bin_get_child (GTK_BIN (attach_widget)); if (GTK_IS_LABEL (child)) title = gtk_label_get_text (GTK_LABEL (child)); } @@ -2093,7 +2121,7 @@ gtk_menu_get_toplevel (GtkWidget *menu) attach = gtk_menu_get_attach_widget (GTK_MENU (menu)); if (GTK_IS_MENU_ITEM (attach)) - attach = attach->parent; + attach = gtk_widget_get_parent (attach); if (GTK_IS_MENU (attach)) return gtk_menu_get_toplevel (attach); @@ -2118,10 +2146,13 @@ void gtk_menu_set_tearoff_state (GtkMenu *menu, gboolean torn_off) { - gint width, height; + gint width, height; + GtkMenuPrivate *priv; g_return_if_fail (GTK_IS_MENU (menu)); + priv = gtk_menu_get_private (menu); + if (menu->torn_off != torn_off) { menu->torn_off = torn_off; @@ -2162,11 +2193,12 @@ gtk_menu_set_tearoff_state (GtkMenu *menu, menu->tearoff_hbox = gtk_hbox_new (FALSE, FALSE); gtk_container_add (GTK_CONTAINER (menu->tearoff_window), menu->tearoff_hbox); - gdk_drawable_get_size (GTK_WIDGET (menu)->window, &width, &height); - menu->tearoff_adjustment = + gdk_drawable_get_size (gtk_widget_get_window (GTK_WIDGET (menu)), + &width, &height); + menu->tearoff_adjustment = GTK_ADJUSTMENT (gtk_adjustment_new (0, 0, - GTK_WIDGET (menu)->requisition.height, + priv->requested_height, MENU_SCROLL_STEP2, height/2, height)); @@ -2187,12 +2219,14 @@ gtk_menu_set_tearoff_state (GtkMenu *menu, gtk_menu_reparent (menu, menu->tearoff_hbox, FALSE); - gdk_drawable_get_size (GTK_WIDGET (menu)->window, &width, NULL); + gdk_drawable_get_size (gtk_widget_get_window (GTK_WIDGET (menu)), + &width, NULL); /* Update menu->requisition */ - gtk_widget_size_request (GTK_WIDGET (menu), NULL); - + gtk_size_request_get_size (GTK_SIZE_REQUEST (menu), + NULL, NULL); + gtk_menu_set_tearoff_hints (menu, width); gtk_widget_realize (menu->tearoff_window); @@ -2318,10 +2352,13 @@ gtk_menu_style_set (GtkWidget *widget, if (gtk_widget_get_realized (widget)) { GtkMenu *menu = GTK_MENU (widget); - - gtk_style_set_background (widget->style, menu->bin_window, GTK_STATE_NORMAL); - gtk_style_set_background (widget->style, menu->view_window, GTK_STATE_NORMAL); - gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL); + GtkStyle *style; + + style = gtk_widget_get_style (widget); + + gtk_style_set_background (style, menu->bin_window, GTK_STATE_NORMAL); + gtk_style_set_background (style, menu->view_window, GTK_STATE_NORMAL); + gtk_style_set_background (style, gtk_widget_get_window (widget), GTK_STATE_NORMAL); } } @@ -2363,10 +2400,14 @@ get_arrows_border (GtkMenu *menu, static void gtk_menu_realize (GtkWidget *widget) { + GtkAllocation allocation; + GtkStyle *style; + GdkWindow *window; GdkWindowAttr attributes; gint attributes_mask; gint border_width; GtkMenu *menu; + GtkMenuPrivate *priv; GtkWidget *child; GList *children; guint vertical_padding; @@ -2376,53 +2417,64 @@ gtk_menu_realize (GtkWidget *widget) g_return_if_fail (GTK_IS_MENU (widget)); menu = GTK_MENU (widget); - + priv = gtk_menu_get_private (menu); + gtk_widget_set_realized (widget, TRUE); - + + gtk_widget_get_allocation (widget, &allocation); + attributes.window_type = GDK_WINDOW_CHILD; - attributes.x = widget->allocation.x; - attributes.y = widget->allocation.y; - attributes.width = widget->allocation.width; - attributes.height = widget->allocation.height; + attributes.x = allocation.x; + attributes.y = allocation.y; + attributes.width = allocation.width; + attributes.height = allocation.height; attributes.wclass = GDK_INPUT_OUTPUT; attributes.visual = gtk_widget_get_visual (widget); attributes.colormap = gtk_widget_get_colormap (widget); - attributes.event_mask = gtk_widget_get_events (widget); - attributes.event_mask |= (GDK_EXPOSURE_MASK | GDK_KEY_PRESS_MASK | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK ); - + attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP; - widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask); - gdk_window_set_user_data (widget->window, widget); - - border_width = GTK_CONTAINER (widget)->border_width; + 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); + + border_width = gtk_container_get_border_width (GTK_CONTAINER (widget)); + + style = gtk_widget_get_style (widget); gtk_widget_style_get (GTK_WIDGET (menu), "vertical-padding", &vertical_padding, "horizontal-padding", &horizontal_padding, NULL); - 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); + gtk_widget_get_allocation (widget, &allocation); + + attributes.x = border_width + style->xthickness + horizontal_padding; + attributes.y = border_width + style->ythickness + vertical_padding; + attributes.width = MAX (1, allocation.width - attributes.x * 2); + attributes.height = MAX (1, allocation.height - attributes.y * 2); get_arrows_border (menu, &arrow_border); attributes.y += arrow_border.top; attributes.height -= arrow_border.top; attributes.height -= arrow_border.bottom; - menu->view_window = gdk_window_new (widget->window, &attributes, attributes_mask); + menu->view_window = gdk_window_new (window, + &attributes, attributes_mask); gdk_window_set_user_data (menu->view_window, menu); + gtk_widget_get_allocation (widget, &allocation); + attributes.x = 0; attributes.y = 0; - attributes.width = MAX (1, widget->allocation.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); + attributes.width = MAX (1, allocation.width - (border_width + style->xthickness + horizontal_padding) * 2); + attributes.height = MAX (1, priv->requested_height - (border_width + style->ythickness + vertical_padding) * 2); + + menu->bin_window = gdk_window_new (menu->view_window, + &attributes, attributes_mask); gdk_window_set_user_data (menu->bin_window, menu); children = GTK_MENU_SHELL (menu)->children; @@ -2433,11 +2485,11 @@ gtk_menu_realize (GtkWidget *widget) gtk_widget_set_parent_window (child, menu->bin_window); } - - widget->style = gtk_style_attach (widget->style, widget->window); - gtk_style_set_background (widget->style, menu->bin_window, GTK_STATE_NORMAL); - gtk_style_set_background (widget->style, menu->view_window, GTK_STATE_NORMAL); - gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL); + + gtk_widget_style_attach (widget); + gtk_style_set_background (style, menu->bin_window, GTK_STATE_NORMAL); + gtk_style_set_background (style, menu->view_window, GTK_STATE_NORMAL); + gtk_style_set_background (style, window, GTK_STATE_NORMAL); if (GTK_MENU_SHELL (widget)->active_menu_item) gtk_menu_scroll_item_visible (GTK_MENU_SHELL (widget), @@ -2521,118 +2573,81 @@ gtk_menu_unrealize (GtkWidget *widget) GTK_WIDGET_CLASS (gtk_menu_parent_class)->unrealize (widget); } -static void -gtk_menu_size_request (GtkWidget *widget, - GtkRequisition *requisition) + +static gint +calculate_line_heights (GtkMenu *menu, + gint for_width, + guint **ret_min_heights, + guint **ret_nat_heights) { - gint i; - GtkMenu *menu; - GtkMenuShell *menu_shell; - GtkWidget *child; - GList *children; - guint max_toggle_size; - guint max_accel_width; - guint vertical_padding; - guint horizontal_padding; - GtkRequisition child_requisition; + GtkMenuShell *menu_shell; GtkMenuPrivate *priv; + GtkWidget *child, *widget; + GList *children; + guint horizontal_padding; + guint border_width; + guint n_columns; + gint n_heights; + guint *min_heights; + guint *nat_heights; + gint avail_width; + + widget = GTK_WIDGET (menu); + menu_shell = GTK_MENU_SHELL (widget); + priv = gtk_menu_get_private (menu); - g_return_if_fail (GTK_IS_MENU (widget)); - g_return_if_fail (requisition != NULL); - - menu = GTK_MENU (widget); - menu_shell = GTK_MENU_SHELL (widget); - priv = gtk_menu_get_private (menu); - - requisition->width = 0; - requisition->height = 0; - - max_toggle_size = 0; - max_accel_width = 0; - - g_free (priv->heights); - priv->heights = g_new0 (guint, gtk_menu_get_n_rows (menu)); - priv->heights_length = gtk_menu_get_n_rows (menu); + min_heights = g_new0 (guint, gtk_menu_get_n_rows (menu)); + nat_heights = g_new0 (guint, gtk_menu_get_n_rows (menu)); + n_heights = gtk_menu_get_n_rows (menu); + n_columns = gtk_menu_get_n_columns (menu); + avail_width = for_width - (2 * menu->toggle_size + priv->accel_size) * n_columns; - children = menu_shell->children; - while (children) + gtk_widget_style_get (GTK_WIDGET (menu), + "horizontal-padding", &horizontal_padding, + NULL); + + border_width = gtk_container_get_border_width (GTK_CONTAINER (menu)); + avail_width -= (border_width + horizontal_padding + gtk_widget_get_style (widget)->xthickness) * 2; + + for (children = menu_shell->children; children; children = children->next) { gint part; gint toggle_size; gint l, r, t, b; + gint child_min, child_nat; child = children->data; - children = children->next; if (! gtk_widget_get_visible (child)) continue; get_effective_child_attach (child, &l, &r, &t, &b); - /* It's important to size_request the child - * before doing the toggle size request, in - * case the toggle size request depends on the size - * request of a child of the child (e.g. for ImageMenuItem) - */ - - GTK_MENU_ITEM (child)->show_submenu_indicator = TRUE; - gtk_widget_size_request (child, &child_requisition); - - gtk_menu_item_toggle_size_request (GTK_MENU_ITEM (child), &toggle_size); - max_toggle_size = MAX (max_toggle_size, toggle_size); - max_accel_width = MAX (max_accel_width, - GTK_MENU_ITEM (child)->accelerator_width); - - part = child_requisition.width / (r - l); - requisition->width = MAX (requisition->width, part); + part = avail_width / (r - l); - part = MAX (child_requisition.height, toggle_size) / (b - t); - priv->heights[t] = MAX (priv->heights[t], part); - } - - /* If the menu doesn't include any images or check items - * reserve the space so that all menus are consistent. - * We only do this for 'ordinary' menus, not for combobox - * menus or multi-column menus - */ - if (max_toggle_size == 0 && - gtk_menu_get_n_columns (menu) == 1 && - !priv->no_toggle_size) - { - guint toggle_spacing; - guint indicator_size; + gtk_size_request_get_height_for_width (GTK_SIZE_REQUEST (child), part, + &child_min, &child_nat); - gtk_style_get (widget->style, - GTK_TYPE_CHECK_MENU_ITEM, - "toggle-spacing", &toggle_spacing, - "indicator-size", &indicator_size, - NULL); + gtk_menu_item_toggle_size_request (GTK_MENU_ITEM (child), &toggle_size); + + part = MAX (child_min, toggle_size) / (b - t); + min_heights[t] = MAX (min_heights[t], part); - max_toggle_size = indicator_size + toggle_spacing; + part = MAX (child_nat, toggle_size) / (b - t); + nat_heights[t] = MAX (nat_heights[t], part); } - for (i = 0; i < gtk_menu_get_n_rows (menu); i++) - requisition->height += priv->heights[i]; - - requisition->width += 2 * max_toggle_size + max_accel_width; - requisition->width *= gtk_menu_get_n_columns (menu); - - gtk_widget_style_get (GTK_WIDGET (menu), - "vertical-padding", &vertical_padding, - "horizontal-padding", &horizontal_padding, - NULL); + if (ret_min_heights) + *ret_min_heights = min_heights; + else + g_free (min_heights); - 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; + if (ret_nat_heights) + *ret_nat_heights = nat_heights; + else + g_free (nat_heights); - 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). - */ - if (menu->tearoff_active) - gtk_menu_set_tearoff_hints (menu, requisition->width); + return n_heights; } static void @@ -2643,11 +2658,12 @@ gtk_menu_size_allocate (GtkWidget *widget, GtkMenuShell *menu_shell; GtkWidget *child; GtkAllocation child_allocation; - GtkRequisition child_requisition; GtkMenuPrivate *priv; + GtkStyle *style; GList *children; - gint x, y; + gint x, y, i; gint width, height; + guint border_width; guint vertical_padding; guint horizontal_padding; @@ -2658,23 +2674,34 @@ gtk_menu_size_allocate (GtkWidget *widget, menu_shell = GTK_MENU_SHELL (widget); priv = gtk_menu_get_private (menu); - widget->allocation = *allocation; - gtk_widget_get_child_requisition (GTK_WIDGET (menu), &child_requisition); + gtk_widget_set_allocation (widget, allocation); + + style = gtk_widget_get_style (widget); gtk_widget_style_get (GTK_WIDGET (menu), "vertical-padding", &vertical_padding, "horizontal-padding", &horizontal_padding, NULL); + border_width = gtk_container_get_border_width (GTK_CONTAINER (menu)); - x = GTK_CONTAINER (menu)->border_width + widget->style->xthickness + horizontal_padding; - y = GTK_CONTAINER (menu)->border_width + widget->style->ythickness + vertical_padding; + g_free (priv->heights); + priv->heights_length = + calculate_line_heights (menu, + allocation->width, + &priv->heights, + NULL); + + /* refresh our cached height request */ + priv->requested_height = (border_width + vertical_padding + style->ythickness) * 2; + for (i = 0; i < priv->heights_length; i++) + priv->requested_height += priv->heights[i]; + + x = border_width + style->xthickness + horizontal_padding; + y = border_width + style->ythickness + vertical_padding; width = MAX (1, allocation->width - x * 2); height = MAX (1, allocation->height - y * 2); - child_requisition.width -= x * 2; - child_requisition.height -= y * 2; - if (menu_shell->active) gtk_menu_scroll_to (menu, menu->scroll_offset); @@ -2690,7 +2717,7 @@ gtk_menu_size_allocate (GtkWidget *widget, if (gtk_widget_get_realized (widget)) { - gdk_window_move_resize (widget->window, + gdk_window_move_resize (gtk_widget_get_window (widget), allocation->x, allocation->y, allocation->width, allocation->height); @@ -2763,7 +2790,7 @@ gtk_menu_size_allocate (GtkWidget *widget, if (menu->tearoff_active) { - if (allocation->height >= widget->requisition.height) + if (height >= priv->requested_height) { if (gtk_widget_get_visible (menu->tearoff_scrollbar)) { @@ -2775,7 +2802,7 @@ gtk_menu_size_allocate (GtkWidget *widget, } else { - menu->tearoff_adjustment->upper = widget->requisition.height; + menu->tearoff_adjustment->upper = priv->requested_height; menu->tearoff_adjustment->page_size = allocation->height; if (menu->tearoff_adjustment->value + menu->tearoff_adjustment->page_size > @@ -2807,11 +2834,15 @@ get_arrows_visible_area (GtkMenu *menu, GdkRectangle *lower, gint *arrow_space) { + GtkArrowPlacement arrow_placement; + GtkStyle *style; GtkWidget *widget = GTK_WIDGET (menu); + guint border_width; guint vertical_padding; guint horizontal_padding; gint scroll_arrow_height; - GtkArrowPlacement arrow_placement; + + style = gtk_widget_get_style (widget); gtk_widget_style_get (widget, "vertical-padding", &vertical_padding, @@ -2820,9 +2851,11 @@ get_arrows_visible_area (GtkMenu *menu, "arrow-placement", &arrow_placement, 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, &border->width, &border->height); + border_width = gtk_container_get_border_width (GTK_CONTAINER (widget)); + border->x = border_width + style->xthickness + horizontal_padding; + border->y = border_width + style->ythickness + vertical_padding; + gdk_drawable_get_size (gtk_widget_get_window (widget), + &border->width, &border->height); switch (arrow_placement) { @@ -2868,7 +2901,7 @@ get_arrows_visible_area (GtkMenu *menu, lower->x = lower->y = lower->width = lower->height = 0; } - *arrow_space = scroll_arrow_height - 2 * widget->style->ythickness; + *arrow_space = scroll_arrow_height - 2 * style->ythickness; } static void @@ -2877,9 +2910,11 @@ gtk_menu_paint (GtkWidget *widget, { GtkMenu *menu; GtkMenuPrivate *priv; + GtkStyle *style; GdkRectangle border; GdkRectangle upper; GdkRectangle lower; + GdkWindow *window; gint arrow_space; g_return_if_fail (GTK_IS_MENU (widget)); @@ -2887,9 +2922,12 @@ gtk_menu_paint (GtkWidget *widget, menu = GTK_MENU (widget); priv = gtk_menu_get_private (menu); + style = gtk_widget_get_style (widget); + window = gtk_widget_get_window (widget); + get_arrows_visible_area (menu, &border, &upper, &lower, &arrow_space); - if (event->window == widget->window) + if (event->window == window) { gfloat arrow_scaling; gint arrow_size; @@ -2897,8 +2935,8 @@ gtk_menu_paint (GtkWidget *widget, gtk_widget_style_get (widget, "arrow-scaling", &arrow_scaling, NULL); arrow_size = arrow_scaling * arrow_space; - gtk_paint_box (widget->style, - widget->window, + gtk_paint_box (style, + window, GTK_STATE_NORMAL, GTK_SHADOW_OUT, &event->area, widget, "menu", @@ -2906,8 +2944,8 @@ gtk_menu_paint (GtkWidget *widget, if (menu->upper_arrow_visible && !menu->tearoff_active) { - gtk_paint_box (widget->style, - widget->window, + gtk_paint_box (style, + window, priv->upper_arrow_state, GTK_SHADOW_OUT, &event->area, widget, "menu_scroll_arrow_up", @@ -2916,22 +2954,22 @@ gtk_menu_paint (GtkWidget *widget, upper.width, upper.height); - gtk_paint_arrow (widget->style, - widget->window, + gtk_paint_arrow (style, + window, priv->upper_arrow_state, GTK_SHADOW_OUT, &event->area, widget, "menu_scroll_arrow_up", GTK_ARROW_UP, TRUE, upper.x + (upper.width - arrow_size) / 2, - upper.y + widget->style->ythickness + (arrow_space - arrow_size) / 2, + upper.y + style->ythickness + (arrow_space - arrow_size) / 2, arrow_size, arrow_size); } if (menu->lower_arrow_visible && !menu->tearoff_active) { - gtk_paint_box (widget->style, - widget->window, + gtk_paint_box (style, + window, priv->lower_arrow_state, GTK_SHADOW_OUT, &event->area, widget, "menu_scroll_arrow_down", @@ -2940,15 +2978,15 @@ gtk_menu_paint (GtkWidget *widget, lower.width, lower.height); - gtk_paint_arrow (widget->style, - widget->window, + gtk_paint_arrow (style, + window, priv->lower_arrow_state, GTK_SHADOW_OUT, &event->area, widget, "menu_scroll_arrow_down", GTK_ARROW_DOWN, TRUE, lower.x + (lower.width - arrow_size) / 2, - lower.y + widget->style->ythickness + (arrow_space - arrow_size) / 2, + lower.y + style->ythickness + (arrow_space - arrow_size) / 2, arrow_size, arrow_size); } } @@ -2964,7 +3002,7 @@ gtk_menu_paint (GtkWidget *widget, y -= arrow_border.top; } - gtk_paint_box (widget->style, + gtk_paint_box (style, menu->bin_window, GTK_STATE_NORMAL, GTK_SHADOW_OUT, @@ -3001,6 +3039,202 @@ gtk_menu_show (GtkWidget *widget) GTK_WIDGET_CLASS (gtk_menu_parent_class)->show (widget); } + + +static void +gtk_menu_size_request_init (GtkSizeRequestIface *iface) +{ + iface->get_width = gtk_menu_get_width; + iface->get_height = gtk_menu_get_height; + iface->get_height_for_width = gtk_menu_get_height_for_width; +} + +static void +gtk_menu_get_width (GtkSizeRequest *widget, + gint *minimum_size, + gint *natural_size) +{ + GtkMenu *menu; + GtkMenuShell *menu_shell; + GtkMenuPrivate *priv; + GtkStyle *style; + GtkWidget *child; + 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; + + menu = GTK_MENU (widget); + menu_shell = GTK_MENU_SHELL (widget); + priv = gtk_menu_get_private (menu); + + style = gtk_widget_get_style (GTK_WIDGET (widget)); + + min_width = nat_width = 0; + + max_toggle_size = 0; + max_accel_width = 0; + + children = menu_shell->children; + while (children) + { + gint part; + gint toggle_size; + gint l, r, t, b; + + child = children->data; + children = children->next; + + if (! gtk_widget_get_visible (child)) + continue; + + get_effective_child_attach (child, &l, &r, &t, &b); + + /* It's important to size_request the child + * before doing the toggle size request, in + * case the toggle size request depends on the size + * request of a child of the child (e.g. for ImageMenuItem) + */ + + GTK_MENU_ITEM (child)->show_submenu_indicator = TRUE; + gtk_size_request_get_width (GTK_SIZE_REQUEST (child), &child_min, &child_nat); + + gtk_menu_item_toggle_size_request (GTK_MENU_ITEM (child), &toggle_size); + max_toggle_size = MAX (max_toggle_size, toggle_size); + max_accel_width = MAX (max_accel_width, + GTK_MENU_ITEM (child)->accelerator_width); + + part = child_min / (r - l); + min_width = MAX (min_width, part); + + part = child_nat / (r - l); + nat_width = MAX (nat_width, part); + } + + /* If the menu doesn't include any images or check items + * reserve the space so that all menus are consistent. + * We only do this for 'ordinary' menus, not for combobox + * menus or multi-column menus + */ + if (max_toggle_size == 0 && + gtk_menu_get_n_columns (menu) == 1 && + !priv->no_toggle_size) + { + guint toggle_spacing; + guint indicator_size; + + gtk_style_get (style, + GTK_TYPE_CHECK_MENU_ITEM, + "toggle-spacing", &toggle_spacing, + "indicator-size", &indicator_size, + NULL); + + max_toggle_size = indicator_size + toggle_spacing; + } + + min_width += 2 * max_toggle_size + max_accel_width; + min_width *= gtk_menu_get_n_columns (menu); + + 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); + + border_width = gtk_container_get_border_width (GTK_CONTAINER (menu)); + min_width += (border_width + horizontal_padding + style->xthickness) * 2; + nat_width += (border_width + horizontal_padding + style->xthickness) * 2; + + menu->toggle_size = max_toggle_size; + priv->accel_size = max_accel_width; + + if (minimum_size) + *minimum_size = min_width; + + if (natural_size) + *natural_size = nat_width; + + /* Don't resize the tearoff if it is not active, because it won't redraw (it is only a background pixmap). + */ + if (menu->tearoff_active) + gtk_menu_set_tearoff_hints (menu, min_width); +} + +static void +gtk_menu_get_height (GtkSizeRequest *widget, + gint *minimum_size, + gint *natural_size) +{ + gint min_width; + + /* Menus are height-for-width only, just return the height for the minimum width */ + GTK_SIZE_REQUEST_GET_IFACE (widget)->get_width (widget, &min_width, NULL); + GTK_SIZE_REQUEST_GET_IFACE (widget)->get_height_for_width (widget, min_width, minimum_size, natural_size); +} + +static void +gtk_menu_get_height_for_width (GtkSizeRequest *widget, + gint for_size, + gint *minimum_size, + gint *natural_size) +{ + GtkMenu *menu = GTK_MENU (widget); + GtkMenuPrivate *private = gtk_menu_get_private (menu); + guint *min_heights, *nat_heights; + guint vertical_padding, border_width; + gint n_heights, i; + gint min_height, nat_height; + + gtk_widget_style_get (GTK_WIDGET (menu), "vertical-padding", &vertical_padding, NULL); + border_width = gtk_container_get_border_width (GTK_CONTAINER (menu)); + + min_height = nat_height = (border_width + vertical_padding + gtk_widget_get_style (GTK_WIDGET (widget))->ythickness) * 2; + + n_heights = + calculate_line_heights (menu, for_size, &min_heights, &nat_heights); + + for (i = 0; i < n_heights; i++) + { + min_height += min_heights[i]; + nat_height += nat_heights[i]; + } + + if (private->have_position) + { + GdkScreen *screen = gtk_widget_get_screen (menu->toplevel); + GdkRectangle monitor; + + gdk_screen_get_monitor_geometry (screen, private->monitor_num, &monitor); + + if (private->y + min_height > monitor.y + monitor.height) + min_height = monitor.y + monitor.height - private->y; + + if (private->y + nat_height > monitor.y + monitor.height) + nat_height = monitor.y + monitor.height - private->y; + + if (private->y < monitor.y) + { + min_height -= monitor.y - private->y; + nat_height -= monitor.y - private->y; + } + } + + if (minimum_size) + *minimum_size = min_height; + + if (natural_size) + *natural_size = nat_height; + + g_free (min_heights); + g_free (nat_heights); +} + + + static gboolean gtk_menu_button_scroll (GtkMenu *menu, GdkEventButton *event) @@ -3030,6 +3264,7 @@ pointer_in_menu_window (GtkWidget *widget, gdouble x_root, gdouble y_root) { + GtkAllocation allocation; GtkMenu *menu = GTK_MENU (widget); if (gtk_widget_get_mapped (menu->toplevel)) @@ -3037,10 +3272,12 @@ pointer_in_menu_window (GtkWidget *widget, GtkMenuShell *menu_shell; gint window_x, window_y; - gdk_window_get_position (menu->toplevel->window, &window_x, &window_y); + gdk_window_get_position (gtk_widget_get_window (menu->toplevel), + &window_x, &window_y); - if (x_root >= window_x && x_root < window_x + widget->allocation.width && - y_root >= window_y && y_root < window_y + widget->allocation.height) + gtk_widget_get_allocation (widget, &allocation); + if (x_root >= window_x && x_root < window_x + allocation.width && + y_root >= window_y && y_root < window_y + allocation.height) return TRUE; menu_shell = GTK_MENU_SHELL (widget); @@ -3137,7 +3374,7 @@ get_accel_path (GtkWidget *menu_item, { *locked = TRUE; - label = GTK_BIN (menu_item)->child; + label = gtk_bin_get_child (GTK_BIN (menu_item)); if (GTK_IS_ACCEL_LABEL (label)) { @@ -3214,9 +3451,9 @@ gtk_menu_key_press (GtkWidget *widget, switch (event->keyval) { - case GDK_Delete: - case GDK_KP_Delete: - case GDK_BackSpace: + case GDK_KEY_Delete: + case GDK_KEY_KP_Delete: + case GDK_KEY_BackSpace: delete = TRUE; break; default: @@ -3240,7 +3477,7 @@ gtk_menu_key_press (GtkWidget *widget, /* Modify the accelerators */ if (can_change_accels && menu_shell->active_menu_item && - GTK_BIN (menu_shell->active_menu_item)->child && /* no separators */ + gtk_bin_get_child (GTK_BIN (menu_shell->active_menu_item)) && /* no separators */ GTK_MENU_ITEM (menu_shell->active_menu_item)->submenu == NULL && /* no submenus */ (delete || gtk_accelerator_valid (accel_key, accel_mods))) { @@ -3323,6 +3560,16 @@ definitely_within_item (GtkWidget *widget, check_threshold (widget, 0, h - 1, x, y); } +static gboolean +gtk_menu_has_navigation_triangle (GtkMenu *menu) +{ + GtkMenuPrivate *priv; + + priv = gtk_menu_get_private (menu); + + return priv->navigation_height && priv->navigation_width; +} + static gboolean gtk_menu_motion_notify (GtkWidget *widget, GdkEventMotion *event) @@ -3330,6 +3577,7 @@ gtk_menu_motion_notify (GtkWidget *widget, GtkWidget *menu_item; GtkMenu *menu; GtkMenuShell *menu_shell; + GtkWidget *parent; gboolean need_enter; @@ -3354,17 +3602,18 @@ gtk_menu_motion_notify (GtkWidget *widget, * which may be different from 'widget'. */ menu_item = gtk_get_event_widget ((GdkEvent*) event); + parent = gtk_widget_get_parent (menu_item); if (!GTK_IS_MENU_ITEM (menu_item) || - !GTK_IS_MENU (menu_item->parent)) + !GTK_IS_MENU (parent)) return FALSE; - menu_shell = GTK_MENU_SHELL (menu_item->parent); + menu_shell = GTK_MENU_SHELL (parent); menu = GTK_MENU (menu_shell); if (definitely_within_item (menu_item, event->x, event->y)) menu_shell->activate_time = 0; - need_enter = (menu->navigation_region != NULL || menu_shell->ignore_enter); + need_enter = (gtk_menu_has_navigation_triangle (menu) || menu_shell->ignore_enter); /* Check to see if we are within an active submenu's navigation region */ @@ -3448,14 +3697,16 @@ static void gtk_menu_scroll_by (GtkMenu *menu, gint step) { + GtkMenuPrivate *priv; + GtkBorder arrow_border; GtkWidget *widget; gint offset; gint view_width, view_height; gboolean double_arrows; - GtkBorder arrow_border; - + widget = GTK_WIDGET (menu); offset = menu->scroll_offset + step; + priv = gtk_menu_get_private (menu); get_arrows_border (menu, &arrow_border); @@ -3474,10 +3725,11 @@ gtk_menu_scroll_by (GtkMenu *menu, if ((menu->scroll_offset >= 0) && (offset < 0)) offset = 0; - gdk_drawable_get_size (widget->window, &view_width, &view_height); + gdk_drawable_get_size (gtk_widget_get_window (widget), + &view_width, &view_height); if (menu->scroll_offset == 0 && - view_height >= widget->requisition.height) + view_height >= priv->requested_height) return; /* Don't scroll past the bottom if we weren't before: */ @@ -3490,9 +3742,9 @@ gtk_menu_scroll_by (GtkMenu *menu, if (double_arrows) view_height -= arrow_border.bottom; - if ((menu->scroll_offset + view_height <= widget->requisition.height) && - (offset + view_height > widget->requisition.height)) - offset = widget->requisition.height - view_height; + if ((menu->scroll_offset + view_height <= priv->requested_height) && + (offset + view_height > priv->requested_height)) + offset = priv->requested_height - view_height; if (offset != menu->scroll_offset) gtk_menu_scroll_to (menu, offset); @@ -3609,25 +3861,29 @@ get_arrows_sensitive_area (GtkMenu *menu, GdkRectangle *upper, GdkRectangle *lower) { + GtkArrowPlacement arrow_placement; + GtkWidget *widget = GTK_WIDGET (menu); + GdkWindow *window; gint width, height; - gint border; + guint border; guint vertical_padding; gint win_x, win_y; gint scroll_arrow_height; - GtkArrowPlacement arrow_placement; - gdk_drawable_get_size (GTK_WIDGET (menu)->window, &width, &height); + window = gtk_widget_get_window (widget); - gtk_widget_style_get (GTK_WIDGET (menu), + gdk_drawable_get_size (window, &width, &height); + + gtk_widget_style_get (widget, "vertical-padding", &vertical_padding, "scroll-arrow-vlength", &scroll_arrow_height, "arrow-placement", &arrow_placement, NULL); - border = GTK_CONTAINER (menu)->border_width + - GTK_WIDGET (menu)->style->ythickness + vertical_padding; + border = gtk_container_get_border_width (GTK_CONTAINER (menu)) + + gtk_widget_get_style (widget)->ythickness + vertical_padding; - gdk_window_get_position (GTK_WIDGET (menu)->window, &win_x, &win_y); + gdk_window_get_position (window, &win_x, &win_y); switch (arrow_placement) { @@ -3711,7 +3967,8 @@ gtk_menu_handle_scrolling (GtkMenu *menu, "gtk-touchscreen-mode", &touchscreen_mode, NULL); - gdk_window_get_position (menu->toplevel->window, &top_x, &top_y); + gdk_window_get_position (gtk_widget_get_window (menu->toplevel), + &top_x, &top_y); x -= top_x; y -= top_y; @@ -3817,7 +4074,7 @@ gtk_menu_handle_scrolling (GtkMenu *menu, { priv->upper_arrow_state = arrow_state; - gdk_window_invalidate_rect (GTK_WIDGET (menu)->window, + gdk_window_invalidate_rect (gtk_widget_get_window (GTK_WIDGET (menu)), &rect, FALSE); } } @@ -3925,7 +4182,7 @@ gtk_menu_handle_scrolling (GtkMenu *menu, { priv->lower_arrow_state = arrow_state; - gdk_window_invalidate_rect (GTK_WIDGET (menu)->window, + gdk_window_invalidate_rect (gtk_widget_get_window (GTK_WIDGET (menu)), &rect, FALSE); } } @@ -3937,6 +4194,7 @@ gtk_menu_enter_notify (GtkWidget *widget, GdkEventCrossing *event) { GtkWidget *menu_item; + GtkWidget *parent; gboolean touchscreen_mode; if (event->mode == GDK_CROSSING_GTK_GRAB || @@ -3960,7 +4218,7 @@ gtk_menu_enter_notify (GtkWidget *widget, if (!touchscreen_mode && GTK_IS_MENU_ITEM (menu_item)) { - GtkWidget *menu = menu_item->parent; + GtkWidget *menu = gtk_widget_get_parent (menu_item); if (GTK_IS_MENU (menu)) { @@ -3998,8 +4256,9 @@ gtk_menu_enter_notify (GtkWidget *widget, * will not correspond to the event widget's parent. Check to see * if we are in the parent's navigation region. */ - if (GTK_IS_MENU_ITEM (menu_item) && GTK_IS_MENU (menu_item->parent) && - gtk_menu_navigating_submenu (GTK_MENU (menu_item->parent), + parent = gtk_widget_get_parent (menu_item); + if (GTK_IS_MENU_ITEM (menu_item) && GTK_IS_MENU (parent) && + gtk_menu_navigating_submenu (GTK_MENU (parent), event->x_root, event->y_root)) return TRUE; @@ -4064,11 +4323,13 @@ gtk_menu_leave_notify (GtkWidget *widget, static void gtk_menu_stop_navigating_submenu (GtkMenu *menu) { - if (menu->navigation_region) - { - gdk_region_destroy (menu->navigation_region); - menu->navigation_region = NULL; - } + GtkMenuPrivate *priv = gtk_menu_get_private (menu); + + priv->navigation_x = 0; + priv->navigation_y = 0; + priv->navigation_width = 0; + priv->navigation_height = 0; + if (menu->navigation_timeout) { g_source_remove (menu->navigation_timeout); @@ -4117,82 +4378,47 @@ gtk_menu_navigating_submenu (GtkMenu *menu, gint event_x, gint event_y) { - if (menu->navigation_region) - { - if (gdk_region_point_in (menu->navigation_region, event_x, event_y)) - return TRUE; - else - { - gtk_menu_stop_navigating_submenu (menu); - return FALSE; - } - } - return FALSE; -} - -#undef DRAW_STAY_UP_TRIANGLE + GtkMenuPrivate *priv; + int width, height; -#ifdef DRAW_STAY_UP_TRIANGLE + if (!gtk_menu_has_navigation_triangle (menu)) + return FALSE; -static void -draw_stay_up_triangle (GdkWindow *window, - GdkRegion *region) -{ - /* Draw ugly color all over the stay-up triangle */ - GdkColor ugly_color = { 0, 50000, 10000, 10000 }; - GdkGCValues gc_values; - GdkGC *ugly_gc; - GdkRectangle clipbox; - - gc_values.subwindow_mode = GDK_INCLUDE_INFERIORS; - ugly_gc = gdk_gc_new_with_values (window, &gc_values, 0 | GDK_GC_SUBWINDOW); - gdk_gc_set_rgb_fg_color (ugly_gc, &ugly_color); - gdk_gc_set_clip_region (ugly_gc, region); - - gdk_region_get_clipbox (region, &clipbox); - - gdk_draw_rectangle (window, - ugly_gc, - TRUE, - clipbox.x, clipbox.y, - clipbox.width, clipbox.height); - - g_object_unref (ugly_gc); -} -#endif + priv = gtk_menu_get_private (menu); + width = priv->navigation_width; + height = priv->navigation_height; -static GdkRegion * -flip_region (GdkRegion *region, - gboolean flip_x, - gboolean flip_y) -{ - gint n_rectangles; - GdkRectangle *rectangles; - GdkRectangle clipbox; - GdkRegion *new_region; - gint i; + /* check if x/y are in the triangle spanned by the navigation parameters */ - new_region = gdk_region_new (); - - gdk_region_get_rectangles (region, &rectangles, &n_rectangles); - gdk_region_get_clipbox (region, &clipbox); + /* 1) Move the coordinates so the triangle starts at 0,0 */ + event_x -= priv->navigation_x; + event_y -= priv->navigation_y; - for (i = 0; i < n_rectangles; ++i) + /* 2) Ensure both legs move along the positive axis */ + if (width < 0) { - GdkRectangle rect = rectangles[i]; - - if (flip_y) - rect.y -= 2 * (rect.y - clipbox.y) + rect.height; - - if (flip_x) - rect.x -= 2 * (rect.x - clipbox.x) + rect.width; - - gdk_region_union_with_rect (new_region, &rect); + event_x = -event_x; + width = -width; + } + if (height < 0) + { + event_y = -event_y; + height = -height; } - g_free (rectangles); - - return new_region; + /* 3) Check that the given coordinate is inside the triangle. The formula + * is a transformed form of this formula: x/w + y/h <= 1 + */ + if (event_x >= 0 && event_y >= 0 && + event_x * height + event_y * width <= width * height) + { + return TRUE; + } + else + { + gtk_menu_stop_navigating_submenu (menu); + return FALSE; + } } static void @@ -4206,79 +4432,71 @@ gtk_menu_set_submenu_navigation_region (GtkMenu *menu, gint submenu_bottom = 0; gint width = 0; gint height = 0; - GdkPoint point[3]; GtkWidget *event_widget; GtkMenuPopdownData *popdown_data; + GtkMenuPrivate *priv; + GdkWindow *window; g_return_if_fail (menu_item->submenu != NULL); g_return_if_fail (event != NULL); + priv = gtk_menu_get_private (menu); + event_widget = gtk_get_event_widget ((GdkEvent*) event); - - gdk_window_get_origin (menu_item->submenu->window, &submenu_left, &submenu_top); - gdk_drawable_get_size (menu_item->submenu->window, &width, &height); - + + window = gtk_widget_get_window (menu_item->submenu); + gdk_window_get_origin (window, &submenu_left, &submenu_top); + gdk_drawable_get_size (window, &width, &height); + submenu_right = submenu_left + width; submenu_bottom = submenu_top + height; - - gdk_drawable_get_size (event_widget->window, &width, &height); - + + gdk_drawable_get_size (gtk_widget_get_window (event_widget), + &width, &height); + if (event->x >= 0 && event->x < width) { gint popdown_delay; - gboolean flip_y = FALSE; - gboolean flip_x = FALSE; gtk_menu_stop_navigating_submenu (menu); + /* The navigation region is the triangle closest to the x/y + * location of the rectangle. This is why the width or height + * can be negative. + */ + if (menu_item->submenu_direction == GTK_DIRECTION_RIGHT) { /* right */ - point[0].x = event->x_root; - point[1].x = submenu_left; + priv->navigation_x = submenu_left; + priv->navigation_width = event->x_root - submenu_left; } else { /* left */ - point[0].x = event->x_root + 1; - point[1].x = 2 * (event->x_root + 1) - submenu_right; - - flip_x = TRUE; + priv->navigation_x = submenu_right; + priv->navigation_width = event->x_root - submenu_right; } if (event->y < 0) { /* top */ - point[0].y = event->y_root + 1; - point[1].y = 2 * (event->y_root + 1) - submenu_top + NAVIGATION_REGION_OVERSHOOT; + priv->navigation_y = event->y_root; + priv->navigation_height = submenu_top - event->y_root - NAVIGATION_REGION_OVERSHOOT; - if (point[0].y >= point[1].y - NAVIGATION_REGION_OVERSHOOT) + if (priv->navigation_height >= 0) return; - - flip_y = TRUE; } else { /* bottom */ - point[0].y = event->y_root; - point[1].y = submenu_bottom + NAVIGATION_REGION_OVERSHOOT; + priv->navigation_y = event->y_root; + priv->navigation_height = submenu_bottom - event->y_root + NAVIGATION_REGION_OVERSHOOT; - if (point[0].y >= submenu_bottom) + if (priv->navigation_height <= 0) return; } - point[2].x = point[1].x; - point[2].y = point[0].y; - - menu->navigation_region = gdk_region_polygon (point, 3, GDK_WINDING_RULE); - - if (flip_x || flip_y) - { - GdkRegion *new_region = flip_region (menu->navigation_region, flip_x, flip_y); - gdk_region_destroy (menu->navigation_region); - menu->navigation_region = new_region; - } - g_object_get (gtk_widget_get_settings (GTK_WIDGET (menu)), "gtk-menu-popdown-delay", &popdown_delay, NULL); @@ -4292,11 +4510,6 @@ gtk_menu_set_submenu_navigation_region (GtkMenu *menu, gtk_menu_stop_navigating_submenu_cb, popdown_data, (GDestroyNotify) g_free); - -#ifdef DRAW_STAY_UP_TRIANGLE - draw_stay_up_triangle (gdk_get_default_root_window(), - menu->navigation_region); -#endif } } @@ -4339,12 +4552,10 @@ gtk_menu_position (GtkMenu *menu) gdk_display_get_device_state (gdk_screen_get_display (screen), pointer, &pointer_screen, &x, &y, NULL); - /* We need the requisition to figure out the right place to - * popup the menu. In fact, we always need to ask here, since - * if a size_request was queued while we weren't popped up, - * the requisition won't have been recomputed yet. + /* Get the minimum height for minimum width to figure out + * the right place to popup the menu. */ - gtk_widget_size_request (widget, &requisition); + gtk_size_request_get_size (GTK_SIZE_REQUEST (widget), &requisition, NULL); if (pointer_screen != screen) { @@ -4378,11 +4589,12 @@ gtk_menu_position (GtkMenu *menu) } else { + GtkStyle *style = gtk_widget_get_style (widget); gint space_left, space_right, space_above, space_below; gint needed_width; gint needed_height; - gint xthickness = widget->style->xthickness; - gint ythickness = widget->style->ythickness; + gint xthickness = style->xthickness; + gint ythickness = style->ythickness; gboolean rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL); /* The placement of popup menus horizontally works like this (with @@ -4498,7 +4710,7 @@ gtk_menu_position (GtkMenu *menu) if (private->initially_pushed_in) { - menu_height = GTK_WIDGET (menu)->requisition.height; + menu_height = requisition.height; if (y + menu_height > monitor.y + monitor.height) { @@ -4549,7 +4761,7 @@ gtk_menu_position (GtkMenu *menu) gtk_window_resize (GTK_WINDOW (menu->tearoff_window), requisition.width, requisition.height); } - + menu->scroll_offset = scroll_offset; } @@ -4585,6 +4797,10 @@ static void gtk_menu_scroll_to (GtkMenu *menu, gint offset) { + GtkMenuPrivate *priv; + GtkAllocation allocation; + GtkBorder arrow_border; + GtkStyle *style; GtkWidget *widget; gint x, y; gint view_width, view_height; @@ -4593,9 +4809,9 @@ gtk_menu_scroll_to (GtkMenu *menu, guint vertical_padding; guint horizontal_padding; gboolean double_arrows; - GtkBorder arrow_border; - + widget = GTK_WIDGET (menu); + priv = gtk_menu_get_private (menu); if (menu->tearoff_active && menu->tearoff_adjustment && @@ -4608,8 +4824,11 @@ gtk_menu_scroll_to (GtkMenu *menu, } /* Move/resize the viewport according to arrows: */ - view_width = widget->allocation.width; - view_height = widget->allocation.height; + gtk_widget_get_allocation (widget, &allocation); + view_width = allocation.width; + view_height = allocation.height; + + style = gtk_widget_get_style (widget); gtk_widget_style_get (GTK_WIDGET (menu), "vertical-padding", &vertical_padding, @@ -4618,14 +4837,14 @@ gtk_menu_scroll_to (GtkMenu *menu, double_arrows = get_double_arrows (menu); - border_width = GTK_CONTAINER (menu)->border_width; - 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; + border_width = gtk_container_get_border_width (GTK_CONTAINER (menu)); + + view_width -= (border_width + style->xthickness + horizontal_padding) * 2; + view_height -= (border_width + style->ythickness + vertical_padding) * 2; + menu_height = priv->requested_height - (border_width + style->ythickness + vertical_padding) * 2; - x = border_width + widget->style->xthickness + horizontal_padding; - y = border_width + widget->style->ythickness + vertical_padding; + x = border_width + style->xthickness + horizontal_padding; + y = border_width + style->ythickness + vertical_padding; if (double_arrows && !menu->tearoff_active) { @@ -4799,14 +5018,13 @@ static void gtk_menu_scroll_item_visible (GtkMenuShell *menu_shell, GtkWidget *menu_item) { - GtkMenu *menu; + GtkMenu *menu = GTK_MENU (menu_shell); + GtkWidget *widget = GTK_WIDGET (menu_shell); gint child_offset, child_height; gint width, height; gint y; gint arrow_height; gboolean last_child = 0; - - menu = GTK_MENU (menu_shell); /* We need to check if the selected item fully visible. * If not we need to scroll the menu so that it becomes fully @@ -4820,16 +5038,18 @@ gtk_menu_scroll_item_visible (GtkMenuShell *menu_shell, gboolean double_arrows; y = menu->scroll_offset; - gdk_drawable_get_size (GTK_WIDGET (menu)->window, &width, &height); + gdk_drawable_get_size (gtk_widget_get_window (widget), + &width, &height); - gtk_widget_style_get (GTK_WIDGET (menu), + gtk_widget_style_get (widget, "vertical-padding", &vertical_padding, NULL); double_arrows = get_double_arrows (menu); - height -= 2*GTK_CONTAINER (menu)->border_width + 2*GTK_WIDGET (menu)->style->ythickness + 2*vertical_padding; - + height -= 2 * gtk_container_get_border_width (GTK_CONTAINER (menu)) + + 2 * gtk_widget_get_style (widget)->ythickness + + 2 * vertical_padding; if (child_offset < y) { /* Ignore the enter event we might get if the pointer is on the menu @@ -4927,7 +5147,7 @@ gtk_menu_reparent (GtkMenu *menu, if (unrealize) { g_object_ref (object); - gtk_container_remove (GTK_CONTAINER (widget->parent), widget); + gtk_container_remove (GTK_CONTAINER (gtk_widget_get_parent (widget)), widget); gtk_container_add (GTK_CONTAINER (new_parent), widget); g_object_unref (object); } @@ -5014,17 +5234,18 @@ gtk_menu_attach (GtkMenu *menu, guint bottom_attach) { GtkMenuShell *menu_shell; - + GtkWidget *parent; + g_return_if_fail (GTK_IS_MENU (menu)); g_return_if_fail (GTK_IS_MENU_ITEM (child)); - g_return_if_fail (child->parent == NULL || - child->parent == GTK_WIDGET (menu)); + parent = gtk_widget_get_parent (child); + g_return_if_fail (parent == NULL || parent == GTK_WIDGET (menu)); g_return_if_fail (left_attach < right_attach); g_return_if_fail (top_attach < bottom_attach); menu_shell = GTK_MENU_SHELL (menu); - - if (!child->parent) + + if (!parent) { AttachInfo *ai = get_attach_info (child); @@ -5041,7 +5262,7 @@ gtk_menu_attach (GtkMenu *menu, } else { - gtk_container_child_set (GTK_CONTAINER (child->parent), child, + gtk_container_child_set (GTK_CONTAINER (parent), child, "left-attach", left_attach, "right-attach", right_attach, "top-attach", top_attach, @@ -5207,12 +5428,15 @@ gtk_menu_move_current (GtkMenuShell *menu_shell, static gint get_visible_size (GtkMenu *menu) { + GtkAllocation allocation; GtkWidget *widget = GTK_WIDGET (menu); GtkContainer *container = GTK_CONTAINER (menu); - - gint menu_height = (widget->allocation.height - - 2 * (container->border_width - + widget->style->ythickness)); + gint menu_height; + + gtk_widget_get_allocation (widget, &allocation); + menu_height = (allocation.height + - 2 * (gtk_container_get_border_width (container) + + gtk_widget_get_style (widget)->ythickness)); if (!menu->tearoff_active) { @@ -5250,7 +5474,8 @@ child_at (GtkMenu *menu, { GtkRequisition child_requisition; - gtk_widget_size_request (children->data, &child_requisition); + gtk_size_request_get_size (GTK_SIZE_REQUEST (children->data), + &child_requisition, NULL); if (_gtk_menu_item_is_selectable (children->data) && child_offset >= lower && @@ -5273,11 +5498,14 @@ child_at (GtkMenu *menu, static gint get_menu_height (GtkMenu *menu) { - gint height; + GtkAllocation allocation; GtkWidget *widget = GTK_WIDGET (menu); + gint height; + + gtk_widget_get_allocation (widget, &allocation); - height = widget->requisition.height; - height -= (GTK_CONTAINER (widget)->border_width + widget->style->ythickness) * 2; + height = allocation.height; + height -= gtk_container_get_border_width (GTK_CONTAINER (widget) + gtk_widget_get_style (widget)->ythickness) * 2; if (!menu->tearoff_active) { @@ -5517,6 +5745,3 @@ gtk_menu_get_reserve_toggle_size (GtkMenu *menu) return !priv->no_toggle_size; } - -#define __GTK_MENU_C__ -#include "gtkaliasdef.c"