1 /* GTK - The GIMP Toolkit
2 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
21 * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
22 * file for a list of people on the GTK+ Team. See the ChangeLog
23 * files for a list of changes. These files are distributed with
24 * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
29 * @Short_description: Base class for GtkHButtonBox and GtkVButtonBox
30 * @Title: GtkButtonBox
31 * @See_also: #GtkVButtonBox, #GtkHButtonBox
33 * The primary purpose of this class is to keep track of the various properties
34 * of #GtkHButtonBox and #GtkVButtonBox widgets.
36 * gtk_button_box_get_child_size() retrieves the minimum width and height
37 * for widgets in a given button box.
39 * The internal padding of buttons can be retrieved and changed per button box
40 * using gtk_button_box_get_child_ipadding() and
41 * gtk_button_box_set_child_ipadding() respectively.
43 * gtk_button_box_get_spacing() and gtk_button_box_set_spacing() retrieve and
44 * change default number of pixels between buttons, respectively.
46 * gtk_button_box_get_layout() and gtk_button_box_set_layout() retrieve and
47 * alter the method used to spread the buttons in a button box across the
48 * container, respectively.
50 * The main purpose of GtkButtonBox is to make sure the children have all the
51 * same size. Therefore it ignores the homogeneous property which it inherited
52 * from GtkBox, and always behaves as if homogeneous was %TRUE.
57 #include "gtkorientable.h"
58 #include "gtkprivate.h"
62 struct _GtkButtonBoxPriv
64 GtkButtonBoxStyle layout_style;
77 #define GTK_BOX_SECONDARY_CHILD "gtk-box-secondary-child"
79 static void gtk_button_box_set_property (GObject *object,
83 static void gtk_button_box_get_property (GObject *object,
87 static void gtk_button_box_size_request (GtkWidget *widget,
88 GtkRequisition *requisition);
89 static void gtk_button_box_size_allocate (GtkWidget *widget,
90 GtkAllocation *allocation);
91 static void gtk_button_box_remove (GtkContainer *container,
93 static void gtk_button_box_set_child_property (GtkContainer *container,
98 static void gtk_button_box_get_child_property (GtkContainer *container,
104 #define DEFAULT_CHILD_MIN_WIDTH 85
105 #define DEFAULT_CHILD_MIN_HEIGHT 27
106 #define DEFAULT_CHILD_IPAD_X 4
107 #define DEFAULT_CHILD_IPAD_Y 0
108 #define DEFAULT_LAYOUT_STYLE GTK_BUTTONBOX_EDGE
110 G_DEFINE_TYPE (GtkButtonBox, gtk_button_box, GTK_TYPE_BOX)
113 gtk_button_box_class_init (GtkButtonBoxClass *class)
115 GtkWidgetClass *widget_class;
116 GObjectClass *gobject_class;
117 GtkContainerClass *container_class;
119 gobject_class = G_OBJECT_CLASS (class);
120 widget_class = (GtkWidgetClass*) class;
121 container_class = (GtkContainerClass*) class;
123 gobject_class->set_property = gtk_button_box_set_property;
124 gobject_class->get_property = gtk_button_box_get_property;
126 widget_class->size_request = gtk_button_box_size_request;
127 widget_class->size_allocate = gtk_button_box_size_allocate;
129 container_class->remove = gtk_button_box_remove;
130 container_class->set_child_property = gtk_button_box_set_child_property;
131 container_class->get_child_property = gtk_button_box_get_child_property;
133 /* FIXME we need to override the "spacing" property on GtkBox once
134 * libgobject allows that.
136 gtk_widget_class_install_style_property (widget_class,
137 g_param_spec_int ("child-min-width",
138 P_("Minimum child width"),
139 P_("Minimum width of buttons inside the box"),
142 DEFAULT_CHILD_MIN_WIDTH,
143 GTK_PARAM_READABLE));
145 gtk_widget_class_install_style_property (widget_class,
146 g_param_spec_int ("child-min-height",
147 P_("Minimum child height"),
148 P_("Minimum height of buttons inside the box"),
151 DEFAULT_CHILD_MIN_HEIGHT,
152 GTK_PARAM_READABLE));
154 gtk_widget_class_install_style_property (widget_class,
155 g_param_spec_int ("child-internal-pad-x",
156 P_("Child internal width padding"),
157 P_("Amount to increase child's size on either side"),
160 DEFAULT_CHILD_IPAD_X,
161 GTK_PARAM_READABLE));
163 gtk_widget_class_install_style_property (widget_class,
164 g_param_spec_int ("child-internal-pad-y",
165 P_("Child internal height padding"),
166 P_("Amount to increase child's size on the top and bottom"),
169 DEFAULT_CHILD_IPAD_Y,
170 GTK_PARAM_READABLE));
171 g_object_class_install_property (gobject_class,
173 g_param_spec_enum ("layout-style",
175 P_("How to layout the buttons in the box. Possible values are spread, edge, start and end"),
176 GTK_TYPE_BUTTON_BOX_STYLE,
177 DEFAULT_LAYOUT_STYLE,
178 GTK_PARAM_READWRITE));
180 gtk_container_class_install_child_property (container_class,
181 CHILD_PROP_SECONDARY,
182 g_param_spec_boolean ("secondary",
184 P_("If TRUE, the child appears in a secondary group of children, suitable for, e.g., help buttons"),
186 GTK_PARAM_READWRITE));
188 g_type_class_add_private (class, sizeof (GtkButtonBoxPriv));
192 gtk_button_box_init (GtkButtonBox *button_box)
194 GtkButtonBoxPriv *priv;
196 button_box->priv = G_TYPE_INSTANCE_GET_PRIVATE (button_box,
199 priv = button_box->priv;
201 gtk_box_set_spacing (GTK_BOX (button_box), 0);
202 priv->layout_style = DEFAULT_LAYOUT_STYLE;
206 gtk_button_box_set_property (GObject *object,
213 case PROP_LAYOUT_STYLE:
214 gtk_button_box_set_layout (GTK_BUTTON_BOX (object),
215 g_value_get_enum (value));
218 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
224 gtk_button_box_get_property (GObject *object,
229 GtkButtonBoxPriv *priv = GTK_BUTTON_BOX (object)->priv;
233 case PROP_LAYOUT_STYLE:
234 g_value_set_enum (value, priv->layout_style);
237 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
243 gtk_button_box_set_child_property (GtkContainer *container,
251 case CHILD_PROP_SECONDARY:
252 gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (container), child,
253 g_value_get_boolean (value));
256 GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
262 gtk_button_box_get_child_property (GtkContainer *container,
270 case CHILD_PROP_SECONDARY:
271 g_value_set_boolean (value,
272 gtk_button_box_get_child_secondary (GTK_BUTTON_BOX (container),
276 GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
282 gtk_button_box_remove (GtkContainer *container,
285 /* clear is_secondary flag in case the widget
286 * is added to another container
288 gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (container),
292 GTK_CONTAINER_CLASS (gtk_button_box_parent_class)->remove (container, widget);
296 * gtk_button_box_set_layout:
297 * @widget: a #GtkButtonBox
298 * @layout_style: the new layout style
300 * Changes the way buttons are arranged in their container.
303 gtk_button_box_set_layout (GtkButtonBox *widget,
304 GtkButtonBoxStyle layout_style)
306 GtkButtonBoxPriv *priv;
308 g_return_if_fail (GTK_IS_BUTTON_BOX (widget));
312 if (priv->layout_style != layout_style)
314 priv->layout_style = layout_style;
315 g_object_notify (G_OBJECT (widget), "layout-style");
316 gtk_widget_queue_resize (GTK_WIDGET (widget));
321 * gtk_button_box_get_layout:
322 * @widget: a #GtkButtonBox
324 * Retrieves the method being used to arrange the buttons in a button box.
326 * Returns: the method used to layout buttons in @widget.
329 gtk_button_box_get_layout (GtkButtonBox *widget)
331 g_return_val_if_fail (GTK_IS_BUTTON_BOX (widget), DEFAULT_LAYOUT_STYLE);
333 return widget->priv->layout_style;
337 * gtk_button_box_get_child_secondary:
338 * @widget: a #GtkButtonBox
339 * @child: a child of @widget
341 * Returns whether @child should appear in a secondary group of children.
343 * Return value: whether @child should appear in a secondary group of children.
348 gtk_button_box_get_child_secondary (GtkButtonBox *widget,
351 g_return_val_if_fail (GTK_IS_BUTTON_BOX (widget), FALSE);
352 g_return_val_if_fail (GTK_IS_WIDGET (child), FALSE);
354 return (g_object_get_data (G_OBJECT (child), GTK_BOX_SECONDARY_CHILD) != NULL);
358 * gtk_button_box_set_child_secondary
359 * @widget: a #GtkButtonBox
360 * @child: a child of @widget
361 * @is_secondary: if %TRUE, the @child appears in a secondary group of the
364 * Sets whether @child should appear in a secondary group of children.
365 * A typical use of a secondary child is the help button in a dialog.
367 * This group appears after the other children if the style
368 * is %GTK_BUTTONBOX_START, %GTK_BUTTONBOX_SPREAD or
369 * %GTK_BUTTONBOX_EDGE, and before the other children if the style
370 * is %GTK_BUTTONBOX_END. For horizontal button boxes, the definition
371 * of before/after depends on direction of the widget (see
372 * gtk_widget_set_direction()). If the style is %GTK_BUTTONBOX_START
373 * or %GTK_BUTTONBOX_END, then the secondary children are aligned at
374 * the other end of the button box from the main children. For the
375 * other styles, they appear immediately next to the main children.
378 gtk_button_box_set_child_secondary (GtkButtonBox *widget,
380 gboolean is_secondary)
382 g_return_if_fail (GTK_IS_BUTTON_BOX (widget));
383 g_return_if_fail (GTK_IS_WIDGET (child));
384 g_return_if_fail (gtk_widget_get_parent (child) == GTK_WIDGET (widget));
386 g_object_set_data (G_OBJECT (child),
387 GTK_BOX_SECONDARY_CHILD,
388 is_secondary ? GINT_TO_POINTER (1) : NULL);
389 gtk_widget_child_notify (child, "secondary");
391 if (gtk_widget_get_visible (GTK_WIDGET (widget)) &&
392 gtk_widget_get_visible (child))
393 gtk_widget_queue_resize (child);
396 /* Ask children how much space they require and round up
397 * to match minimum size and internal padding.
398 * Returns the size each single child should have.
401 gtk_button_box_child_requisition (GtkWidget *widget,
403 gint *nvis_secondaries,
407 GtkButtonBoxPriv *priv;
409 GList *children, *list;
415 GtkRequisition child_requisition;
418 gint child_min_width;
419 gint child_min_height;
422 gboolean homogeneous;
425 g_return_if_fail (GTK_IS_BUTTON_BOX (widget));
427 bbox = GTK_BUTTON_BOX (widget);
430 homogeneous = gtk_box_get_homogeneous (GTK_BOX (widget));
432 gtk_widget_style_get (widget,
433 "child-min-width", &child_min_width,
434 "child-min-height", &child_min_height,
435 "child-internal-pad-x", &ipad_x,
436 "child-internal-pad-y", &ipad_y,
441 list = children = _gtk_box_get_children (GTK_BOX (bbox));
442 needed_width = child_min_width;
443 needed_height = child_min_height;
452 child = children->data;
453 children = children->next;
455 if (gtk_widget_get_visible (child))
458 gtk_widget_size_request (child, &child_requisition);
459 avg_w += child_requisition.width + ipad_w;
460 avg_h += child_requisition.height + ipad_h;
466 *widths = g_new (gint, nchildren);
467 *heights = g_new (gint, nchildren);
474 gboolean is_secondary;
476 child = children->data;
477 children = children->next;
479 if (gtk_widget_get_visible (child))
481 is_secondary = gtk_button_box_get_child_secondary (bbox, child);
485 gtk_widget_get_child_requisition (child, &child_requisition);
488 (child_requisition.width + ipad_w < avg_w * 1.5)) /* &&
489 child_requisition.width + ipad_w > avg_w / 1.5)) */
492 if (child_requisition.width + ipad_w > needed_width)
493 needed_width = child_requisition.width + ipad_w;
497 (*widths)[i] = child_requisition.width + ipad_w;
501 (child_requisition.height + ipad_h < avg_h * 1.5)) /* &&
502 child_requisition.height + ipad_h > avg_h / 1.5)) */
505 if (child_requisition.height + ipad_h > needed_height)
506 needed_height = child_requisition.height + ipad_h;
510 (*heights)[i] = child_requisition.height + ipad_h;
519 for (i = 0; i < nchildren; i++)
521 if ((*widths)[i] == -1)
522 (*widths)[i] = needed_width;
523 if ((*heights)[i] == -1)
524 (*heights)[i] = needed_height;
528 *nvis_children = nchildren;
530 if (nvis_secondaries)
531 *nvis_secondaries = nsecondaries;
535 gtk_button_box_size_request (GtkWidget *widget,
536 GtkRequisition *requisition)
538 GtkButtonBoxPriv *priv;
545 GtkOrientation orientation;
550 bbox = GTK_BUTTON_BOX (widget);
553 orientation = gtk_orientable_get_orientation (GTK_ORIENTABLE (widget));
554 spacing = gtk_box_get_spacing (GTK_BOX (widget));
556 gtk_button_box_child_requisition (widget,
563 for (i = 0; i < nvis_children; i++)
565 if (orientation == GTK_ORIENTATION_HORIZONTAL)
567 total_size += widths[i];
568 max_size = MAX (max_size, heights[i]);
572 total_size += heights[i];
573 max_size = MAX (max_size, widths[i]);
579 if (nvis_children == 0)
581 requisition->width = 0;
582 requisition->height = 0;
586 switch (priv->layout_style)
588 case GTK_BUTTONBOX_SPREAD:
589 if (orientation == GTK_ORIENTATION_HORIZONTAL)
590 requisition->width = total_size + ((nvis_children + 1)*spacing);
592 requisition->height = total_size + ((nvis_children + 1)*spacing);
595 case GTK_BUTTONBOX_EDGE:
596 case GTK_BUTTONBOX_START:
597 case GTK_BUTTONBOX_END:
598 case GTK_BUTTONBOX_CENTER:
599 if (orientation == GTK_ORIENTATION_HORIZONTAL)
600 requisition->width = total_size + ((nvis_children - 1)*spacing);
602 requisition->height = total_size + ((nvis_children - 1)*spacing);
606 g_assert_not_reached ();
610 if (orientation == GTK_ORIENTATION_HORIZONTAL)
611 requisition->height = max_size;
613 requisition->width = max_size;
616 border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
617 requisition->width += border_width * 2;
618 requisition->height += border_width * 2;
622 gtk_button_box_size_allocate (GtkWidget *widget,
623 GtkAllocation *allocation)
625 GtkButtonBoxPriv *priv;
627 GList *children, *list;
628 GtkAllocation child_allocation;
634 gint secondary_x = 0;
635 gint secondary_y = 0;
638 gint childspacing = 0;
641 GtkOrientation orientation;
651 bbox = GTK_BUTTON_BOX (widget);
654 border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
655 orientation = gtk_orientable_get_orientation (GTK_ORIENTABLE (widget));
656 spacing = gtk_box_get_spacing (GTK_BOX (widget));
658 gtk_widget_style_get (widget,
659 "child-internal-pad-x", &ipad_x,
660 "child-internal-pad-y", &ipad_y,
662 gtk_button_box_child_requisition (widget,
667 n_primaries = nvis_children - n_secondaries;
670 if (orientation == GTK_ORIENTATION_HORIZONTAL)
676 list = children = _gtk_box_get_children (GTK_BOX (widget));
681 child = children->data;
682 children = children->next;
684 if (gtk_widget_get_visible (child))
686 if (gtk_button_box_get_child_secondary (bbox, child))
687 secondary_size += sizes[i];
689 primary_size += sizes[i];
693 total_size = primary_size + secondary_size;
695 gtk_widget_set_allocation (widget, allocation);
697 if (orientation == GTK_ORIENTATION_HORIZONTAL)
698 width = allocation->width - border_width*2;
700 height = allocation->height - border_width*2;
702 switch (priv->layout_style)
704 case GTK_BUTTONBOX_SPREAD:
706 if (orientation == GTK_ORIENTATION_HORIZONTAL)
708 childspacing = (width - total_size) / (nvis_children + 1);
709 x = allocation->x + border_width + childspacing;
710 secondary_x = x + primary_size + n_primaries * childspacing;
714 childspacing = (height - total_size) / (nvis_children + 1);
715 y = allocation->y + border_width + childspacing;
716 secondary_y = y + primary_size + n_primaries * childspacing;
721 case GTK_BUTTONBOX_EDGE:
723 if (orientation == GTK_ORIENTATION_HORIZONTAL)
725 if (nvis_children >= 2)
727 childspacing = (width - total_size) / (nvis_children - 1);
728 x = allocation->x + border_width;
729 secondary_x = x + primary_size + n_primaries * childspacing;
733 /* one or zero children, just center */
734 childspacing = width;
735 x = secondary_x = allocation->x
736 + (allocation->width - widths[0]) / 2;
741 if (nvis_children >= 2)
743 childspacing = (height - total_size) / (nvis_children - 1);
744 y = allocation->y + border_width;
745 secondary_y = y + primary_size + n_primaries * childspacing;
749 /* one or zero children, just center */
750 childspacing = height;
751 y = secondary_y = allocation->y
752 + (allocation->height - heights[0]) / 2;
758 case GTK_BUTTONBOX_START:
760 if (orientation == GTK_ORIENTATION_HORIZONTAL)
762 childspacing = spacing;
763 x = allocation->x + border_width;
764 secondary_x = allocation->x + allocation->width
765 - secondary_size - spacing * (n_secondaries - 1) - border_width;
769 childspacing = spacing;
770 y = allocation->y + border_width;
771 secondary_y = allocation->y + allocation->height
772 - secondary_size - spacing * (n_secondaries - 1) - border_width;
777 case GTK_BUTTONBOX_END:
779 if (orientation == GTK_ORIENTATION_HORIZONTAL)
781 childspacing = spacing;
782 x = allocation->x + allocation->width
783 - primary_size - spacing * (n_primaries - 1) - border_width;
784 secondary_x = allocation->x + border_width;
788 childspacing = spacing;
789 y = allocation->y + allocation->height
790 - primary_size - spacing * (n_primaries - 1) - border_width;
791 secondary_y = allocation->y + border_width;
796 case GTK_BUTTONBOX_CENTER:
798 if (orientation == GTK_ORIENTATION_HORIZONTAL)
800 childspacing = spacing;
803 - (primary_size + spacing * (n_primaries - 1))) / 2
804 + (secondary_size + n_secondaries * spacing) / 2;
805 secondary_x = allocation->x + border_width;
809 childspacing = spacing;
812 - (primary_size + spacing * (n_primaries - 1))) / 2
813 + (secondary_size + n_secondaries * spacing) / 2;
814 secondary_y = allocation->y + border_width;
820 g_assert_not_reached ();
830 child = children->data;
831 children = children->next;
833 if (gtk_widget_get_visible (child))
835 child_allocation.width = widths[i];
836 child_allocation.height = heights[i];
838 if (orientation == GTK_ORIENTATION_HORIZONTAL)
840 child_allocation.y = allocation->y + (allocation->height - child_allocation.height) / 2;
842 if (gtk_button_box_get_child_secondary (bbox, child))
844 child_allocation.x = secondary_x;
845 secondary_x += child_allocation.width + childspacing;
849 child_allocation.x = x;
850 x += child_allocation.width + childspacing;
853 if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
854 child_allocation.x = (allocation->x + allocation->width)
855 - (child_allocation.x + child_allocation.width - allocation->x);
859 child_allocation.x = allocation->x + (allocation->width - child_allocation.width) / 2;
861 if (gtk_button_box_get_child_secondary (bbox, child))
863 child_allocation.y = secondary_y;
864 secondary_y += child_allocation.height + childspacing;
868 child_allocation.y = y;
869 y += child_allocation.height + childspacing;
873 gtk_widget_size_allocate (child, &child_allocation);
884 * gtk_button_box_new:
885 * @orientation: the box' orientation.
887 * Creates a new #GtkButtonBox.
889 * Return value: a new #GtkButtonBox.
894 gtk_button_box_new (GtkOrientation orientation)
896 return g_object_new (GTK_TYPE_BUTTON_BOX,
897 "orientation", orientation,