| /* GTK - The GIMP Toolkit |
| * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * 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, see <http://www.gnu.org/licenses/>. |
| */ |
| |
| /* |
| * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS |
| * file for a list of people on the GTK+ Team. See the ChangeLog |
| * files for a list of changes. These files are distributed with |
| * GTK+ at ftp://ftp.gtk.org/pub/gtk/. |
| */ |
| |
| /** |
| * SECTION:gtkbbox |
| * @Short_description: A container for arranging buttons |
| * @Title: GtkButtonBox |
| * |
| * A button box should be used to provide a consistent layout of buttons |
| * throughout your application. The layout/spacing can be altered by the |
| * programmer, or if desired, by the user to alter the 'feel' of a |
| * program to a small degree. |
| * |
| * gtk_button_box_get_layout() and gtk_button_box_set_layout() retrieve and |
| * alter the method used to spread the buttons in a button box across the |
| * container, respectively. |
| * |
| * The main purpose of GtkButtonBox is to make sure the children have all the |
| * same size. GtkButtonBox gives all children the same size, but it does allow |
| * 'outliers' to keep their own larger size. To force all children to be |
| * strictly the same size without exceptions, you can set the |
| * #GtkButtonBox:homogeneous property to %TRUE. |
| * |
| * To excempt individual children from homogeneous sizing regardless of their |
| * 'outlier' status, you can set the #GtkButtonBox:non-homogeneous child |
| * property. |
| */ |
| |
| #include "config.h" |
| |
| #include "gtkbbox.h" |
| |
| #include "gtkboxprivate.h" |
| #include "gtkorientable.h" |
| #include "gtktypebuiltins.h" |
| #include "gtkprivate.h" |
| #include "gtksizerequest.h" |
| |
| #include "gtkintl.h" |
| |
| |
| struct _GtkButtonBoxPrivate |
| { |
| GtkButtonBoxStyle layout_style; |
| }; |
| |
| enum { |
| PROP_0, |
| PROP_LAYOUT_STYLE |
| }; |
| |
| enum { |
| CHILD_PROP_0, |
| CHILD_PROP_SECONDARY, |
| CHILD_PROP_NONHOMOGENEOUS |
| }; |
| |
| #define GTK_BOX_SECONDARY_CHILD "gtk-box-secondary-child" |
| #define GTK_BOX_NON_HOMOGENEOUS "gtk-box-non-homogeneous" |
| |
| static void gtk_button_box_set_property (GObject *object, |
| guint prop_id, |
| const GValue *value, |
| GParamSpec *pspec); |
| static void gtk_button_box_get_property (GObject *object, |
| guint prop_id, |
| GValue *value, |
| GParamSpec *pspec); |
| static void gtk_button_box_get_preferred_width (GtkWidget *widget, |
| gint *minimum, |
| gint *natural); |
| static void gtk_button_box_get_preferred_height (GtkWidget *widget, |
| gint *minimum, |
| gint *natural); |
| static void gtk_button_box_get_preferred_width_for_height (GtkWidget *widget, |
| gint height, |
| gint *minimum, |
| gint *natural); |
| static void gtk_button_box_get_preferred_height_for_width (GtkWidget *widget, |
| gint width, |
| gint *minimum, |
| gint *natural); |
| |
| static void gtk_button_box_size_allocate (GtkWidget *widget, |
| GtkAllocation *allocation); |
| static void gtk_button_box_remove (GtkContainer *container, |
| GtkWidget *widget); |
| static void gtk_button_box_set_child_property (GtkContainer *container, |
| GtkWidget *child, |
| guint property_id, |
| const GValue *value, |
| GParamSpec *pspec); |
| static void gtk_button_box_get_child_property (GtkContainer *container, |
| GtkWidget *child, |
| guint property_id, |
| GValue *value, |
| GParamSpec *pspec); |
| |
| #define DEFAULT_CHILD_MIN_WIDTH 85 |
| #define DEFAULT_CHILD_MIN_HEIGHT 27 |
| #define DEFAULT_CHILD_IPAD_X 4 |
| #define DEFAULT_CHILD_IPAD_Y 0 |
| #define DEFAULT_LAYOUT_STYLE GTK_BUTTONBOX_EDGE |
| |
| G_DEFINE_TYPE (GtkButtonBox, gtk_button_box, GTK_TYPE_BOX) |
| |
| static void |
| gtk_button_box_class_init (GtkButtonBoxClass *class) |
| { |
| GtkWidgetClass *widget_class; |
| GObjectClass *gobject_class; |
| GtkContainerClass *container_class; |
| |
| gobject_class = G_OBJECT_CLASS (class); |
| widget_class = (GtkWidgetClass*) class; |
| container_class = (GtkContainerClass*) class; |
| |
| gobject_class->set_property = gtk_button_box_set_property; |
| gobject_class->get_property = gtk_button_box_get_property; |
| |
| widget_class->get_preferred_width = gtk_button_box_get_preferred_width; |
| widget_class->get_preferred_height = gtk_button_box_get_preferred_height; |
| widget_class->get_preferred_width_for_height = gtk_button_box_get_preferred_width_for_height; |
| widget_class->get_preferred_height_for_width = gtk_button_box_get_preferred_height_for_width; |
| widget_class->size_allocate = gtk_button_box_size_allocate; |
| |
| container_class->remove = gtk_button_box_remove; |
| container_class->set_child_property = gtk_button_box_set_child_property; |
| container_class->get_child_property = gtk_button_box_get_child_property; |
| gtk_container_class_handle_border_width (container_class); |
| |
| /* FIXME we need to override the "spacing" property on GtkBox once |
| * libgobject allows that. |
| */ |
| gtk_widget_class_install_style_property (widget_class, |
| g_param_spec_int ("child-min-width", |
| P_("Minimum child width"), |
| P_("Minimum width of buttons inside the box"), |
| 0, |
| G_MAXINT, |
| DEFAULT_CHILD_MIN_WIDTH, |
| GTK_PARAM_READABLE)); |
| |
| gtk_widget_class_install_style_property (widget_class, |
| g_param_spec_int ("child-min-height", |
| P_("Minimum child height"), |
| P_("Minimum height of buttons inside the box"), |
| 0, |
| G_MAXINT, |
| DEFAULT_CHILD_MIN_HEIGHT, |
| GTK_PARAM_READABLE)); |
| |
| gtk_widget_class_install_style_property (widget_class, |
| g_param_spec_int ("child-internal-pad-x", |
| P_("Child internal width padding"), |
| P_("Amount to increase child's size on either side"), |
| 0, |
| G_MAXINT, |
| DEFAULT_CHILD_IPAD_X, |
| GTK_PARAM_READABLE)); |
| |
| gtk_widget_class_install_style_property (widget_class, |
| g_param_spec_int ("child-internal-pad-y", |
| P_("Child internal height padding"), |
| P_("Amount to increase child's size on the top and bottom"), |
| 0, |
| G_MAXINT, |
| DEFAULT_CHILD_IPAD_Y, |
| GTK_PARAM_READABLE)); |
| g_object_class_install_property (gobject_class, |
| PROP_LAYOUT_STYLE, |
| g_param_spec_enum ("layout-style", |
| P_("Layout style"), |
| P_("How to lay out the buttons in the box. Possible values are: spread, edge, start and end"), |
| GTK_TYPE_BUTTON_BOX_STYLE, |
| DEFAULT_LAYOUT_STYLE, |
| GTK_PARAM_READWRITE)); |
| |
| gtk_container_class_install_child_property (container_class, |
| CHILD_PROP_SECONDARY, |
| g_param_spec_boolean ("secondary", |
| P_("Secondary"), |
| P_("If TRUE, the child appears in a secondary group of children, suitable for, e.g., help buttons"), |
| FALSE, |
| GTK_PARAM_READWRITE)); |
| |
| gtk_container_class_install_child_property (container_class, |
| CHILD_PROP_NONHOMOGENEOUS, |
| g_param_spec_boolean ("non-homogeneous", |
| P_("Non-Homogeneous"), |
| P_("If TRUE, the child will not be subject to homogeneous sizing"), |
| FALSE, |
| GTK_PARAM_READWRITE)); |
| |
| g_type_class_add_private (class, sizeof (GtkButtonBoxPrivate)); |
| } |
| |
| static void |
| gtk_button_box_init (GtkButtonBox *button_box) |
| { |
| GtkButtonBoxPrivate *priv; |
| |
| button_box->priv = G_TYPE_INSTANCE_GET_PRIVATE (button_box, |
| GTK_TYPE_BUTTON_BOX, |
| GtkButtonBoxPrivate); |
| priv = button_box->priv; |
| |
| gtk_box_set_spacing (GTK_BOX (button_box), 0); |
| priv->layout_style = DEFAULT_LAYOUT_STYLE; |
| } |
| |
| static void |
| gtk_button_box_set_property (GObject *object, |
| guint prop_id, |
| const GValue *value, |
| GParamSpec *pspec) |
| { |
| switch (prop_id) |
| { |
| case PROP_LAYOUT_STYLE: |
| gtk_button_box_set_layout (GTK_BUTTON_BOX (object), |
| g_value_get_enum (value)); |
| break; |
| default: |
| G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
| break; |
| } |
| } |
| |
| static void |
| gtk_button_box_get_property (GObject *object, |
| guint prop_id, |
| GValue *value, |
| GParamSpec *pspec) |
| { |
| GtkButtonBoxPrivate *priv = GTK_BUTTON_BOX (object)->priv; |
| |
| switch (prop_id) |
| { |
| case PROP_LAYOUT_STYLE: |
| g_value_set_enum (value, priv->layout_style); |
| break; |
| default: |
| G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
| break; |
| } |
| } |
| |
| static void |
| gtk_button_box_set_child_property (GtkContainer *container, |
| GtkWidget *child, |
| guint property_id, |
| const GValue *value, |
| GParamSpec *pspec) |
| { |
| switch (property_id) |
| { |
| case CHILD_PROP_SECONDARY: |
| gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (container), child, |
| g_value_get_boolean (value)); |
| break; |
| case CHILD_PROP_NONHOMOGENEOUS: |
| gtk_button_box_set_child_non_homogeneous (GTK_BUTTON_BOX (container), child, |
| g_value_get_boolean (value)); |
| break; |
| default: |
| GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec); |
| break; |
| } |
| } |
| |
| static void |
| gtk_button_box_get_child_property (GtkContainer *container, |
| GtkWidget *child, |
| guint property_id, |
| GValue *value, |
| GParamSpec *pspec) |
| { |
| switch (property_id) |
| { |
| case CHILD_PROP_SECONDARY: |
| g_value_set_boolean (value, |
| gtk_button_box_get_child_secondary (GTK_BUTTON_BOX (container), |
| child)); |
| break; |
| case CHILD_PROP_NONHOMOGENEOUS: |
| g_value_set_boolean (value, |
| gtk_button_box_get_child_non_homogeneous (GTK_BUTTON_BOX (container), |
| child)); |
| break; |
| default: |
| GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec); |
| break; |
| } |
| } |
| |
| static void |
| gtk_button_box_remove (GtkContainer *container, |
| GtkWidget *widget) |
| { |
| /* clear is_secondary and nonhomogeneous flag in case the widget |
| * is added to another container |
| */ |
| gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (container), widget, FALSE); |
| gtk_button_box_set_child_non_homogeneous (GTK_BUTTON_BOX (container), widget, FALSE); |
| |
| GTK_CONTAINER_CLASS (gtk_button_box_parent_class)->remove (container, widget); |
| } |
| |
| /** |
| * gtk_button_box_set_layout: |
| * @widget: a #GtkButtonBox |
| * @layout_style: the new layout style |
| * |
| * Changes the way buttons are arranged in their container. |
| */ |
| void |
| gtk_button_box_set_layout (GtkButtonBox *widget, |
| GtkButtonBoxStyle layout_style) |
| { |
| GtkButtonBoxPrivate *priv; |
| |
| g_return_if_fail (GTK_IS_BUTTON_BOX (widget)); |
| |
| priv = widget->priv; |
| |
| if (priv->layout_style != layout_style) |
| { |
| priv->layout_style = layout_style; |
| g_object_notify (G_OBJECT (widget), "layout-style"); |
| gtk_widget_queue_resize (GTK_WIDGET (widget)); |
| } |
| } |
| |
| /** |
| * gtk_button_box_get_layout: |
| * @widget: a #GtkButtonBox |
| * |
| * Retrieves the method being used to arrange the buttons in a button box. |
| * |
| * Returns: the method used to lay out buttons in @widget. |
| */ |
| GtkButtonBoxStyle |
| gtk_button_box_get_layout (GtkButtonBox *widget) |
| { |
| g_return_val_if_fail (GTK_IS_BUTTON_BOX (widget), DEFAULT_LAYOUT_STYLE); |
| |
| return widget->priv->layout_style; |
| } |
| |
| /** |
| * gtk_button_box_get_child_secondary: |
| * @widget: a #GtkButtonBox |
| * @child: a child of @widget |
| * |
| * Returns whether @child should appear in a secondary group of children. |
| * |
| * Return value: whether @child should appear in a secondary group of children. |
| * |
| * Since: 2.4 |
| **/ |
| gboolean |
| gtk_button_box_get_child_secondary (GtkButtonBox *widget, |
| GtkWidget *child) |
| { |
| g_return_val_if_fail (GTK_IS_BUTTON_BOX (widget), FALSE); |
| g_return_val_if_fail (GTK_IS_WIDGET (child), FALSE); |
| |
| return (g_object_get_data (G_OBJECT (child), GTK_BOX_SECONDARY_CHILD) != NULL); |
| } |
| |
| /** |
| * gtk_button_box_set_child_secondary: |
| * @widget: a #GtkButtonBox |
| * @child: a child of @widget |
| * @is_secondary: if %TRUE, the @child appears in a secondary group of the |
| * button box. |
| * |
| * Sets whether @child should appear in a secondary group of children. |
| * A typical use of a secondary child is the help button in a dialog. |
| * |
| * This group appears after the other children if the style |
| * is %GTK_BUTTONBOX_START, %GTK_BUTTONBOX_SPREAD or |
| * %GTK_BUTTONBOX_EDGE, and before the other children if the style |
| * is %GTK_BUTTONBOX_END. For horizontal button boxes, the definition |
| * of before/after depends on direction of the widget (see |
| * gtk_widget_set_direction()). If the style is %GTK_BUTTONBOX_START |
| * or %GTK_BUTTONBOX_END, then the secondary children are aligned at |
| * the other end of the button box from the main children. For the |
| * other styles, they appear immediately next to the main children. |
| **/ |
| void |
| gtk_button_box_set_child_secondary (GtkButtonBox *widget, |
| GtkWidget *child, |
| gboolean is_secondary) |
| { |
| g_return_if_fail (GTK_IS_BUTTON_BOX (widget)); |
| g_return_if_fail (GTK_IS_WIDGET (child)); |
| g_return_if_fail (gtk_widget_get_parent (child) == GTK_WIDGET (widget)); |
| |
| g_object_set_data (G_OBJECT (child), |
| GTK_BOX_SECONDARY_CHILD, |
| is_secondary ? GINT_TO_POINTER (1) : NULL); |
| gtk_widget_child_notify (child, "secondary"); |
| |
| if (gtk_widget_get_visible (GTK_WIDGET (widget)) && |
| gtk_widget_get_visible (child)) |
| gtk_widget_queue_resize (child); |
| } |
| |
| /* Ask children how much space they require and round up |
| * to match minimum size and internal padding. |
| * Returns the size each single child should have. |
| */ |
| static void |
| gtk_button_box_child_requisition (GtkWidget *widget, |
| gint *nvis_children, |
| gint *nvis_secondaries, |
| gint **widths, |
| gint **heights) |
| { |
| GtkButtonBox *bbox; |
| GList *children, *list; |
| gint nchildren; |
| gint nsecondaries; |
| gint needed_width; |
| gint needed_height; |
| gint avg_w, avg_h; |
| GtkRequisition child_requisition; |
| gint ipad_w; |
| gint ipad_h; |
| gint child_min_width; |
| gint child_min_height; |
| gint ipad_x; |
| gint ipad_y; |
| gboolean homogeneous; |
| gint i; |
| |
| g_return_if_fail (GTK_IS_BUTTON_BOX (widget)); |
| |
| bbox = GTK_BUTTON_BOX (widget); |
| |
| homogeneous = gtk_box_get_homogeneous (GTK_BOX (widget)); |
| |
| gtk_widget_style_get (widget, |
| "child-min-width", &child_min_width, |
| "child-min-height", &child_min_height, |
| "child-internal-pad-x", &ipad_x, |
| "child-internal-pad-y", &ipad_y, |
| NULL); |
| |
| nchildren = 0; |
| nsecondaries = 0; |
| list = children = _gtk_box_get_children (GTK_BOX (bbox)); |
| needed_width = child_min_width; |
| needed_height = child_min_height; |
| ipad_w = ipad_x * 2; |
| ipad_h = ipad_y * 2; |
| |
| avg_w = avg_h = 0; |
| while (children) |
| { |
| GtkWidget *child; |
| |
| child = children->data; |
| children = children->next; |
| |
| if (gtk_widget_get_visible (child)) |
| { |
| nchildren += 1; |
| gtk_widget_get_preferred_size (child, |
| &child_requisition, NULL); |
| avg_w += child_requisition.width + ipad_w; |
| avg_h += child_requisition.height + ipad_h; |
| } |
| } |
| avg_w /= MAX (nchildren, 1); |
| avg_h /= MAX (nchildren, 1); |
| |
| *widths = g_new (gint, nchildren); |
| *heights = g_new (gint, nchildren); |
| |
| i = 0; |
| children = list; |
| while (children) |
| { |
| GtkWidget *child; |
| gboolean is_secondary; |
| gboolean non_homogeneous; |
| |
| child = children->data; |
| children = children->next; |
| |
| if (gtk_widget_get_visible (child)) |
| { |
| is_secondary = gtk_button_box_get_child_secondary (bbox, child); |
| non_homogeneous = gtk_button_box_get_child_non_homogeneous (bbox, child); |
| |
| if (is_secondary) |
| nsecondaries++; |
| |
| gtk_widget_get_preferred_size (child, &child_requisition, NULL); |
| |
| if (homogeneous || |
| (!non_homogeneous && (child_requisition.width + ipad_w < avg_w * 1.5))) |
| { |
| (*widths)[i] = -1; |
| if (child_requisition.width + ipad_w > needed_width) |
| needed_width = child_requisition.width + ipad_w; |
| } |
| else |
| { |
| (*widths)[i] = child_requisition.width + ipad_w; |
| } |
| |
| if (homogeneous || |
| (!non_homogeneous && (child_requisition.height + ipad_h < avg_h * 1.5))) |
| { |
| (*heights)[i] = -1; |
| if (child_requisition.height + ipad_h > needed_height) |
| needed_height = child_requisition.height + ipad_h; |
| } |
| else |
| { |
| (*heights)[i] = child_requisition.height + ipad_h; |
| } |
| |
| i++; |
| } |
| } |
| |
| g_list_free (list); |
| |
| for (i = 0; i < nchildren; i++) |
| { |
| if ((*widths)[i] == -1) |
| (*widths)[i] = needed_width; |
| if ((*heights)[i] == -1) |
| (*heights)[i] = needed_height; |
| } |
| |
| if (nvis_children) |
| *nvis_children = nchildren; |
| |
| if (nvis_secondaries) |
| *nvis_secondaries = nsecondaries; |
| } |
| |
| static void |
| gtk_button_box_size_request (GtkWidget *widget, |
| GtkRequisition *requisition) |
| { |
| GtkButtonBoxPrivate *priv; |
| GtkButtonBox *bbox; |
| gint nvis_children; |
| gint max_size; |
| gint total_size; |
| gint spacing; |
| GtkOrientation orientation; |
| gint *widths; |
| gint *heights; |
| gint i; |
| |
| bbox = GTK_BUTTON_BOX (widget); |
| priv = bbox->priv; |
| |
| orientation = gtk_orientable_get_orientation (GTK_ORIENTABLE (widget)); |
| spacing = gtk_box_get_spacing (GTK_BOX (widget)); |
| |
| gtk_button_box_child_requisition (widget, |
| &nvis_children, |
| NULL, |
| &widths, &heights); |
| |
| max_size = 0; |
| total_size = 0; |
| for (i = 0; i < nvis_children; i++) |
| { |
| if (orientation == GTK_ORIENTATION_HORIZONTAL) |
| { |
| total_size += widths[i]; |
| max_size = MAX (max_size, heights[i]); |
| } |
| else |
| { |
| total_size += heights[i]; |
| max_size = MAX (max_size, widths[i]); |
| } |
| } |
| g_free (widths); |
| g_free (heights); |
| |
| if (nvis_children == 0) |
| { |
| requisition->width = 0; |
| requisition->height = 0; |
| } |
| else |
| { |
| switch (priv->layout_style) |
| { |
| case GTK_BUTTONBOX_SPREAD: |
| if (orientation == GTK_ORIENTATION_HORIZONTAL) |
| requisition->width = total_size + ((nvis_children + 1)*spacing); |
| else |
| requisition->height = total_size + ((nvis_children + 1)*spacing); |
| |
| break; |
| case GTK_BUTTONBOX_EDGE: |
| case GTK_BUTTONBOX_START: |
| case GTK_BUTTONBOX_END: |
| case GTK_BUTTONBOX_CENTER: |
| if (orientation == GTK_ORIENTATION_HORIZONTAL) |
| requisition->width = total_size + ((nvis_children - 1)*spacing); |
| else |
| requisition->height = total_size + ((nvis_children - 1)*spacing); |
| |
| break; |
| default: |
| g_assert_not_reached (); |
| break; |
| } |
| |
| if (orientation == GTK_ORIENTATION_HORIZONTAL) |
| requisition->height = max_size; |
| else |
| requisition->width = max_size; |
| } |
| } |
| |
| static void |
| gtk_button_box_get_preferred_width (GtkWidget *widget, |
| gint *minimum, |
| gint *natural) |
| { |
| GtkRequisition requisition; |
| |
| gtk_button_box_size_request (widget, &requisition); |
| |
| *minimum = *natural = requisition.width; |
| } |
| |
| static void |
| gtk_button_box_get_preferred_height (GtkWidget *widget, |
| gint *minimum, |
| gint *natural) |
| { |
| GtkRequisition requisition; |
| |
| gtk_button_box_size_request (widget, &requisition); |
| |
| *minimum = *natural = requisition.height; |
| } |
| |
| static void |
| gtk_button_box_get_preferred_width_for_height (GtkWidget *widget, |
| gint height, |
| gint *minimum, |
| gint *natural) |
| { |
| gtk_button_box_get_preferred_width (widget, minimum, natural); |
| } |
| |
| static void |
| gtk_button_box_get_preferred_height_for_width (GtkWidget *widget, |
| gint width, |
| gint *minimum, |
| gint *natural) |
| { |
| gtk_button_box_get_preferred_height (widget, minimum, natural); |
| } |
| |
| static void |
| gtk_button_box_size_allocate (GtkWidget *widget, |
| GtkAllocation *allocation) |
| { |
| GtkButtonBoxPrivate *priv; |
| GtkButtonBox *bbox; |
| GList *children, *list; |
| GtkAllocation child_allocation; |
| gint nvis_children; |
| gint n_primaries; |
| gint n_secondaries; |
| gint x = 0; |
| gint y = 0; |
| gint secondary_x = 0; |
| gint secondary_y = 0; |
| gint width = 0; |
| gint height = 0; |
| gint childspacing = 0; |
| gint spacing; |
| GtkOrientation orientation; |
| gint ipad_x, ipad_y; |
| gint *widths; |
| gint *heights; |
| gint *sizes; |
| gint primary_size; |
| gint secondary_size; |
| gint total_size; |
| gint i; |
| |
| bbox = GTK_BUTTON_BOX (widget); |
| priv = bbox->priv; |
| |
| orientation = gtk_orientable_get_orientation (GTK_ORIENTABLE (widget)); |
| spacing = gtk_box_get_spacing (GTK_BOX (widget)); |
| |
| gtk_widget_style_get (widget, |
| "child-internal-pad-x", &ipad_x, |
| "child-internal-pad-y", &ipad_y, |
| NULL); |
| gtk_button_box_child_requisition (widget, |
| &nvis_children, |
| &n_secondaries, |
| &widths, &heights); |
| |
| n_primaries = nvis_children - n_secondaries; |
| primary_size = 0; |
| secondary_size = 0; |
| if (orientation == GTK_ORIENTATION_HORIZONTAL) |
| sizes = widths; |
| else |
| sizes = heights; |
| |
| i = 0; |
| list = children = _gtk_box_get_children (GTK_BOX (widget)); |
| while (children) |
| { |
| GtkWidget *child; |
| |
| child = children->data; |
| children = children->next; |
| |
| if (gtk_widget_get_visible (child)) |
| { |
| if (gtk_button_box_get_child_secondary (bbox, child)) |
| secondary_size += sizes[i]; |
| else |
| primary_size += sizes[i]; |
| i++; |
| } |
| } |
| total_size = primary_size + secondary_size; |
| |
| gtk_widget_set_allocation (widget, allocation); |
| |
| if (orientation == GTK_ORIENTATION_HORIZONTAL) |
| width = allocation->width; |
| else |
| height = allocation->height; |
| |
| switch (priv->layout_style) |
| { |
| case GTK_BUTTONBOX_SPREAD: |
| |
| if (orientation == GTK_ORIENTATION_HORIZONTAL) |
| { |
| childspacing = (width - total_size) / (nvis_children + 1); |
| x = allocation->x + childspacing; |
| secondary_x = x + primary_size + n_primaries * childspacing; |
| } |
| else |
| { |
| childspacing = (height - total_size) / (nvis_children + 1); |
| y = allocation->y + childspacing; |
| secondary_y = y + primary_size + n_primaries * childspacing; |
| } |
| |
| break; |
| |
| case GTK_BUTTONBOX_EDGE: |
| |
| if (orientation == GTK_ORIENTATION_HORIZONTAL) |
| { |
| if (nvis_children >= 2) |
| { |
| childspacing = (width - total_size) / (nvis_children - 1); |
| x = allocation->x; |
| secondary_x = x + primary_size + n_primaries * childspacing; |
| } |
| else if (nvis_children == 1) |
| { |
| /* one child, just center */ |
| childspacing = width; |
| x = secondary_x = allocation->x |
| + (allocation->width - widths[0]) / 2; |
| } |
| else |
| { |
| /* zero children, meh */ |
| childspacing = width; |
| x = secondary_x = allocation->x + allocation->width / 2; |
| } |
| } |
| else |
| { |
| if (nvis_children >= 2) |
| { |
| childspacing = (height - total_size) / (nvis_children - 1); |
| y = allocation->y; |
| secondary_y = y + primary_size + n_primaries * childspacing; |
| } |
| else if (nvis_children == 1) |
| { |
| /* one child, just center */ |
| childspacing = height; |
| y = secondary_y = allocation->y |
| + (allocation->height - heights[0]) / 2; |
| } |
| else |
| { |
| /* zero children, meh */ |
| childspacing = height; |
| y = secondary_y = allocation->y + allocation->height / 2; |
| } |
| } |
| |
| break; |
| |
| case GTK_BUTTONBOX_START: |
| |
| if (orientation == GTK_ORIENTATION_HORIZONTAL) |
| { |
| childspacing = spacing; |
| x = allocation->x; |
| secondary_x = allocation->x + allocation->width |
| - secondary_size - spacing * (n_secondaries - 1); |
| } |
| else |
| { |
| childspacing = spacing; |
| y = allocation->y; |
| secondary_y = allocation->y + allocation->height |
| - secondary_size - spacing * (n_secondaries - 1); |
| } |
| |
| break; |
| |
| case GTK_BUTTONBOX_END: |
| |
| if (orientation == GTK_ORIENTATION_HORIZONTAL) |
| { |
| childspacing = spacing; |
| x = allocation->x + allocation->width |
| - primary_size - spacing * (n_primaries - 1); |
| secondary_x = allocation->x; |
| } |
| else |
| { |
| childspacing = spacing; |
| y = allocation->y + allocation->height |
| - primary_size - spacing * (n_primaries - 1); |
| secondary_y = allocation->y; |
| } |
| |
| break; |
| |
| case GTK_BUTTONBOX_CENTER: |
| |
| if (orientation == GTK_ORIENTATION_HORIZONTAL) |
| { |
| childspacing = spacing; |
| x = allocation->x + |
| (allocation->width |
| - (primary_size + spacing * (n_primaries - 1))) / 2 |
| + (secondary_size + n_secondaries * spacing) / 2; |
| secondary_x = allocation->x; |
| } |
| else |
| { |
| childspacing = spacing; |
| y = allocation->y + |
| (allocation->height |
| - (primary_size + spacing * (n_primaries - 1))) / 2 |
| + (secondary_size + n_secondaries * spacing) / 2; |
| secondary_y = allocation->y; |
| } |
| |
| break; |
| |
| default: |
| g_assert_not_reached (); |
| break; |
| } |
| |
| children = list; |
| i = 0; |
| while (children) |
| { |
| GtkWidget *child; |
| |
| child = children->data; |
| children = children->next; |
| |
| if (gtk_widget_get_visible (child)) |
| { |
| child_allocation.width = widths[i]; |
| child_allocation.height = heights[i]; |
| |
| if (orientation == GTK_ORIENTATION_HORIZONTAL) |
| { |
| child_allocation.y = allocation->y + (allocation->height - child_allocation.height) / 2; |
| |
| if (gtk_button_box_get_child_secondary (bbox, child)) |
| { |
| child_allocation.x = secondary_x; |
| secondary_x += child_allocation.width + childspacing; |
| } |
| else |
| { |
| child_allocation.x = x; |
| x += child_allocation.width + childspacing; |
| } |
| |
| if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL) |
| child_allocation.x = (allocation->x + allocation->width) |
| - (child_allocation.x + child_allocation.width - allocation->x); |
| } |
| else |
| { |
| child_allocation.x = allocation->x + (allocation->width - child_allocation.width) / 2; |
| |
| if (gtk_button_box_get_child_secondary (bbox, child)) |
| { |
| child_allocation.y = secondary_y; |
| secondary_y += child_allocation.height + childspacing; |
| } |
| else |
| { |
| child_allocation.y = y; |
| y += child_allocation.height + childspacing; |
| } |
| } |
| |
| gtk_widget_size_allocate (child, &child_allocation); |
| i++; |
| } |
| } |
| |
| g_list_free (list); |
| g_free (widths); |
| g_free (heights); |
| } |
| |
| /** |
| * gtk_button_box_new: |
| * @orientation: the box' orientation. |
| * |
| * Creates a new #GtkButtonBox. |
| * |
| * Return value: a new #GtkButtonBox. |
| * |
| * Since: 3.0 |
| */ |
| GtkWidget * |
| gtk_button_box_new (GtkOrientation orientation) |
| { |
| return g_object_new (GTK_TYPE_BUTTON_BOX, |
| "orientation", orientation, |
| NULL); |
| } |
| |
| /** |
| * gtk_button_box_get_child_non_homogeneous: |
| * @widget: a #GtkButtonBox |
| * @child: a child of @widget |
| * |
| * Returns whether the child is exempted from homogenous |
| * sizing. |
| * |
| * Returns: %TRUE if the child is not subject to homogenous sizing |
| * |
| * Since: 3.2 |
| */ |
| gboolean |
| gtk_button_box_get_child_non_homogeneous (GtkButtonBox *widget, |
| GtkWidget *child) |
| { |
| g_return_val_if_fail (GTK_IS_BUTTON_BOX (widget), FALSE); |
| g_return_val_if_fail (GTK_IS_WIDGET (child), FALSE); |
| |
| return (g_object_get_data (G_OBJECT (child), GTK_BOX_NON_HOMOGENEOUS) != NULL); |
| } |
| |
| /** |
| * gtk_button_box_set_child_non_homogeneous: |
| * @widget: a #GtkButtonBox |
| * @child: a child of @widget |
| * @non_homogeneous: the new value |
| * |
| * Sets whether the child is exempted from homogeous sizing. |
| * |
| * Since: 3.2 |
| */ |
| void |
| gtk_button_box_set_child_non_homogeneous (GtkButtonBox *widget, |
| GtkWidget *child, |
| gboolean non_homogeneous) |
| { |
| g_return_if_fail (GTK_IS_BUTTON_BOX (widget)); |
| g_return_if_fail (GTK_IS_WIDGET (child)); |
| g_return_if_fail (gtk_widget_get_parent (child) == GTK_WIDGET (widget)); |
| |
| g_object_set_data (G_OBJECT (child), |
| GTK_BOX_NON_HOMOGENEOUS, |
| non_homogeneous ? GINT_TO_POINTER (1) : NULL); |
| gtk_widget_child_notify (child, "non-homogeneous"); |
| |
| if (gtk_widget_get_visible (GTK_WIDGET (widget)) && |
| gtk_widget_get_visible (child)) |
| gtk_widget_queue_resize (child); |
| } |