X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;f=gtk%2Fgtkmenubar.c;h=d337d712ae4f7ed2a1b09c0874f801c1fec035c4;hb=cf216d780cb2c889a3bcb5faa825fc1b21af8896;hp=d35d03b3097a551edbfb9551b100c715bed849ab;hpb=dd74be94c69edd6c5827d04a1cfba961972a27c5;p=~andy%2Fgtk diff --git a/gtk/gtkmenubar.c b/gtk/gtkmenubar.c index d35d03b30..d337d712a 100644 --- a/gtk/gtkmenubar.c +++ b/gtk/gtkmenubar.c @@ -12,9 +12,7 @@ * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * License along with this library. If not, see . */ /* @@ -24,238 +22,452 @@ * GTK+ at ftp://ftp.gtk.org/pub/gtk/. */ -#define GTK_MENU_INTERNALS +/** + * SECTION:gtkmenubar + * @Title: GtkMenuBar + * @Short_description: A subclass of GtkMenuShell which holds GtkMenuItem widgets + * @See_also: #GtkMenuShell, #GtkMenu, #GtkMenuItem + * + * The #GtkMenuBar is a subclass of #GtkMenuShell which contains one or + * more #GtkMenuItems. The result is a standard menu bar which can hold + * many menu items. + */ + +#include "config.h" + +#include "gtkmenubar.h" -#include "gdk/gdkkeysyms.h" #include "gtkbindings.h" #include "gtkmain.h" #include "gtkmarshalers.h" -#include "gtkmenubar.h" -#include "gtkmenuitem.h" +#include "gtkmenuitemprivate.h" +#include "gtkmenuprivate.h" +#include "gtkmenushellprivate.h" #include "gtksettings.h" -#include "gtkintl.h" +#include "gtksizerequest.h" #include "gtkwindow.h" +#include "gtkcontainerprivate.h" +#include "gtkintl.h" +#include "gtkprivate.h" +#include "gtktypebuiltins.h" +/* Properties */ +enum { + PROP_0, + PROP_PACK_DIRECTION, + PROP_CHILD_PACK_DIRECTION +}; -#define BORDER_SPACING 0 -#define CHILD_SPACING 3 -#define DEFAULT_IPADDING 1 - -static void gtk_menu_bar_class_init (GtkMenuBarClass *klass); -static void gtk_menu_bar_size_request (GtkWidget *widget, - GtkRequisition *requisition); +struct _GtkMenuBarPrivate +{ + GtkPackDirection pack_direction; + GtkPackDirection child_pack_direction; +}; + + +static void gtk_menu_bar_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec); +static void gtk_menu_bar_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec); +static void gtk_menu_bar_get_preferred_width (GtkWidget *widget, + gint *minimum, + gint *natural); +static void gtk_menu_bar_get_preferred_height (GtkWidget *widget, + gint *minimum, + gint *natural); +static void gtk_menu_bar_get_preferred_width_for_height (GtkWidget *widget, + gint height, + gint *minimum, + gint *natural); +static void gtk_menu_bar_get_preferred_height_for_width (GtkWidget *widget, + gint width, + gint *minimum, + gint *natural); static void gtk_menu_bar_size_allocate (GtkWidget *widget, GtkAllocation *allocation); -static void gtk_menu_bar_paint (GtkWidget *widget, - GdkRectangle *area); -static gint gtk_menu_bar_expose (GtkWidget *widget, - GdkEventExpose *event); +static gint gtk_menu_bar_draw (GtkWidget *widget, + cairo_t *cr); static void gtk_menu_bar_hierarchy_changed (GtkWidget *widget, GtkWidget *old_toplevel); -static gint gtk_menu_bar_get_popup_delay (GtkMenuShell *menu_shell); - +static gint gtk_menu_bar_get_popup_delay (GtkMenuShell *menu_shell); +static void gtk_menu_bar_move_current (GtkMenuShell *menu_shell, + GtkMenuDirectionType direction); static GtkShadowType get_shadow_type (GtkMenuBar *menubar); -static GtkMenuShellClass *parent_class = NULL; - -GType -gtk_menu_bar_get_type (void) -{ - static GType menu_bar_type = 0; - - if (!menu_bar_type) - { - static const GTypeInfo menu_bar_info = - { - sizeof (GtkMenuBarClass), - NULL, /* base_init */ - NULL, /* base_finalize */ - (GClassInitFunc) gtk_menu_bar_class_init, - NULL, /* class_finalize */ - NULL, /* class_data */ - sizeof (GtkMenuBar), - 0, /* n_preallocs */ - NULL, /* instance_init */ - }; - - menu_bar_type = g_type_register_static (GTK_TYPE_MENU_SHELL, "GtkMenuBar", - &menu_bar_info, 0); - } - - return menu_bar_type; -} +G_DEFINE_TYPE (GtkMenuBar, gtk_menu_bar, GTK_TYPE_MENU_SHELL) static void gtk_menu_bar_class_init (GtkMenuBarClass *class) { - GtkObjectClass *object_class; + GObjectClass *gobject_class; GtkWidgetClass *widget_class; GtkMenuShellClass *menu_shell_class; GtkBindingSet *binding_set; - parent_class = g_type_class_peek_parent (class); - - object_class = (GtkObjectClass*) class; + gobject_class = (GObjectClass*) class; widget_class = (GtkWidgetClass*) class; menu_shell_class = (GtkMenuShellClass*) class; - widget_class->size_request = gtk_menu_bar_size_request; + gobject_class->get_property = gtk_menu_bar_get_property; + gobject_class->set_property = gtk_menu_bar_set_property; + + widget_class->get_preferred_width = gtk_menu_bar_get_preferred_width; + widget_class->get_preferred_height = gtk_menu_bar_get_preferred_height; + widget_class->get_preferred_width_for_height = gtk_menu_bar_get_preferred_width_for_height; + widget_class->get_preferred_height_for_width = gtk_menu_bar_get_preferred_height_for_width; widget_class->size_allocate = gtk_menu_bar_size_allocate; - widget_class->expose_event = gtk_menu_bar_expose; + widget_class->draw = gtk_menu_bar_draw; widget_class->hierarchy_changed = gtk_menu_bar_hierarchy_changed; + gtk_widget_class_set_accessible_role (widget_class, ATK_ROLE_MENU_BAR); + menu_shell_class->submenu_placement = GTK_TOP_BOTTOM; menu_shell_class->get_popup_delay = gtk_menu_bar_get_popup_delay; + menu_shell_class->move_current = gtk_menu_bar_move_current; binding_set = gtk_binding_set_by_class (class); gtk_binding_entry_add_signal (binding_set, - GDK_Left, 0, - "move_current", 1, + GDK_KEY_Left, 0, + "move-current", 1, GTK_TYPE_MENU_DIRECTION_TYPE, GTK_MENU_DIR_PREV); gtk_binding_entry_add_signal (binding_set, - GDK_KP_Left, 0, - "move_current", 1, + GDK_KEY_KP_Left, 0, + "move-current", 1, GTK_TYPE_MENU_DIRECTION_TYPE, GTK_MENU_DIR_PREV); gtk_binding_entry_add_signal (binding_set, - GDK_Right, 0, - "move_current", 1, + GDK_KEY_Right, 0, + "move-current", 1, GTK_TYPE_MENU_DIRECTION_TYPE, GTK_MENU_DIR_NEXT); gtk_binding_entry_add_signal (binding_set, - GDK_KP_Right, 0, - "move_current", 1, + GDK_KEY_KP_Right, 0, + "move-current", 1, GTK_TYPE_MENU_DIRECTION_TYPE, GTK_MENU_DIR_NEXT); gtk_binding_entry_add_signal (binding_set, - GDK_Up, 0, - "move_current", 1, + GDK_KEY_Up, 0, + "move-current", 1, GTK_TYPE_MENU_DIRECTION_TYPE, GTK_MENU_DIR_PARENT); gtk_binding_entry_add_signal (binding_set, - GDK_KP_Up, 0, - "move_current", 1, + GDK_KEY_KP_Up, 0, + "move-current", 1, GTK_TYPE_MENU_DIRECTION_TYPE, GTK_MENU_DIR_PARENT); gtk_binding_entry_add_signal (binding_set, - GDK_Down, 0, - "move_current", 1, + GDK_KEY_Down, 0, + "move-current", 1, GTK_TYPE_MENU_DIRECTION_TYPE, GTK_MENU_DIR_CHILD); gtk_binding_entry_add_signal (binding_set, - GDK_KP_Down, 0, - "move_current", 1, + GDK_KEY_KP_Down, 0, + "move-current", 1, GTK_TYPE_MENU_DIRECTION_TYPE, GTK_MENU_DIR_CHILD); + /** + * GtkMenuBar:pack-direction: + * + * The pack direction of the menubar. It determines how + * menuitems are arranged in the menubar. + * + * Since: 2.8 + */ + g_object_class_install_property (gobject_class, + PROP_PACK_DIRECTION, + g_param_spec_enum ("pack-direction", + P_("Pack direction"), + P_("The pack direction of the menubar"), + GTK_TYPE_PACK_DIRECTION, + GTK_PACK_DIRECTION_LTR, + GTK_PARAM_READWRITE)); + + /** + * GtkMenuBar:child-pack-direction: + * + * The child pack direction of the menubar. It determines how + * the widgets contained in child menuitems are arranged. + * + * Since: 2.8 + */ + g_object_class_install_property (gobject_class, + PROP_CHILD_PACK_DIRECTION, + g_param_spec_enum ("child-pack-direction", + P_("Child Pack direction"), + P_("The child pack direction of the menubar"), + GTK_TYPE_PACK_DIRECTION, + GTK_PACK_DIRECTION_LTR, + GTK_PARAM_READWRITE)); + + gtk_widget_class_install_style_property (widget_class, - g_param_spec_enum ("shadow_type", - _("Shadow type"), - _("Style of bevel around the menubar"), + g_param_spec_enum ("shadow-type", + P_("Shadow type"), + P_("Style of bevel around the menubar"), GTK_TYPE_SHADOW_TYPE, GTK_SHADOW_OUT, - G_PARAM_READABLE)); - + GTK_PARAM_READABLE)); + + /** + * GtkMenuBar:internal-padding: + * + * Amount of border space between the menubar shadow and the menu items + * + * 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 ("internal_padding", - _("Internal padding"), - _("Amount of border space between the menubar shadow and the menu items"), + g_param_spec_int ("internal-padding", + P_("Internal padding"), + P_("Amount of border space between the menubar shadow and the menu items"), 0, G_MAXINT, - DEFAULT_IPADDING, - G_PARAM_READABLE)); - - gtk_settings_install_property (g_param_spec_int ("gtk-menu-bar-popup-delay", - _("Delay before drop down menus appear"), - _("Delay before the submenus of a menu bar appear"), - 0, - G_MAXINT, - 0, - G_PARAM_READWRITE)); + 0, + GTK_PARAM_READABLE | + G_PARAM_DEPRECATED)); + + g_type_class_add_private (gobject_class, sizeof (GtkMenuBarPrivate)); +} + +static void +gtk_menu_bar_init (GtkMenuBar *menu_bar) +{ + GtkStyleContext *context; + + menu_bar->priv = G_TYPE_INSTANCE_GET_PRIVATE (menu_bar, + GTK_TYPE_MENU_BAR, + GtkMenuBarPrivate); + + context = gtk_widget_get_style_context (GTK_WIDGET (menu_bar)); + gtk_style_context_add_class (context, GTK_STYLE_CLASS_MENUBAR); } - + +/** + * gtk_menu_bar_new: + * + * Creates a new #GtkMenuBar + * + * Returns: the new menu bar, as a #GtkWidget + */ GtkWidget* gtk_menu_bar_new (void) { return g_object_new (GTK_TYPE_MENU_BAR, NULL); } +static void +gtk_menu_bar_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GtkMenuBar *menubar = GTK_MENU_BAR (object); + + switch (prop_id) + { + case PROP_PACK_DIRECTION: + gtk_menu_bar_set_pack_direction (menubar, g_value_get_enum (value)); + break; + case PROP_CHILD_PACK_DIRECTION: + gtk_menu_bar_set_child_pack_direction (menubar, g_value_get_enum (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gtk_menu_bar_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GtkMenuBar *menubar = GTK_MENU_BAR (object); + + switch (prop_id) + { + case PROP_PACK_DIRECTION: + g_value_set_enum (value, gtk_menu_bar_get_pack_direction (menubar)); + break; + case PROP_CHILD_PACK_DIRECTION: + g_value_set_enum (value, gtk_menu_bar_get_child_pack_direction (menubar)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +get_preferred_size_for_size (GtkWidget *widget, + GtkOrientation orientation, + gint size, + gint *minimum, + gint *natural) +{ + if (orientation == GTK_ORIENTATION_HORIZONTAL) + if (size < 0) + gtk_widget_get_preferred_width (widget, minimum, natural); + else + gtk_widget_get_preferred_width_for_height (widget, size, minimum, natural); + else + if (size < 0) + gtk_widget_get_preferred_height (widget, minimum, natural); + else + gtk_widget_get_preferred_height_for_width (widget, size, minimum, natural); +} + static void gtk_menu_bar_size_request (GtkWidget *widget, - GtkRequisition *requisition) + GtkOrientation orientation, + gint size, + gint *minimum, + gint *natural) { GtkMenuBar *menu_bar; + GtkMenuBarPrivate *priv; GtkMenuShell *menu_shell; GtkWidget *child; GList *children; - gint nchildren; - GtkRequisition child_requisition; - gint ipadding; + guint border_width; + gboolean use_toggle_size, use_maximize; + gint child_minimum, child_natural; + GtkStyleContext *context; + GtkBorder border; + GtkStateFlags flags; - g_return_if_fail (GTK_IS_MENU_BAR (widget)); - g_return_if_fail (requisition != NULL); + *minimum = 0; + *natural = 0; - requisition->width = 0; - requisition->height = 0; - - if (GTK_WIDGET_VISIBLE (widget)) - { - menu_bar = GTK_MENU_BAR (widget); - menu_shell = GTK_MENU_SHELL (widget); + menu_bar = GTK_MENU_BAR (widget); + menu_shell = GTK_MENU_SHELL (widget); + priv = menu_bar->priv; - nchildren = 0; - children = menu_shell->children; + children = menu_shell->priv->children; - while (children) - { - child = children->data; - children = children->next; + if (priv->child_pack_direction == GTK_PACK_DIRECTION_LTR || + priv->child_pack_direction == GTK_PACK_DIRECTION_RTL) + use_toggle_size = (orientation == GTK_ORIENTATION_HORIZONTAL); + else + use_toggle_size = (orientation == GTK_ORIENTATION_VERTICAL); - if (GTK_WIDGET_VISIBLE (child)) - { + if (priv->pack_direction == GTK_PACK_DIRECTION_LTR || + priv->pack_direction == GTK_PACK_DIRECTION_RTL) + use_maximize = (orientation == GTK_ORIENTATION_VERTICAL); + else + use_maximize = (orientation == GTK_ORIENTATION_HORIZONTAL); + + while (children) + { + child = children->data; + children = children->next; + + if (gtk_widget_get_visible (child)) + { + get_preferred_size_for_size (child, orientation, size, &child_minimum, &child_natural); + + if (use_toggle_size) + { gint toggle_size; - - GTK_MENU_ITEM (child)->show_submenu_indicator = FALSE; - gtk_widget_size_request (child, &child_requisition); + gtk_menu_item_toggle_size_request (GTK_MENU_ITEM (child), &toggle_size); - - requisition->width += child_requisition.width; - requisition->width += toggle_size; - - requisition->height = MAX (requisition->height, child_requisition.height); - /* Support for the right justified help menu */ - if ((children == NULL) && GTK_IS_MENU_ITEM(child) && - GTK_MENU_ITEM(child)->right_justify) - { - requisition->width += CHILD_SPACING; - } - - nchildren += 1; - } - } - gtk_widget_style_get (widget, "internal_padding", &ipadding, NULL); - - requisition->width += (GTK_CONTAINER (menu_bar)->border_width + - ipadding + - BORDER_SPACING) * 2; - requisition->height += (GTK_CONTAINER (menu_bar)->border_width + - ipadding + - BORDER_SPACING) * 2; + child_minimum += toggle_size; + child_natural += toggle_size; + } + + if (use_maximize) + { + *minimum = MAX (*minimum, child_minimum); + *natural = MAX (*natural, child_natural); + } + else + { + *minimum += child_minimum; + *natural += child_natural; + } + } + } - if (get_shadow_type (menu_bar) != GTK_SHADOW_NONE) - { - requisition->width += widget->style->xthickness * 2; - requisition->height += widget->style->ythickness * 2; - } + context = gtk_widget_get_style_context (widget); + flags = gtk_widget_get_state_flags (widget); + gtk_style_context_get_padding (context, flags, &border); - if (nchildren > 0) - requisition->width += 2 * CHILD_SPACING * (nchildren - 1); + if (orientation == GTK_ORIENTATION_HORIZONTAL) + { + *minimum += border.left + border.right; + *natural += border.left + border.right; } + else + { + *minimum += border.top + border.bottom; + *natural += border.top + border.bottom; + } + + border_width = gtk_container_get_border_width (GTK_CONTAINER (menu_bar)); + *minimum += border_width * 2; + *natural += border_width * 2; + + if (get_shadow_type (menu_bar) != GTK_SHADOW_NONE) + { + gtk_style_context_get_border (context, flags, &border); + + if (orientation == GTK_ORIENTATION_HORIZONTAL) + { + *minimum += border.left + border.right; + *natural += border.left + border.right; + } + else + { + *minimum += border.top + border.bottom; + *natural += border.top + border.bottom; + } + } +} + +static void +gtk_menu_bar_get_preferred_width (GtkWidget *widget, + gint *minimum, + gint *natural) +{ + gtk_menu_bar_size_request (widget, GTK_ORIENTATION_HORIZONTAL, -1, minimum, natural); +} + +static void +gtk_menu_bar_get_preferred_height (GtkWidget *widget, + gint *minimum, + gint *natural) +{ + gtk_menu_bar_size_request (widget, GTK_ORIENTATION_VERTICAL, -1, minimum, natural); +} + +static void +gtk_menu_bar_get_preferred_width_for_height (GtkWidget *widget, + gint height, + gint *minimum, + gint *natural) +{ + gtk_menu_bar_size_request (widget, GTK_ORIENTATION_HORIZONTAL, height, minimum, natural); +} + +static void +gtk_menu_bar_get_preferred_height_for_width (GtkWidget *widget, + gint width, + gint *minimum, + gint *natural) +{ + gtk_menu_bar_size_request (widget, GTK_ORIENTATION_VERTICAL, width, minimum, natural); } static void @@ -264,127 +476,196 @@ gtk_menu_bar_size_allocate (GtkWidget *widget, { GtkMenuBar *menu_bar; GtkMenuShell *menu_shell; + GtkMenuBarPrivate *priv; GtkWidget *child; GList *children; - GtkAllocation child_allocation; - GtkRequisition child_requisition; - guint offset; - gint ipadding; - GtkTextDirection direction; - gint ltr_x; + GtkAllocation remaining_space; + guint border_width; + GArray *requested_sizes; + gint toggle_size; + guint i; g_return_if_fail (GTK_IS_MENU_BAR (widget)); g_return_if_fail (allocation != NULL); menu_bar = GTK_MENU_BAR (widget); menu_shell = GTK_MENU_SHELL (widget); + priv = menu_bar->priv; - direction = gtk_widget_get_direction (widget); + gtk_widget_set_allocation (widget, allocation); - widget->allocation = *allocation; - if (GTK_WIDGET_REALIZED (widget)) - gdk_window_move_resize (widget->window, + if (gtk_widget_get_realized (widget)) + gdk_window_move_resize (gtk_widget_get_window (widget), allocation->x, allocation->y, allocation->width, allocation->height); - gtk_widget_style_get (widget, "internal_padding", &ipadding, NULL); - - if (menu_shell->children) + if (menu_shell->priv->children) { - child_allocation.x = (GTK_CONTAINER (menu_bar)->border_width + - ipadding + - BORDER_SPACING); - child_allocation.y = (GTK_CONTAINER (menu_bar)->border_width + - ipadding + - BORDER_SPACING); + GtkStyleContext *context; + GtkStateFlags flags; + GtkBorder border; + + context = gtk_widget_get_style_context (widget); + flags = gtk_widget_get_state_flags (widget); + gtk_style_context_get_padding (context, flags, &border); + + border_width = gtk_container_get_border_width (GTK_CONTAINER (menu_bar)); + + remaining_space.x = (border_width + border.left); + remaining_space.y = (border_width + border.top); + remaining_space.width = allocation->width - + 2 * (border_width) - border.left - border.right; + remaining_space.height = allocation->height - + 2 * (border_width) - border.top - border.bottom; if (get_shadow_type (menu_bar) != GTK_SHADOW_NONE) { - child_allocation.x += widget->style->xthickness; - child_allocation.y += widget->style->ythickness; + gtk_style_context_get_border (context, flags, &border); + + remaining_space.x += border.left; + remaining_space.y += border.top; + remaining_space.width -= border.left + border.right; + remaining_space.height -= border.top + border.bottom; } - child_allocation.height = MAX (1, (gint)allocation->height - child_allocation.y * 2); - - offset = child_allocation.x; /* Window edge to menubar start */ - ltr_x = child_allocation.x; + requested_sizes = g_array_new (FALSE, FALSE, sizeof (GtkRequestedSize)); - children = menu_shell->children; - while (children) + if (priv->pack_direction == GTK_PACK_DIRECTION_LTR || + priv->pack_direction == GTK_PACK_DIRECTION_RTL) { - gint toggle_size; + int size = remaining_space.width; + gboolean ltr = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR) == (priv->pack_direction == GTK_PACK_DIRECTION_LTR); - child = children->data; - children = children->next; + for (children = menu_shell->priv->children; children; children = children->next) + { + GtkRequestedSize request; + child = children->data; - gtk_menu_item_toggle_size_request (GTK_MENU_ITEM (child), - &toggle_size); - gtk_widget_get_child_requisition (child, &child_requisition); + if (!gtk_widget_get_visible (child)) + continue; - child_requisition.width += toggle_size; - - /* Support for the right justified help menu */ - if ((children == NULL) && (GTK_IS_MENU_ITEM(child)) - && (GTK_MENU_ITEM(child)->right_justify)) - { - ltr_x = allocation->width - - child_requisition.width - offset; - } - if (GTK_WIDGET_VISIBLE (child)) - { - if (direction == GTK_TEXT_DIR_LTR) - child_allocation.x = ltr_x; - else - child_allocation.x = allocation->width - - child_requisition.width - ltr_x; + request.data = child; + gtk_widget_get_preferred_width_for_height (child, + remaining_space.height, + &request.minimum_size, + &request.natural_size); + gtk_menu_item_toggle_size_request (GTK_MENU_ITEM (child), + &toggle_size); + request.minimum_size += toggle_size; + request.natural_size += toggle_size; - child_allocation.width = child_requisition.width; + gtk_menu_item_toggle_size_allocate (GTK_MENU_ITEM (child), toggle_size); - gtk_menu_item_toggle_size_allocate (GTK_MENU_ITEM (child), - toggle_size); - gtk_widget_size_allocate (child, &child_allocation); + g_array_append_val (requested_sizes, request); - ltr_x += child_allocation.width + CHILD_SPACING * 2; - } + size -= request.minimum_size; + } + + size = gtk_distribute_natural_allocation (size, + requested_sizes->len, + (GtkRequestedSize *) requested_sizes->data); + + for (i = 0; i < requested_sizes->len; i++) + { + GtkAllocation child_allocation = remaining_space; + GtkRequestedSize *request = &g_array_index (requested_sizes, GtkRequestedSize, i); + + child_allocation.width = request->minimum_size; + remaining_space.width -= request->minimum_size; + + if (i + 1 == requested_sizes->len && GTK_IS_MENU_ITEM (request->data) && + GTK_MENU_ITEM (request->data)->priv->right_justify) + ltr = !ltr; + + if (ltr) + remaining_space.x += request->minimum_size; + else + child_allocation.x += remaining_space.width; + + gtk_widget_size_allocate (request->data, &child_allocation); + } } - } -} + else + { + int size = remaining_space.height; + gboolean ttb = (priv->pack_direction == GTK_PACK_DIRECTION_TTB); -static void -gtk_menu_bar_paint (GtkWidget *widget, GdkRectangle *area) -{ - g_return_if_fail (GTK_IS_MENU_BAR (widget)); + for (children = menu_shell->priv->children; children; children = children->next) + { + GtkRequestedSize request; + child = children->data; - if (GTK_WIDGET_DRAWABLE (widget)) - { - gint border; + if (!gtk_widget_get_visible (child)) + continue; - border = GTK_CONTAINER (widget)->border_width; - - gtk_paint_box (widget->style, - widget->window, - GTK_WIDGET_STATE (widget), - get_shadow_type (GTK_MENU_BAR (widget)), - area, widget, "menubar", - border, border, - widget->allocation.width - border * 2, - widget->allocation.height - border * 2); + request.data = child; + gtk_widget_get_preferred_height_for_width (child, + remaining_space.width, + &request.minimum_size, + &request.natural_size); + gtk_menu_item_toggle_size_request (GTK_MENU_ITEM (child), + &toggle_size); + request.minimum_size += toggle_size; + request.natural_size += toggle_size; + + gtk_menu_item_toggle_size_allocate (GTK_MENU_ITEM (child), toggle_size); + + g_array_append_val (requested_sizes, request); + + size -= request.minimum_size; + } + + size = gtk_distribute_natural_allocation (size, + requested_sizes->len, + (GtkRequestedSize *) requested_sizes->data); + + for (i = 0; i < requested_sizes->len; i++) + { + GtkAllocation child_allocation = remaining_space; + GtkRequestedSize *request = &g_array_index (requested_sizes, GtkRequestedSize, i); + + child_allocation.height = request->minimum_size; + remaining_space.height -= request->minimum_size; + + if (i + 1 == requested_sizes->len && GTK_IS_MENU_ITEM (request->data) && + GTK_MENU_ITEM (request->data)->priv->right_justify) + ttb = !ttb; + + if (ttb) + remaining_space.y += request->minimum_size; + else + child_allocation.y += remaining_space.height; + + gtk_widget_size_allocate (request->data, &child_allocation); + } + } + + g_array_free (requested_sizes, TRUE); } } static gint -gtk_menu_bar_expose (GtkWidget *widget, - GdkEventExpose *event) +gtk_menu_bar_draw (GtkWidget *widget, + cairo_t *cr) { - g_return_val_if_fail (GTK_IS_MENU_BAR (widget), FALSE); - g_return_val_if_fail (event != NULL, FALSE); + GtkStyleContext *context; + int border; - if (GTK_WIDGET_DRAWABLE (widget)) - { - gtk_menu_bar_paint (widget, &event->area); + border = gtk_container_get_border_width (GTK_CONTAINER (widget)); + context = gtk_widget_get_style_context (widget); - (* GTK_WIDGET_CLASS (parent_class)->expose_event) (widget, event); - } + gtk_render_background (context, cr, + border, border, + gtk_widget_get_allocated_width (widget) - border * 2, + gtk_widget_get_allocated_height (widget) - border * 2); + + if (get_shadow_type (GTK_MENU_BAR (widget)) != GTK_SHADOW_NONE) + gtk_render_frame (context, cr, + border, border, + gtk_widget_get_allocated_width (widget) - border * 2, + gtk_widget_get_allocated_height (widget) - border * 2); + + GTK_WIDGET_CLASS (gtk_menu_bar_parent_class)->draw (widget, cr); return FALSE; } @@ -410,10 +691,10 @@ get_viewable_menu_bars (GtkWindow *window) while (widget) { - if (!GTK_WIDGET_MAPPED (widget)) + if (!gtk_widget_get_mapped (widget)) viewable = FALSE; - - widget = widget->parent; + + widget = gtk_widget_get_parent (widget); } if (viewable) @@ -427,7 +708,7 @@ static void set_menu_bars (GtkWindow *window, GList *menubars) { - g_object_set_data (G_OBJECT (window), "gtk-menu-bar-list", menubars); + g_object_set_data (G_OBJECT (window), I_("gtk-menu-bar-list"), menubars); } static gboolean @@ -439,11 +720,10 @@ window_key_press_handler (GtkWidget *widget, gboolean retval = FALSE; g_object_get (gtk_widget_get_settings (widget), - "gtk-menu-bar-accel", - &accel, + "gtk-menu-bar-accel", &accel, NULL); - if (accel) + if (accel && *accel) { guint keyval = 0; GdkModifierType mods = 0; @@ -472,7 +752,7 @@ window_key_press_handler (GtkWidget *widget, { GtkMenuShell *menu_shell = GTK_MENU_SHELL (menubars->data); - _gtk_menu_shell_activate (menu_shell); + _gtk_menu_shell_set_keyboard_mode (menu_shell, TRUE); gtk_menu_shell_select_first (menu_shell, FALSE); g_list_free (menubars); @@ -480,10 +760,10 @@ window_key_press_handler (GtkWidget *widget, retval = TRUE; } } - - g_free (accel); } + g_free (accel); + return retval; } @@ -496,7 +776,7 @@ add_to_window (GtkWindow *window, if (!menubars) { g_signal_connect (window, - "key_press_event", + "key-press-event", G_CALLBACK (window_key_press_handler), NULL); } @@ -510,9 +790,6 @@ remove_from_window (GtkWindow *window, { GList *menubars = get_menu_bars (window); - menubars = g_object_get_data (G_OBJECT (window), - "gtk-menu-bar-list"); - menubars = g_list_remove (menubars, menubar); if (!menubars) @@ -539,7 +816,7 @@ gtk_menu_bar_hierarchy_changed (GtkWidget *widget, if (old_toplevel) remove_from_window (GTK_WINDOW (old_toplevel), menubar); - if (GTK_WIDGET_TOPLEVEL (toplevel)) + if (gtk_widget_is_toplevel (toplevel)) add_to_window (GTK_WINDOW (toplevel), menubar); } @@ -557,7 +834,7 @@ _gtk_menu_bar_cycle_focus (GtkMenuBar *menubar, GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (menubar)); GtkMenuItem *to_activate = NULL; - if (GTK_WIDGET_TOPLEVEL (toplevel)) + if (gtk_widget_is_toplevel (toplevel)) { GList *tmp_menubars = get_viewable_menu_bars (GTK_WINDOW (toplevel)); GList *menubars; @@ -574,15 +851,15 @@ _gtk_menu_bar_cycle_focus (GtkMenuBar *menubar, if (current && current->next) { GtkMenuShell *new_menushell = GTK_MENU_SHELL (current->next->data); - if (new_menushell->children) - to_activate = new_menushell->children->data; + if (new_menushell->priv->children) + to_activate = new_menushell->priv->children->data; } } g_list_free (menubars); } - g_signal_emit_by_name (menubar, "cancel", 0); + gtk_menu_shell_cancel (GTK_MENU_SHELL (menubar)); if (to_activate) g_signal_emit_by_name (to_activate, "activate_item"); @@ -594,7 +871,7 @@ get_shadow_type (GtkMenuBar *menubar) GtkShadowType shadow_type = GTK_SHADOW_OUT; gtk_widget_style_get (GTK_WIDGET (menubar), - "shadow_type", &shadow_type, + "shadow-type", &shadow_type, NULL); return shadow_type; @@ -611,3 +888,200 @@ gtk_menu_bar_get_popup_delay (GtkMenuShell *menu_shell) return popup_delay; } + +static void +gtk_menu_bar_move_current (GtkMenuShell *menu_shell, + GtkMenuDirectionType direction) +{ + GtkMenuBar *menubar = GTK_MENU_BAR (menu_shell); + GtkTextDirection text_dir; + GtkPackDirection pack_dir; + + text_dir = gtk_widget_get_direction (GTK_WIDGET (menubar)); + pack_dir = gtk_menu_bar_get_pack_direction (menubar); + + if (pack_dir == GTK_PACK_DIRECTION_LTR || pack_dir == GTK_PACK_DIRECTION_RTL) + { + if ((text_dir == GTK_TEXT_DIR_RTL) == (pack_dir == GTK_PACK_DIRECTION_LTR)) + { + switch (direction) + { + case GTK_MENU_DIR_PREV: + direction = GTK_MENU_DIR_NEXT; + break; + case GTK_MENU_DIR_NEXT: + direction = GTK_MENU_DIR_PREV; + break; + default: ; + } + } + } + else + { + switch (direction) + { + case GTK_MENU_DIR_PARENT: + if ((text_dir == GTK_TEXT_DIR_LTR) == (pack_dir == GTK_PACK_DIRECTION_TTB)) + direction = GTK_MENU_DIR_PREV; + else + direction = GTK_MENU_DIR_NEXT; + break; + case GTK_MENU_DIR_CHILD: + if ((text_dir == GTK_TEXT_DIR_LTR) == (pack_dir == GTK_PACK_DIRECTION_TTB)) + direction = GTK_MENU_DIR_NEXT; + else + direction = GTK_MENU_DIR_PREV; + break; + case GTK_MENU_DIR_PREV: + if (text_dir == GTK_TEXT_DIR_RTL) + direction = GTK_MENU_DIR_CHILD; + else + direction = GTK_MENU_DIR_PARENT; + break; + case GTK_MENU_DIR_NEXT: + if (text_dir == GTK_TEXT_DIR_RTL) + direction = GTK_MENU_DIR_PARENT; + else + direction = GTK_MENU_DIR_CHILD; + break; + default: ; + } + } + + GTK_MENU_SHELL_CLASS (gtk_menu_bar_parent_class)->move_current (menu_shell, direction); +} + +/** + * gtk_menu_bar_get_pack_direction: + * @menubar: a #GtkMenuBar + * + * Retrieves the current pack direction of the menubar. + * See gtk_menu_bar_set_pack_direction(). + * + * Return value: the pack direction + * + * Since: 2.8 + */ +GtkPackDirection +gtk_menu_bar_get_pack_direction (GtkMenuBar *menubar) +{ + g_return_val_if_fail (GTK_IS_MENU_BAR (menubar), + GTK_PACK_DIRECTION_LTR); + + return menubar->priv->pack_direction; +} + +/** + * gtk_menu_bar_set_pack_direction: + * @menubar: a #GtkMenuBar + * @pack_dir: a new #GtkPackDirection + * + * Sets how items should be packed inside a menubar. + * + * Since: 2.8 + */ +void +gtk_menu_bar_set_pack_direction (GtkMenuBar *menubar, + GtkPackDirection pack_dir) +{ + GtkMenuBarPrivate *priv; + GList *l; + + g_return_if_fail (GTK_IS_MENU_BAR (menubar)); + + priv = menubar->priv; + + if (priv->pack_direction != pack_dir) + { + priv->pack_direction = pack_dir; + + gtk_widget_queue_resize (GTK_WIDGET (menubar)); + + for (l = GTK_MENU_SHELL (menubar)->priv->children; l; l = l->next) + gtk_widget_queue_resize (GTK_WIDGET (l->data)); + + g_object_notify (G_OBJECT (menubar), "pack-direction"); + } +} + +/** + * gtk_menu_bar_get_child_pack_direction: + * @menubar: a #GtkMenuBar + * + * Retrieves the current child pack direction of the menubar. + * See gtk_menu_bar_set_child_pack_direction(). + * + * Return value: the child pack direction + * + * Since: 2.8 + */ +GtkPackDirection +gtk_menu_bar_get_child_pack_direction (GtkMenuBar *menubar) +{ + g_return_val_if_fail (GTK_IS_MENU_BAR (menubar), + GTK_PACK_DIRECTION_LTR); + + return menubar->priv->child_pack_direction; +} + +/** + * gtk_menu_bar_set_child_pack_direction: + * @menubar: a #GtkMenuBar + * @child_pack_dir: a new #GtkPackDirection + * + * Sets how widgets should be packed inside the children of a menubar. + * + * Since: 2.8 + */ +void +gtk_menu_bar_set_child_pack_direction (GtkMenuBar *menubar, + GtkPackDirection child_pack_dir) +{ + GtkMenuBarPrivate *priv; + GList *l; + + g_return_if_fail (GTK_IS_MENU_BAR (menubar)); + + priv = menubar->priv; + + if (priv->child_pack_direction != child_pack_dir) + { + priv->child_pack_direction = child_pack_dir; + + gtk_widget_queue_resize (GTK_WIDGET (menubar)); + + for (l = GTK_MENU_SHELL (menubar)->priv->children; l; l = l->next) + gtk_widget_queue_resize (GTK_WIDGET (l->data)); + + g_object_notify (G_OBJECT (menubar), "child-pack-direction"); + } +} + +/** + * gtk_menu_bar_new_from_model: + * @model: a #GMenuModel + * + * Creates a new #GtkMenuBar 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 bar belongs - typically + * by means of being contained within the #GtkApplicationWindows + * widget hierarchy. + * + * Returns: a new #GtkMenuBar + * + * Since: 3.4 + */ +GtkWidget * +gtk_menu_bar_new_from_model (GMenuModel *model) +{ + GtkWidget *menubar; + + g_return_val_if_fail (G_IS_MENU_MODEL (model), NULL); + + menubar = gtk_menu_bar_new (); + gtk_menu_shell_bind_model (GTK_MENU_SHELL (menubar), model, NULL, FALSE); + + return menubar; +}