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 (child->parent == 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. */
400 _gtk_button_box_child_requisition (GtkWidget *widget,
402 int *nvis_secondaries,
406 GtkButtonBoxPriv *priv;
408 GList *children, *list;
413 GtkRequisition child_requisition;
420 gint child_min_width;
421 gint child_min_height;
425 g_return_if_fail (GTK_IS_BUTTON_BOX (widget));
427 bbox = GTK_BUTTON_BOX (widget);
430 gtk_widget_style_get (widget,
431 "child-min-width", &width_default,
432 "child-min-height", &height_default,
433 "child-internal-pad-x", &ipad_x_default,
434 "child-internal-pad-y", &ipad_y_default,
437 child_min_width = width_default;
438 child_min_height = height_default;
439 ipad_x = ipad_x_default;
440 ipad_y = ipad_y_default;
444 list = children = _gtk_box_get_children (GTK_BOX (bbox));
445 needed_width = child_min_width;
446 needed_height = child_min_height;
453 gboolean is_secondary;
455 child = children->data;
456 children = children->next;
458 is_secondary = gtk_button_box_get_child_secondary (bbox, child);
460 if (gtk_widget_get_visible (child))
463 gtk_widget_size_request (child, &child_requisition);
465 if (child_requisition.width + ipad_w > needed_width)
466 needed_width = child_requisition.width + ipad_w;
467 if (child_requisition.height + ipad_h > needed_height)
468 needed_height = child_requisition.height + ipad_h;
477 *nvis_children = nchildren;
478 if (nvis_secondaries)
479 *nvis_secondaries = nsecondaries;
481 *width = needed_width;
483 *height = needed_height;
487 gtk_button_box_size_request (GtkWidget *widget,
488 GtkRequisition *requisition)
490 GtkButtonBoxPriv *priv;
498 GtkOrientation orientation;
500 box = GTK_BOX (widget);
501 bbox = GTK_BUTTON_BOX (widget);
504 orientation = gtk_orientable_get_orientation (GTK_ORIENTABLE (widget));
505 spacing = gtk_box_get_spacing (box);
507 _gtk_button_box_child_requisition (widget,
513 if (nvis_children == 0)
515 requisition->width = 0;
516 requisition->height = 0;
520 switch (priv->layout_style)
522 case GTK_BUTTONBOX_SPREAD:
523 if (orientation == GTK_ORIENTATION_HORIZONTAL)
525 nvis_children*child_width + ((nvis_children+1)*spacing);
527 requisition->height =
528 nvis_children*child_height + ((nvis_children+1)*spacing);
531 case GTK_BUTTONBOX_EDGE:
532 case GTK_BUTTONBOX_START:
533 case GTK_BUTTONBOX_END:
534 case GTK_BUTTONBOX_CENTER:
535 if (orientation == GTK_ORIENTATION_HORIZONTAL)
537 nvis_children*child_width + ((nvis_children-1)*spacing);
539 requisition->height =
540 nvis_children*child_height + ((nvis_children-1)*spacing);
544 g_assert_not_reached ();
548 if (orientation == GTK_ORIENTATION_HORIZONTAL)
549 requisition->height = child_height;
551 requisition->width = child_width;
554 border_width = gtk_container_get_border_width (GTK_CONTAINER (box));
555 requisition->width += border_width * 2;
556 requisition->height += border_width * 2;
560 gtk_button_box_size_allocate (GtkWidget *widget,
561 GtkAllocation *allocation)
563 GtkButtonBoxPriv *priv;
566 GList *children, *list;
567 GtkAllocation child_allocation;
574 gint secondary_x = 0;
575 gint secondary_y = 0;
579 gint childspacing = 0;
582 GtkOrientation orientation;
584 base_box = GTK_BOX (widget);
585 box = GTK_BUTTON_BOX (widget);
588 border_width = gtk_container_get_border_width (GTK_CONTAINER (box));
589 orientation = gtk_orientable_get_orientation (GTK_ORIENTABLE (widget));
590 spacing = gtk_box_get_spacing (base_box);
591 _gtk_button_box_child_requisition (widget,
596 widget->allocation = *allocation;
598 if (orientation == GTK_ORIENTATION_HORIZONTAL)
599 width = allocation->width - border_width*2;
601 height = allocation->height - border_width*2;
603 switch (priv->layout_style)
605 case GTK_BUTTONBOX_SPREAD:
607 if (orientation == GTK_ORIENTATION_HORIZONTAL)
609 childspacing = (width - (nvis_children * child_width))
610 / (nvis_children + 1);
611 x = allocation->x + border_width + childspacing;
612 secondary_x = x + ((nvis_children - n_secondaries)
613 * (child_width + childspacing));
617 childspacing = (height - (nvis_children * child_height))
618 / (nvis_children + 1);
619 y = allocation->y + border_width + childspacing;
620 secondary_y = y + ((nvis_children - n_secondaries)
621 * (child_height + childspacing));
626 case GTK_BUTTONBOX_EDGE:
628 if (orientation == GTK_ORIENTATION_HORIZONTAL)
630 if (nvis_children >= 2)
632 childspacing = (width - (nvis_children * child_width))
633 / (nvis_children - 1);
634 x = allocation->x + border_width;
635 secondary_x = x + ((nvis_children - n_secondaries)
636 * (child_width + childspacing));
640 /* one or zero children, just center */
641 childspacing = width;
642 x = secondary_x = allocation->x
643 + (allocation->width - child_width) / 2;
648 if (nvis_children >= 2)
650 childspacing = (height - (nvis_children*child_height))
652 y = allocation->y + border_width;
653 secondary_y = y + ((nvis_children - n_secondaries)
654 * (child_height + childspacing));
658 /* one or zero children, just center */
659 childspacing = height;
660 y = secondary_y = allocation->y
661 + (allocation->height - child_height) / 2;
667 case GTK_BUTTONBOX_START:
669 if (orientation == GTK_ORIENTATION_HORIZONTAL)
671 childspacing = spacing;
672 x = allocation->x + border_width;
673 secondary_x = allocation->x + allocation->width
674 - child_width * n_secondaries
675 - spacing * (n_secondaries - 1)
680 childspacing = spacing;
681 y = allocation->y + border_width;
682 secondary_y = allocation->y + allocation->height
683 - child_height * n_secondaries
684 - spacing * (n_secondaries - 1)
690 case GTK_BUTTONBOX_END:
692 if (orientation == GTK_ORIENTATION_HORIZONTAL)
694 childspacing = spacing;
695 x = allocation->x + allocation->width
696 - child_width * (nvis_children - n_secondaries)
697 - spacing * (nvis_children - n_secondaries - 1)
699 secondary_x = allocation->x + border_width;
703 childspacing = spacing;
704 y = allocation->y + allocation->height
705 - child_height * (nvis_children - n_secondaries)
706 - spacing * (nvis_children - n_secondaries - 1)
708 secondary_y = allocation->y + border_width;
713 case GTK_BUTTONBOX_CENTER:
715 if (orientation == GTK_ORIENTATION_HORIZONTAL)
717 childspacing = spacing;
720 - (child_width * (nvis_children - n_secondaries)
721 + spacing * (nvis_children - n_secondaries - 1))) / 2
722 + (n_secondaries * child_width + n_secondaries * spacing) / 2;
723 secondary_x = allocation->x + border_width;
727 childspacing = spacing;
730 - (child_height * (nvis_children - n_secondaries)
731 + spacing * (nvis_children - n_secondaries - 1))) / 2
732 + (n_secondaries * child_height + n_secondaries * spacing) / 2;
733 secondary_y = allocation->y + border_width;
739 g_assert_not_reached ();
743 if (orientation == GTK_ORIENTATION_HORIZONTAL)
745 y = allocation->y + (allocation->height - child_height) / 2;
746 childspace = child_width + childspacing;
750 x = allocation->x + (allocation->width - child_width) / 2;
751 childspace = child_height + childspacing;
754 list = children = _gtk_box_get_children (GTK_BOX (box));
759 gboolean is_secondary;
761 child = children->data;
762 children = children->next;
764 is_secondary = gtk_button_box_get_child_secondary (box, child);
766 if (gtk_widget_get_visible (child))
768 child_allocation.width = child_width;
769 child_allocation.height = child_height;
771 if (orientation == GTK_ORIENTATION_HORIZONTAL)
773 child_allocation.y = y;
777 child_allocation.x = secondary_x;
778 secondary_x += childspace;
782 child_allocation.x = x;
786 if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
787 child_allocation.x = (allocation->x + allocation->width)
788 - (child_allocation.x + child_width - allocation->x);
792 child_allocation.x = x;
796 child_allocation.y = secondary_y;
797 secondary_y += childspace;
801 child_allocation.y = y;
806 gtk_widget_size_allocate (child, &child_allocation);
814 * gtk_button_box_new:
815 * @orientation: the box' orientation.
817 * Creates a new #GtkButtonBox.
819 * Return value: a new #GtkButtonBox.
824 gtk_button_box_new (GtkOrientation orientation)
826 return g_object_new (GTK_TYPE_BUTTON_BOX,
827 "orientation", orientation,