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.
59 #include "gtkorientable.h"
60 #include "gtkprivate.h"
64 struct _GtkButtonBoxPriv
66 GtkButtonBoxStyle layout_style;
71 gint child_min_height;
84 static void gtk_button_box_set_property (GObject *object,
88 static void gtk_button_box_get_property (GObject *object,
92 static void gtk_button_box_size_request (GtkWidget *widget,
93 GtkRequisition *requisition);
94 static void gtk_button_box_size_allocate (GtkWidget *widget,
95 GtkAllocation *allocation);
96 static void gtk_button_box_set_child_property (GtkContainer *container,
101 static void gtk_button_box_get_child_property (GtkContainer *container,
107 #define DEFAULT_CHILD_MIN_WIDTH 85
108 #define DEFAULT_CHILD_MIN_HEIGHT 27
109 #define DEFAULT_CHILD_IPAD_X 4
110 #define DEFAULT_CHILD_IPAD_Y 0
112 G_DEFINE_TYPE (GtkButtonBox, gtk_button_box, GTK_TYPE_BOX)
115 gtk_button_box_class_init (GtkButtonBoxClass *class)
117 GtkWidgetClass *widget_class;
118 GObjectClass *gobject_class;
119 GtkContainerClass *container_class;
121 gobject_class = G_OBJECT_CLASS (class);
122 widget_class = (GtkWidgetClass*) class;
123 container_class = (GtkContainerClass*) class;
125 gobject_class->set_property = gtk_button_box_set_property;
126 gobject_class->get_property = gtk_button_box_get_property;
128 widget_class->size_request = gtk_button_box_size_request;
129 widget_class->size_allocate = gtk_button_box_size_allocate;
131 container_class->set_child_property = gtk_button_box_set_child_property;
132 container_class->get_child_property = gtk_button_box_get_child_property;
134 /* FIXME we need to override the "spacing" property on GtkBox once
135 * libgobject allows that.
137 gtk_widget_class_install_style_property (widget_class,
138 g_param_spec_int ("child-min-width",
139 P_("Minimum child width"),
140 P_("Minimum width of buttons inside the box"),
143 DEFAULT_CHILD_MIN_WIDTH,
144 GTK_PARAM_READABLE));
146 gtk_widget_class_install_style_property (widget_class,
147 g_param_spec_int ("child-min-height",
148 P_("Minimum child height"),
149 P_("Minimum height of buttons inside the box"),
152 DEFAULT_CHILD_MIN_HEIGHT,
153 GTK_PARAM_READABLE));
155 gtk_widget_class_install_style_property (widget_class,
156 g_param_spec_int ("child-internal-pad-x",
157 P_("Child internal width padding"),
158 P_("Amount to increase child's size on either side"),
161 DEFAULT_CHILD_IPAD_X,
162 GTK_PARAM_READABLE));
164 gtk_widget_class_install_style_property (widget_class,
165 g_param_spec_int ("child-internal-pad-y",
166 P_("Child internal height padding"),
167 P_("Amount to increase child's size on the top and bottom"),
170 DEFAULT_CHILD_IPAD_Y,
171 GTK_PARAM_READABLE));
172 g_object_class_install_property (gobject_class,
174 g_param_spec_enum ("layout-style",
176 P_("How to layout the buttons in the box. Possible values are default, spread, edge, start and end"),
177 GTK_TYPE_BUTTON_BOX_STYLE,
178 GTK_BUTTONBOX_DEFAULT_STYLE,
179 GTK_PARAM_READWRITE));
181 gtk_container_class_install_child_property (container_class,
182 CHILD_PROP_SECONDARY,
183 g_param_spec_boolean ("secondary",
185 P_("If TRUE, the child appears in a secondary group of children, suitable for, e.g., help buttons"),
187 GTK_PARAM_READWRITE));
189 g_type_class_add_private (class, sizeof (GtkButtonBoxPriv));
193 gtk_button_box_init (GtkButtonBox *button_box)
195 GtkButtonBoxPriv *priv;
197 button_box->priv = G_TYPE_INSTANCE_GET_PRIVATE (button_box,
200 priv = button_box->priv;
202 gtk_box_set_spacing (GTK_BOX (button_box), 0);
203 priv->child_min_width = GTK_BUTTONBOX_DEFAULT;
204 priv->child_min_height = GTK_BUTTONBOX_DEFAULT;
205 priv->child_ipad_x = GTK_BUTTONBOX_DEFAULT;
206 priv->child_ipad_y = GTK_BUTTONBOX_DEFAULT;
207 priv->layout_style = GTK_BUTTONBOX_DEFAULT_STYLE;
211 gtk_button_box_set_property (GObject *object,
218 case PROP_LAYOUT_STYLE:
219 gtk_button_box_set_layout (GTK_BUTTON_BOX (object),
220 g_value_get_enum (value));
223 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
229 gtk_button_box_get_property (GObject *object,
234 GtkButtonBoxPriv *priv = GTK_BUTTON_BOX (object)->priv;
238 case PROP_LAYOUT_STYLE:
239 g_value_set_enum (value, priv->layout_style);
242 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
248 gtk_button_box_set_child_property (GtkContainer *container,
256 case CHILD_PROP_SECONDARY:
257 gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (container), child,
258 g_value_get_boolean (value));
261 GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
267 gtk_button_box_get_child_property (GtkContainer *container,
275 case CHILD_PROP_SECONDARY:
276 g_value_set_boolean (value,
277 gtk_button_box_get_child_secondary (GTK_BUTTON_BOX (container),
281 GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
287 * gtk_button_box_set_layout:
288 * @widget: a #GtkButtonBox
289 * @layout_style: the new layout style
291 * Changes the way buttons are arranged in their container.
294 gtk_button_box_set_layout (GtkButtonBox *widget,
295 GtkButtonBoxStyle layout_style)
297 GtkButtonBoxPriv *priv;
299 g_return_if_fail (GTK_IS_BUTTON_BOX (widget));
300 g_return_if_fail (layout_style >= GTK_BUTTONBOX_DEFAULT_STYLE &&
301 layout_style <= GTK_BUTTONBOX_CENTER);
305 if (priv->layout_style != layout_style)
307 priv->layout_style = layout_style;
308 g_object_notify (G_OBJECT (widget), "layout-style");
309 gtk_widget_queue_resize (GTK_WIDGET (widget));
314 * gtk_button_box_get_layout:
315 * @widget: a #GtkButtonBox
317 * Retrieves the method being used to arrange the buttons in a button box.
319 * Returns: the method used to layout buttons in @widget.
322 gtk_button_box_get_layout (GtkButtonBox *widget)
324 g_return_val_if_fail (GTK_IS_BUTTON_BOX (widget), GTK_BUTTONBOX_SPREAD);
326 return widget->priv->layout_style;
330 * gtk_button_box_get_child_secondary:
331 * @widget: a #GtkButtonBox
332 * @child: a child of @widget
334 * Returns whether @child should appear in a secondary group of children.
336 * Return value: whether @child should appear in a secondary group of children.
341 gtk_button_box_get_child_secondary (GtkButtonBox *widget,
344 GtkBoxChild *child_info;
347 g_return_val_if_fail (GTK_IS_BUTTON_BOX (widget), FALSE);
348 g_return_val_if_fail (GTK_IS_WIDGET (child), FALSE);
351 list = GTK_BOX (widget)->children;
354 child_info = list->data;
355 if (child_info->widget == child)
361 g_return_val_if_fail (list != NULL, FALSE);
363 return child_info->is_secondary;
367 * gtk_button_box_set_child_secondary
368 * @widget: a #GtkButtonBox
369 * @child: a child of @widget
370 * @is_secondary: if %TRUE, the @child appears in a secondary group of the
373 * Sets whether @child should appear in a secondary group of children.
374 * A typical use of a secondary child is the help button in a dialog.
376 * This group appears after the other children if the style
377 * is %GTK_BUTTONBOX_START, %GTK_BUTTONBOX_SPREAD or
378 * %GTK_BUTTONBOX_EDGE, and before the other children if the style
379 * is %GTK_BUTTONBOX_END. For horizontal button boxes, the definition
380 * of before/after depends on direction of the widget (see
381 * gtk_widget_set_direction()). If the style is %GTK_BUTTONBOX_START
382 * or %GTK_BUTTONBOX_END, then the secondary children are aligned at
383 * the other end of the button box from the main children. For the
384 * other styles, they appear immediately next to the main children.
387 gtk_button_box_set_child_secondary (GtkButtonBox *widget,
389 gboolean is_secondary)
393 g_return_if_fail (GTK_IS_BUTTON_BOX (widget));
394 g_return_if_fail (GTK_IS_WIDGET (child));
395 g_return_if_fail (child->parent == GTK_WIDGET (widget));
397 list = GTK_BOX (widget)->children;
400 GtkBoxChild *child_info = list->data;
401 if (child_info->widget == child)
403 child_info->is_secondary = is_secondary;
410 gtk_widget_child_notify (child, "secondary");
412 if (gtk_widget_get_visible (GTK_WIDGET (widget))
413 && gtk_widget_get_visible (child))
414 gtk_widget_queue_resize (child);
417 /* Ask children how much space they require and round up
418 to match minimum size and internal padding.
419 Returns the size each single child should have. */
421 _gtk_button_box_child_requisition (GtkWidget *widget,
423 int *nvis_secondaries,
427 GtkButtonBoxPriv *priv;
435 GtkRequisition child_requisition;
443 gint child_min_width;
444 gint child_min_height;
448 g_return_if_fail (GTK_IS_BUTTON_BOX (widget));
450 bbox = GTK_BUTTON_BOX (widget);
453 gtk_widget_style_get (widget,
454 "child-min-width", &width_default,
455 "child-min-height", &height_default,
456 "child-internal-pad-x", &ipad_x_default,
457 "child-internal-pad-y", &ipad_y_default,
460 child_min_width = priv->child_min_width != GTK_BUTTONBOX_DEFAULT
461 ? priv->child_min_width : width_default;
462 child_min_height = priv->child_min_height != GTK_BUTTONBOX_DEFAULT
463 ? priv->child_min_height : height_default;
464 ipad_x = priv->child_ipad_x != GTK_BUTTONBOX_DEFAULT
465 ? priv->child_ipad_x : ipad_x_default;
466 ipad_y = priv->child_ipad_y != GTK_BUTTONBOX_DEFAULT
467 ? priv->child_ipad_y : ipad_y_default;
471 children = GTK_BOX(bbox)->children;
472 needed_width = child_min_width;
473 needed_height = child_min_height;
479 child = children->data;
480 children = children->next;
482 if (gtk_widget_get_visible (child->widget))
485 gtk_widget_size_request (child->widget, &child_requisition);
487 if (child_requisition.width + ipad_w > needed_width)
488 needed_width = child_requisition.width + ipad_w;
489 if (child_requisition.height + ipad_h > needed_height)
490 needed_height = child_requisition.height + ipad_h;
491 if (child->is_secondary)
497 *nvis_children = nchildren;
498 if (nvis_secondaries)
499 *nvis_secondaries = nsecondaries;
501 *width = needed_width;
503 *height = needed_height;
506 /* this is a kludge function to support the deprecated
507 * gtk_[vh]button_box_set_layout_default() just in case anyone is still
510 static GtkButtonBoxStyle
511 gtk_button_box_kludge_get_layout_default (GtkButtonBox *widget)
513 GtkOrientation orientation;
515 orientation = gtk_orientable_get_orientation (GTK_ORIENTABLE (widget));
517 if (orientation == GTK_ORIENTATION_HORIZONTAL)
518 return _gtk_hbutton_box_get_layout_default ();
520 return _gtk_vbutton_box_get_layout_default ();
524 gtk_button_box_size_request (GtkWidget *widget,
525 GtkRequisition *requisition)
527 GtkButtonBoxPriv *priv;
534 GtkButtonBoxStyle layout;
535 GtkOrientation orientation;
537 box = GTK_BOX (widget);
538 bbox = GTK_BUTTON_BOX (widget);
541 orientation = gtk_orientable_get_orientation (GTK_ORIENTABLE (widget));
542 spacing = box->spacing;
543 layout = priv->layout_style != GTK_BUTTONBOX_DEFAULT_STYLE
544 ? priv->layout_style : gtk_button_box_kludge_get_layout_default (GTK_BUTTON_BOX (widget));
546 _gtk_button_box_child_requisition (widget,
552 if (nvis_children == 0)
554 requisition->width = 0;
555 requisition->height = 0;
561 case GTK_BUTTONBOX_SPREAD:
562 if (orientation == GTK_ORIENTATION_HORIZONTAL)
564 nvis_children*child_width + ((nvis_children+1)*spacing);
566 requisition->height =
567 nvis_children*child_height + ((nvis_children+1)*spacing);
570 case GTK_BUTTONBOX_EDGE:
571 case GTK_BUTTONBOX_START:
572 case GTK_BUTTONBOX_END:
573 case GTK_BUTTONBOX_CENTER:
574 if (orientation == GTK_ORIENTATION_HORIZONTAL)
576 nvis_children*child_width + ((nvis_children-1)*spacing);
578 requisition->height =
579 nvis_children*child_height + ((nvis_children-1)*spacing);
583 g_assert_not_reached ();
587 if (orientation == GTK_ORIENTATION_HORIZONTAL)
588 requisition->height = child_height;
590 requisition->width = child_width;
593 requisition->width += GTK_CONTAINER (box)->border_width * 2;
594 requisition->height += GTK_CONTAINER (box)->border_width * 2;
598 gtk_button_box_size_allocate (GtkWidget *widget,
599 GtkAllocation *allocation)
601 GtkButtonBoxPriv *priv;
606 GtkAllocation child_allocation;
613 gint secondary_x = 0;
614 gint secondary_y = 0;
618 gint childspacing = 0;
619 GtkButtonBoxStyle layout;
621 GtkOrientation orientation;
623 base_box = GTK_BOX (widget);
624 box = GTK_BUTTON_BOX (widget);
627 orientation = gtk_orientable_get_orientation (GTK_ORIENTABLE (widget));
628 spacing = base_box->spacing;
629 layout = priv->layout_style != GTK_BUTTONBOX_DEFAULT_STYLE
630 ? priv->layout_style : gtk_button_box_kludge_get_layout_default (GTK_BUTTON_BOX (widget));
631 _gtk_button_box_child_requisition (widget,
636 widget->allocation = *allocation;
638 if (orientation == GTK_ORIENTATION_HORIZONTAL)
639 width = allocation->width - GTK_CONTAINER (box)->border_width*2;
641 height = allocation->height - GTK_CONTAINER (box)->border_width*2;
645 case GTK_BUTTONBOX_SPREAD:
647 if (orientation == GTK_ORIENTATION_HORIZONTAL)
649 childspacing = (width - (nvis_children * child_width))
650 / (nvis_children + 1);
651 x = allocation->x + GTK_CONTAINER (box)->border_width
653 secondary_x = x + ((nvis_children - n_secondaries)
654 * (child_width + childspacing));
658 childspacing = (height - (nvis_children * child_height))
659 / (nvis_children + 1);
660 y = allocation->y + GTK_CONTAINER (box)->border_width
662 secondary_y = y + ((nvis_children - n_secondaries)
663 * (child_height + childspacing));
668 case GTK_BUTTONBOX_EDGE:
670 if (orientation == GTK_ORIENTATION_HORIZONTAL)
672 if (nvis_children >= 2)
674 childspacing = (width - (nvis_children * child_width))
675 / (nvis_children - 1);
676 x = allocation->x + GTK_CONTAINER (box)->border_width;
677 secondary_x = x + ((nvis_children - n_secondaries)
678 * (child_width + childspacing));
682 /* one or zero children, just center */
683 childspacing = width;
684 x = secondary_x = allocation->x
685 + (allocation->width - child_width) / 2;
690 if (nvis_children >= 2)
692 childspacing = (height - (nvis_children*child_height))
694 y = allocation->y + GTK_CONTAINER (box)->border_width;
695 secondary_y = y + ((nvis_children - n_secondaries)
696 * (child_height + childspacing));
700 /* one or zero children, just center */
701 childspacing = height;
702 y = secondary_y = allocation->y
703 + (allocation->height - child_height) / 2;
709 case GTK_BUTTONBOX_START:
711 if (orientation == GTK_ORIENTATION_HORIZONTAL)
713 childspacing = spacing;
714 x = allocation->x + GTK_CONTAINER (box)->border_width;
715 secondary_x = allocation->x + allocation->width
716 - child_width * n_secondaries
717 - spacing * (n_secondaries - 1)
718 - GTK_CONTAINER (box)->border_width;
722 childspacing = spacing;
723 y = allocation->y + GTK_CONTAINER (box)->border_width;
724 secondary_y = allocation->y + allocation->height
725 - child_height * n_secondaries
726 - spacing * (n_secondaries - 1)
727 - GTK_CONTAINER (box)->border_width;
732 case GTK_BUTTONBOX_END:
734 if (orientation == GTK_ORIENTATION_HORIZONTAL)
736 childspacing = spacing;
737 x = allocation->x + allocation->width
738 - child_width * (nvis_children - n_secondaries)
739 - spacing * (nvis_children - n_secondaries - 1)
740 - GTK_CONTAINER (box)->border_width;
741 secondary_x = allocation->x + GTK_CONTAINER (box)->border_width;
745 childspacing = spacing;
746 y = allocation->y + allocation->height
747 - child_height * (nvis_children - n_secondaries)
748 - spacing * (nvis_children - n_secondaries - 1)
749 - GTK_CONTAINER (box)->border_width;
750 secondary_y = allocation->y + GTK_CONTAINER (box)->border_width;
755 case GTK_BUTTONBOX_CENTER:
757 if (orientation == GTK_ORIENTATION_HORIZONTAL)
759 childspacing = spacing;
762 - (child_width * (nvis_children - n_secondaries)
763 + spacing * (nvis_children - n_secondaries - 1))) / 2
764 + (n_secondaries * child_width + n_secondaries * spacing) / 2;
765 secondary_x = allocation->x + GTK_CONTAINER (box)->border_width;
769 childspacing = spacing;
772 - (child_height * (nvis_children - n_secondaries)
773 + spacing * (nvis_children - n_secondaries - 1))) / 2
774 + (n_secondaries * child_height + n_secondaries * spacing) / 2;
775 secondary_y = allocation->y + GTK_CONTAINER (box)->border_width;
781 g_assert_not_reached ();
785 if (orientation == GTK_ORIENTATION_HORIZONTAL)
787 y = allocation->y + (allocation->height - child_height) / 2;
788 childspace = child_width + childspacing;
792 x = allocation->x + (allocation->width - child_width) / 2;
793 childspace = child_height + childspacing;
796 children = GTK_BOX (box)->children;
800 child = children->data;
801 children = children->next;
803 if (gtk_widget_get_visible (child->widget))
805 child_allocation.width = child_width;
806 child_allocation.height = child_height;
808 if (orientation == GTK_ORIENTATION_HORIZONTAL)
810 child_allocation.y = y;
812 if (child->is_secondary)
814 child_allocation.x = secondary_x;
815 secondary_x += childspace;
819 child_allocation.x = x;
823 if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
824 child_allocation.x = (allocation->x + allocation->width)
825 - (child_allocation.x + child_width - allocation->x);
829 child_allocation.x = x;
831 if (child->is_secondary)
833 child_allocation.y = secondary_y;
834 secondary_y += childspace;
838 child_allocation.y = y;
843 gtk_widget_size_allocate (child->widget, &child_allocation);
849 * gtk_button_box_new:
850 * @orientation: the box' orientation.
852 * Creates a new #GtkButtonBox.
854 * Return value: a new #GtkButtonBox.
859 gtk_button_box_new (GtkOrientation orientation)
861 return g_object_new (GTK_TYPE_BUTTON_BOX,
862 "orientation", orientation,