X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;f=gtk%2Fgtkbbox.c;h=8385e2dd2e659a7489f56b40c8b34e579e56ab35;hb=3b2182e711ace4a2d6843848208224cd7525d530;hp=38a57dbb970db45e926f56e326a43096756e9a42;hpb=f26aad1916445d25c99878138dfc384de67a2060;p=~andy%2Fgtk diff --git a/gtk/gtkbbox.c b/gtk/gtkbbox.c index 38a57dbb9..8385e2dd2 100644 --- a/gtk/gtkbbox.c +++ b/gtk/gtkbbox.c @@ -12,60 +12,118 @@ * 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 . */ /* * 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/. + * GTK+ at ftp://ftp.gtk.org/pub/gtk/. */ -#include +/** + * 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" -#include "gtkalias.h" + + +struct _GtkButtonBoxPrivate +{ + GtkButtonBoxStyle layout_style; +}; enum { PROP_0, - PROP_LAYOUT_STYLE, - PROP_LAST + PROP_LAYOUT_STYLE }; enum { CHILD_PROP_0, - CHILD_PROP_SECONDARY + 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); + guint prop_id, + const GValue *value, + GParamSpec *pspec); static void gtk_button_box_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec); + 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); + 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); + 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_ABSTRACT_TYPE (GtkButtonBox, gtk_button_box, GTK_TYPE_BOX); +G_DEFINE_TYPE (GtkButtonBox, gtk_button_box, GTK_TYPE_BOX) static void gtk_button_box_class_init (GtkButtonBoxClass *class) @@ -81,87 +139,108 @@ gtk_button_box_class_init (GtkButtonBoxClass *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, + 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_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, + 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_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, + 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_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, + 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)); + 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 layout the buttons in the box. Possible values are default, spread, edge, start and end"), - GTK_TYPE_BUTTON_BOX_STYLE, - GTK_BUTTONBOX_DEFAULT_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)); + 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) { - GTK_BOX (button_box)->spacing = 0; - button_box->child_min_width = GTK_BUTTONBOX_DEFAULT; - button_box->child_min_height = GTK_BUTTONBOX_DEFAULT; - button_box->child_ipad_x = GTK_BUTTONBOX_DEFAULT; - button_box->child_ipad_y = GTK_BUTTONBOX_DEFAULT; - button_box->layout_style = GTK_BUTTONBOX_DEFAULT_STYLE; + 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) +gtk_button_box_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) { - switch (prop_id) + switch (prop_id) { case PROP_LAYOUT_STYLE: gtk_button_box_set_layout (GTK_BUTTON_BOX (object), - g_value_get_enum (value)); + g_value_get_enum (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); @@ -170,15 +249,17 @@ gtk_button_box_set_property (GObject *object, } static void -gtk_button_box_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) +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, GTK_BUTTON_BOX (object)->layout_style); + g_value_set_enum (value, priv->layout_style); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); @@ -187,17 +268,21 @@ gtk_button_box_get_property (GObject *object, } static void -gtk_button_box_set_child_property (GtkContainer *container, - GtkWidget *child, - guint property_id, - const GValue *value, - GParamSpec *pspec) +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)); + 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); @@ -207,17 +292,22 @@ gtk_button_box_set_child_property (GtkContainer *container, static void gtk_button_box_get_child_property (GtkContainer *container, - GtkWidget *child, - guint property_id, - GValue *value, - GParamSpec *pspec) + 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)); + 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); @@ -225,118 +315,83 @@ gtk_button_box_get_child_property (GtkContainer *container, } } -/* set per widget values for spacing, child size and child internal padding */ - -void -gtk_button_box_set_child_size (GtkButtonBox *widget, - gint width, gint height) -{ - g_return_if_fail (GTK_IS_BUTTON_BOX (widget)); - - widget->child_min_width = width; - widget->child_min_height = height; -} - -void -gtk_button_box_set_child_ipadding (GtkButtonBox *widget, - gint ipad_x, gint ipad_y) +static void +gtk_button_box_remove (GtkContainer *container, + GtkWidget *widget) { - g_return_if_fail (GTK_IS_BUTTON_BOX (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); - widget->child_ipad_x = ipad_x; - widget->child_ipad_y = ipad_y; + 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, +gtk_button_box_set_layout (GtkButtonBox *widget, GtkButtonBoxStyle layout_style) { + GtkButtonBoxPrivate *priv; + g_return_if_fail (GTK_IS_BUTTON_BOX (widget)); - g_return_if_fail (layout_style >= GTK_BUTTONBOX_DEFAULT_STYLE && - layout_style <= GTK_BUTTONBOX_END); - if (widget->layout_style != layout_style) + priv = widget->priv; + + if (priv->layout_style != layout_style) { - widget->layout_style = layout_style; + priv->layout_style = layout_style; g_object_notify (G_OBJECT (widget), "layout-style"); gtk_widget_queue_resize (GTK_WIDGET (widget)); } } - -/* get per widget values for spacing, child size and child internal padding */ - -void -gtk_button_box_get_child_size (GtkButtonBox *widget, - gint *width, gint *height) -{ - g_return_if_fail (GTK_IS_BUTTON_BOX (widget)); - g_return_if_fail (width != NULL); - g_return_if_fail (height != NULL); - - *width = widget->child_min_width; - *height = widget->child_min_height; -} - -void -gtk_button_box_get_child_ipadding (GtkButtonBox *widget, - gint* ipad_x, gint *ipad_y) -{ - g_return_if_fail (GTK_IS_BUTTON_BOX (widget)); - g_return_if_fail (ipad_x != NULL); - g_return_if_fail (ipad_y != NULL); - - *ipad_x = widget->child_ipad_x; - *ipad_y = widget->child_ipad_y; -} - -GtkButtonBoxStyle +/** + * 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), GTK_BUTTONBOX_SPREAD); - - return widget->layout_style; + 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 - * + * @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 +gboolean gtk_button_box_get_child_secondary (GtkButtonBox *widget, - GtkWidget *child) + GtkWidget *child) { - GList *list; - GtkBoxChild *child_info; - g_return_val_if_fail (GTK_IS_BUTTON_BOX (widget), FALSE); g_return_val_if_fail (GTK_IS_WIDGET (child), FALSE); - child_info = NULL; - list = GTK_BOX (widget)->children; - while (list) - { - child_info = list->data; - if (child_info->widget == child) - break; - - list = list->next; - } - - g_return_val_if_fail (list != NULL, FALSE); - - return child_info->is_secondary; + return (g_object_get_data (G_OBJECT (child), GTK_BOX_SECONDARY_CHILD) != NULL); } /** - * gtk_button_box_set_child_secondary + * 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 @@ -355,122 +410,624 @@ gtk_button_box_get_child_secondary (GtkButtonBox *widget, * 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) +void +gtk_button_box_set_child_secondary (GtkButtonBox *widget, + GtkWidget *child, + gboolean is_secondary) { - GList *list; - g_return_if_fail (GTK_IS_BUTTON_BOX (widget)); g_return_if_fail (GTK_IS_WIDGET (child)); - g_return_if_fail (child->parent == GTK_WIDGET (widget)); - - list = GTK_BOX (widget)->children; - while (list) - { - GtkBoxChild *child_info = list->data; - if (child_info->widget == child) - { - child_info->is_secondary = is_secondary; - break; - } - - list = list->next; - } + 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_VISIBLE (widget) && GTK_WIDGET_VISIBLE (child)) + 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. */ -void -_gtk_button_box_child_requisition (GtkWidget *widget, - int *nvis_children, - int *nvis_secondaries, - int *width, - int *height) +/* 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; - GtkBoxChild *child; - GList *children; + 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 width_default; - gint height_default; - gint ipad_x_default; - gint ipad_y_default; - 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", &width_default, - "child-min-height", &height_default, - "child-internal-pad-x", &ipad_x_default, - "child-internal-pad-y", &ipad_y_default, - NULL); - - child_min_width = bbox->child_min_width != GTK_BUTTONBOX_DEFAULT - ? bbox->child_min_width : width_default; - child_min_height = bbox->child_min_height !=GTK_BUTTONBOX_DEFAULT - ? bbox->child_min_height : height_default; - ipad_x = bbox->child_ipad_x != GTK_BUTTONBOX_DEFAULT - ? bbox->child_ipad_x : ipad_x_default; - ipad_y = bbox->child_ipad_y != GTK_BUTTONBOX_DEFAULT - ? bbox->child_ipad_y : ipad_y_default; + "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; - children = GTK_BOX(bbox)->children; + list = children = _gtk_box_get_children (GTK_BOX (bbox)); needed_width = child_min_width; - needed_height = child_min_height; + 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_VISIBLE (child->widget)) - { - nchildren += 1; - gtk_widget_size_request (child->widget, &child_requisition); - - if (child_requisition.width + ipad_w > needed_width) - needed_width = child_requisition.width + ipad_w; - if (child_requisition.height + ipad_h > needed_height) - needed_height = child_requisition.height + ipad_h; - if (child->is_secondary) - nsecondaries++; - } + 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; - if (width) - *width = needed_width; - if (height) - *height = needed_height; } -#define __GTK_BUTTON_BOX_C__ -#include "gtkaliasdef.c" +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); +}