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"
74 static void gtk_button_box_set_property (GObject *object,
78 static void gtk_button_box_get_property (GObject *object,
82 static void gtk_button_box_size_request (GtkWidget *widget,
83 GtkRequisition *requisition);
84 static void gtk_button_box_size_allocate (GtkWidget *widget,
85 GtkAllocation *allocation);
86 static void gtk_button_box_set_child_property (GtkContainer *container,
91 static void gtk_button_box_get_child_property (GtkContainer *container,
97 #define DEFAULT_CHILD_MIN_WIDTH 85
98 #define DEFAULT_CHILD_MIN_HEIGHT 27
99 #define DEFAULT_CHILD_IPAD_X 4
100 #define DEFAULT_CHILD_IPAD_Y 0
102 G_DEFINE_TYPE (GtkButtonBox, gtk_button_box, GTK_TYPE_BOX)
105 gtk_button_box_class_init (GtkButtonBoxClass *class)
107 GtkWidgetClass *widget_class;
108 GObjectClass *gobject_class;
109 GtkContainerClass *container_class;
111 gobject_class = G_OBJECT_CLASS (class);
112 widget_class = (GtkWidgetClass*) class;
113 container_class = (GtkContainerClass*) class;
115 gobject_class->set_property = gtk_button_box_set_property;
116 gobject_class->get_property = gtk_button_box_get_property;
118 widget_class->size_request = gtk_button_box_size_request;
119 widget_class->size_allocate = gtk_button_box_size_allocate;
121 container_class->set_child_property = gtk_button_box_set_child_property;
122 container_class->get_child_property = gtk_button_box_get_child_property;
124 /* FIXME we need to override the "spacing" property on GtkBox once
125 * libgobject allows that.
127 gtk_widget_class_install_style_property (widget_class,
128 g_param_spec_int ("child-min-width",
129 P_("Minimum child width"),
130 P_("Minimum width of buttons inside the box"),
133 DEFAULT_CHILD_MIN_WIDTH,
134 GTK_PARAM_READABLE));
136 gtk_widget_class_install_style_property (widget_class,
137 g_param_spec_int ("child-min-height",
138 P_("Minimum child height"),
139 P_("Minimum height of buttons inside the box"),
142 DEFAULT_CHILD_MIN_HEIGHT,
143 GTK_PARAM_READABLE));
145 gtk_widget_class_install_style_property (widget_class,
146 g_param_spec_int ("child-internal-pad-x",
147 P_("Child internal width padding"),
148 P_("Amount to increase child's size on either side"),
151 DEFAULT_CHILD_IPAD_X,
152 GTK_PARAM_READABLE));
154 gtk_widget_class_install_style_property (widget_class,
155 g_param_spec_int ("child-internal-pad-y",
156 P_("Child internal height padding"),
157 P_("Amount to increase child's size on the top and bottom"),
160 DEFAULT_CHILD_IPAD_Y,
161 GTK_PARAM_READABLE));
162 g_object_class_install_property (gobject_class,
164 g_param_spec_enum ("layout-style",
166 P_("How to layout the buttons in the box. Possible values are default, spread, edge, start and end"),
167 GTK_TYPE_BUTTON_BOX_STYLE,
168 GTK_BUTTONBOX_DEFAULT_STYLE,
169 GTK_PARAM_READWRITE));
171 gtk_container_class_install_child_property (container_class,
172 CHILD_PROP_SECONDARY,
173 g_param_spec_boolean ("secondary",
175 P_("If TRUE, the child appears in a secondary group of children, suitable for, e.g., help buttons"),
177 GTK_PARAM_READWRITE));
181 gtk_button_box_init (GtkButtonBox *button_box)
183 GTK_BOX (button_box)->spacing = 0;
184 button_box->child_min_width = GTK_BUTTONBOX_DEFAULT;
185 button_box->child_min_height = GTK_BUTTONBOX_DEFAULT;
186 button_box->child_ipad_x = GTK_BUTTONBOX_DEFAULT;
187 button_box->child_ipad_y = GTK_BUTTONBOX_DEFAULT;
188 button_box->layout_style = GTK_BUTTONBOX_DEFAULT_STYLE;
192 gtk_button_box_set_property (GObject *object,
199 case PROP_LAYOUT_STYLE:
200 gtk_button_box_set_layout (GTK_BUTTON_BOX (object),
201 g_value_get_enum (value));
204 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
210 gtk_button_box_get_property (GObject *object,
217 case PROP_LAYOUT_STYLE:
218 g_value_set_enum (value, GTK_BUTTON_BOX (object)->layout_style);
221 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
227 gtk_button_box_set_child_property (GtkContainer *container,
235 case CHILD_PROP_SECONDARY:
236 gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (container), child,
237 g_value_get_boolean (value));
240 GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
246 gtk_button_box_get_child_property (GtkContainer *container,
254 case CHILD_PROP_SECONDARY:
255 g_value_set_boolean (value,
256 gtk_button_box_get_child_secondary (GTK_BUTTON_BOX (container),
260 GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
266 * gtk_button_box_set_layout:
267 * @widget: a #GtkButtonBox
268 * @layout_style: the new layout style
270 * Changes the way buttons are arranged in their container.
273 gtk_button_box_set_layout (GtkButtonBox *widget,
274 GtkButtonBoxStyle layout_style)
276 g_return_if_fail (GTK_IS_BUTTON_BOX (widget));
277 g_return_if_fail (layout_style >= GTK_BUTTONBOX_DEFAULT_STYLE &&
278 layout_style <= GTK_BUTTONBOX_CENTER);
280 if (widget->layout_style != layout_style)
282 widget->layout_style = layout_style;
283 g_object_notify (G_OBJECT (widget), "layout-style");
284 gtk_widget_queue_resize (GTK_WIDGET (widget));
289 * gtk_button_box_get_layout:
290 * @widget: a #GtkButtonBox
292 * Retrieves the method being used to arrange the buttons in a button box.
294 * Returns: the method used to layout buttons in @widget.
297 gtk_button_box_get_layout (GtkButtonBox *widget)
299 g_return_val_if_fail (GTK_IS_BUTTON_BOX (widget), GTK_BUTTONBOX_SPREAD);
301 return widget->layout_style;
305 * gtk_button_box_get_child_secondary:
306 * @widget: a #GtkButtonBox
307 * @child: a child of @widget
309 * Returns whether @child should appear in a secondary group of children.
311 * Return value: whether @child should appear in a secondary group of children.
316 gtk_button_box_get_child_secondary (GtkButtonBox *widget,
320 GtkBoxChild *child_info;
322 g_return_val_if_fail (GTK_IS_BUTTON_BOX (widget), FALSE);
323 g_return_val_if_fail (GTK_IS_WIDGET (child), FALSE);
326 list = GTK_BOX (widget)->children;
329 child_info = list->data;
330 if (child_info->widget == child)
336 g_return_val_if_fail (list != NULL, FALSE);
338 return child_info->is_secondary;
342 * gtk_button_box_set_child_secondary
343 * @widget: a #GtkButtonBox
344 * @child: a child of @widget
345 * @is_secondary: if %TRUE, the @child appears in a secondary group of the
348 * Sets whether @child should appear in a secondary group of children.
349 * A typical use of a secondary child is the help button in a dialog.
351 * This group appears after the other children if the style
352 * is %GTK_BUTTONBOX_START, %GTK_BUTTONBOX_SPREAD or
353 * %GTK_BUTTONBOX_EDGE, and before the other children if the style
354 * is %GTK_BUTTONBOX_END. For horizontal button boxes, the definition
355 * of before/after depends on direction of the widget (see
356 * gtk_widget_set_direction()). If the style is %GTK_BUTTONBOX_START
357 * or %GTK_BUTTONBOX_END, then the secondary children are aligned at
358 * the other end of the button box from the main children. For the
359 * other styles, they appear immediately next to the main children.
362 gtk_button_box_set_child_secondary (GtkButtonBox *widget,
364 gboolean is_secondary)
368 g_return_if_fail (GTK_IS_BUTTON_BOX (widget));
369 g_return_if_fail (GTK_IS_WIDGET (child));
370 g_return_if_fail (child->parent == GTK_WIDGET (widget));
372 list = GTK_BOX (widget)->children;
375 GtkBoxChild *child_info = list->data;
376 if (child_info->widget == child)
378 child_info->is_secondary = is_secondary;
385 gtk_widget_child_notify (child, "secondary");
387 if (gtk_widget_get_visible (GTK_WIDGET (widget))
388 && gtk_widget_get_visible (child))
389 gtk_widget_queue_resize (child);
392 /* Ask children how much space they require and round up
393 to match minimum size and internal padding.
394 Returns the size each single child should have. */
396 _gtk_button_box_child_requisition (GtkWidget *widget,
398 int *nvis_secondaries,
409 GtkRequisition child_requisition;
417 gint child_min_width;
418 gint child_min_height;
422 g_return_if_fail (GTK_IS_BUTTON_BOX (widget));
424 bbox = GTK_BUTTON_BOX (widget);
426 gtk_widget_style_get (widget,
427 "child-min-width", &width_default,
428 "child-min-height", &height_default,
429 "child-internal-pad-x", &ipad_x_default,
430 "child-internal-pad-y", &ipad_y_default,
433 child_min_width = bbox->child_min_width != GTK_BUTTONBOX_DEFAULT
434 ? bbox->child_min_width : width_default;
435 child_min_height = bbox->child_min_height !=GTK_BUTTONBOX_DEFAULT
436 ? bbox->child_min_height : height_default;
437 ipad_x = bbox->child_ipad_x != GTK_BUTTONBOX_DEFAULT
438 ? bbox->child_ipad_x : ipad_x_default;
439 ipad_y = bbox->child_ipad_y != GTK_BUTTONBOX_DEFAULT
440 ? bbox->child_ipad_y : ipad_y_default;
444 children = GTK_BOX(bbox)->children;
445 needed_width = child_min_width;
446 needed_height = child_min_height;
452 child = children->data;
453 children = children->next;
455 if (gtk_widget_get_visible (child->widget))
458 gtk_widget_size_request (child->widget, &child_requisition);
460 if (child_requisition.width + ipad_w > needed_width)
461 needed_width = child_requisition.width + ipad_w;
462 if (child_requisition.height + ipad_h > needed_height)
463 needed_height = child_requisition.height + ipad_h;
464 if (child->is_secondary)
470 *nvis_children = nchildren;
471 if (nvis_secondaries)
472 *nvis_secondaries = nsecondaries;
474 *width = needed_width;
476 *height = needed_height;
479 /* this is a kludge function to support the deprecated
480 * gtk_[vh]button_box_set_layout_default() just in case anyone is still
483 static GtkButtonBoxStyle
484 gtk_button_box_kludge_get_layout_default (GtkButtonBox *widget)
486 GtkOrientation orientation;
488 orientation = gtk_orientable_get_orientation (GTK_ORIENTABLE (widget));
490 if (orientation == GTK_ORIENTATION_HORIZONTAL)
491 return _gtk_hbutton_box_get_layout_default ();
493 return _gtk_vbutton_box_get_layout_default ();
497 gtk_button_box_size_request (GtkWidget *widget,
498 GtkRequisition *requisition)
506 GtkButtonBoxStyle layout;
507 GtkOrientation orientation;
509 box = GTK_BOX (widget);
510 bbox = GTK_BUTTON_BOX (widget);
512 orientation = gtk_orientable_get_orientation (GTK_ORIENTABLE (widget));
513 spacing = box->spacing;
514 layout = bbox->layout_style != GTK_BUTTONBOX_DEFAULT_STYLE
515 ? bbox->layout_style : gtk_button_box_kludge_get_layout_default (GTK_BUTTON_BOX (widget));
517 _gtk_button_box_child_requisition (widget,
523 if (nvis_children == 0)
525 requisition->width = 0;
526 requisition->height = 0;
532 case GTK_BUTTONBOX_SPREAD:
533 if (orientation == GTK_ORIENTATION_HORIZONTAL)
535 nvis_children*child_width + ((nvis_children+1)*spacing);
537 requisition->height =
538 nvis_children*child_height + ((nvis_children+1)*spacing);
541 case GTK_BUTTONBOX_EDGE:
542 case GTK_BUTTONBOX_START:
543 case GTK_BUTTONBOX_END:
544 case GTK_BUTTONBOX_CENTER:
545 if (orientation == GTK_ORIENTATION_HORIZONTAL)
547 nvis_children*child_width + ((nvis_children-1)*spacing);
549 requisition->height =
550 nvis_children*child_height + ((nvis_children-1)*spacing);
554 g_assert_not_reached ();
558 if (orientation == GTK_ORIENTATION_HORIZONTAL)
559 requisition->height = child_height;
561 requisition->width = child_width;
564 requisition->width += GTK_CONTAINER (box)->border_width * 2;
565 requisition->height += GTK_CONTAINER (box)->border_width * 2;
569 gtk_button_box_size_allocate (GtkWidget *widget,
570 GtkAllocation *allocation)
576 GtkAllocation child_allocation;
583 gint secondary_x = 0;
584 gint secondary_y = 0;
588 gint childspacing = 0;
589 GtkButtonBoxStyle layout;
591 GtkOrientation orientation;
593 orientation = gtk_orientable_get_orientation (GTK_ORIENTABLE (widget));
594 base_box = GTK_BOX (widget);
595 box = GTK_BUTTON_BOX (widget);
596 spacing = base_box->spacing;
597 layout = box->layout_style != GTK_BUTTONBOX_DEFAULT_STYLE
598 ? box->layout_style : gtk_button_box_kludge_get_layout_default (GTK_BUTTON_BOX (widget));
599 _gtk_button_box_child_requisition (widget,
604 widget->allocation = *allocation;
606 if (orientation == GTK_ORIENTATION_HORIZONTAL)
607 width = allocation->width - GTK_CONTAINER (box)->border_width*2;
609 height = allocation->height - GTK_CONTAINER (box)->border_width*2;
613 case GTK_BUTTONBOX_SPREAD:
615 if (orientation == GTK_ORIENTATION_HORIZONTAL)
617 childspacing = (width - (nvis_children * child_width))
618 / (nvis_children + 1);
619 x = allocation->x + GTK_CONTAINER (box)->border_width
621 secondary_x = x + ((nvis_children - n_secondaries)
622 * (child_width + childspacing));
626 childspacing = (height - (nvis_children * child_height))
627 / (nvis_children + 1);
628 y = allocation->y + GTK_CONTAINER (box)->border_width
630 secondary_y = y + ((nvis_children - n_secondaries)
631 * (child_height + childspacing));
636 case GTK_BUTTONBOX_EDGE:
638 if (orientation == GTK_ORIENTATION_HORIZONTAL)
640 if (nvis_children >= 2)
642 childspacing = (width - (nvis_children * child_width))
643 / (nvis_children - 1);
644 x = allocation->x + GTK_CONTAINER (box)->border_width;
645 secondary_x = x + ((nvis_children - n_secondaries)
646 * (child_width + childspacing));
650 /* one or zero children, just center */
651 childspacing = width;
652 x = secondary_x = allocation->x
653 + (allocation->width - child_width) / 2;
658 if (nvis_children >= 2)
660 childspacing = (height - (nvis_children*child_height))
662 y = allocation->y + GTK_CONTAINER (box)->border_width;
663 secondary_y = y + ((nvis_children - n_secondaries)
664 * (child_height + childspacing));
668 /* one or zero children, just center */
669 childspacing = height;
670 y = secondary_y = allocation->y
671 + (allocation->height - child_height) / 2;
677 case GTK_BUTTONBOX_START:
679 if (orientation == GTK_ORIENTATION_HORIZONTAL)
681 childspacing = spacing;
682 x = allocation->x + GTK_CONTAINER (box)->border_width;
683 secondary_x = allocation->x + allocation->width
684 - child_width * n_secondaries
685 - spacing * (n_secondaries - 1)
686 - GTK_CONTAINER (box)->border_width;
690 childspacing = spacing;
691 y = allocation->y + GTK_CONTAINER (box)->border_width;
692 secondary_y = allocation->y + allocation->height
693 - child_height * n_secondaries
694 - spacing * (n_secondaries - 1)
695 - GTK_CONTAINER (box)->border_width;
700 case GTK_BUTTONBOX_END:
702 if (orientation == GTK_ORIENTATION_HORIZONTAL)
704 childspacing = spacing;
705 x = allocation->x + allocation->width
706 - child_width * (nvis_children - n_secondaries)
707 - spacing * (nvis_children - n_secondaries - 1)
708 - GTK_CONTAINER (box)->border_width;
709 secondary_x = allocation->x + GTK_CONTAINER (box)->border_width;
713 childspacing = spacing;
714 y = allocation->y + allocation->height
715 - child_height * (nvis_children - n_secondaries)
716 - spacing * (nvis_children - n_secondaries - 1)
717 - GTK_CONTAINER (box)->border_width;
718 secondary_y = allocation->y + GTK_CONTAINER (box)->border_width;
723 case GTK_BUTTONBOX_CENTER:
725 if (orientation == GTK_ORIENTATION_HORIZONTAL)
727 childspacing = spacing;
730 - (child_width * (nvis_children - n_secondaries)
731 + spacing * (nvis_children - n_secondaries - 1))) / 2
732 + (n_secondaries * child_width + n_secondaries * spacing) / 2;
733 secondary_x = allocation->x + GTK_CONTAINER (box)->border_width;
737 childspacing = spacing;
740 - (child_height * (nvis_children - n_secondaries)
741 + spacing * (nvis_children - n_secondaries - 1))) / 2
742 + (n_secondaries * child_height + n_secondaries * spacing) / 2;
743 secondary_y = allocation->y + GTK_CONTAINER (box)->border_width;
749 g_assert_not_reached ();
753 if (orientation == GTK_ORIENTATION_HORIZONTAL)
755 y = allocation->y + (allocation->height - child_height) / 2;
756 childspace = child_width + childspacing;
760 x = allocation->x + (allocation->width - child_width) / 2;
761 childspace = child_height + childspacing;
764 children = GTK_BOX (box)->children;
768 child = children->data;
769 children = children->next;
771 if (gtk_widget_get_visible (child->widget))
773 child_allocation.width = child_width;
774 child_allocation.height = child_height;
776 if (orientation == GTK_ORIENTATION_HORIZONTAL)
778 child_allocation.y = y;
780 if (child->is_secondary)
782 child_allocation.x = secondary_x;
783 secondary_x += childspace;
787 child_allocation.x = x;
791 if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
792 child_allocation.x = (allocation->x + allocation->width)
793 - (child_allocation.x + child_width - allocation->x);
797 child_allocation.x = x;
799 if (child->is_secondary)
801 child_allocation.y = secondary_y;
802 secondary_y += childspace;
806 child_allocation.y = y;
811 gtk_widget_size_allocate (child->widget, &child_allocation);
817 * gtk_button_box_new:
818 * @orientation: the box' orientation.
820 * Creates a new #GtkButtonBox.
822 * Return value: a new #GtkButtonBox.
827 gtk_button_box_new (GtkOrientation orientation)
829 return g_object_new (GTK_TYPE_BUTTON_BOX,
830 "orientation", orientation,
834 #define __GTK_BUTTON_BOX_C__
835 #include "gtkaliasdef.c"