]> Pileus Git - ~andy/gtk/blob - gtk/gtkbbox.c
Fixes #136082 and #135265, patch by Morten Welinder.
[~andy/gtk] / gtk / gtkbbox.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
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.
8  *
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.
13  *
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.
18  */
19
20 /*
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/. 
25  */
26
27 #include <config.h>
28 #include "gtkbbox.h"
29 #include "gtkintl.h"
30
31 enum {
32   PROP_0,
33   PROP_LAYOUT_STYLE,
34   PROP_LAST
35 };
36
37 enum {
38   CHILD_PROP_0,
39   CHILD_PROP_SECONDARY
40 };
41
42 static void gtk_button_box_class_init         (GtkButtonBoxClass *klass);
43 static void gtk_button_box_init               (GtkButtonBox      *box);
44 static void gtk_button_box_set_property       (GObject           *object,
45                                                guint              prop_id,
46                                                const GValue      *value,
47                                                GParamSpec        *pspec);
48 static void gtk_button_box_get_property       (GObject           *object,
49                                                guint              prop_id,
50                                                GValue            *value,
51                                                GParamSpec        *pspec);
52 static void gtk_button_box_set_child_property (GtkContainer      *container,
53                                                GtkWidget         *child,
54                                                guint              property_id,
55                                                const GValue      *value,
56                                                GParamSpec        *pspec);
57 static void gtk_button_box_get_child_property (GtkContainer      *container,
58                                                GtkWidget         *child,
59                                                guint              property_id,
60                                                GValue            *value,
61                                                GParamSpec        *pspec);
62
63 #define DEFAULT_CHILD_MIN_WIDTH 85
64 #define DEFAULT_CHILD_MIN_HEIGHT 27
65 #define DEFAULT_CHILD_IPAD_X 4
66 #define DEFAULT_CHILD_IPAD_Y 0
67
68 GType
69 gtk_button_box_get_type (void)
70 {
71   static GType button_box_type = 0;
72
73   if (!button_box_type)
74     {
75       static const GTypeInfo button_box_info =
76       {
77         sizeof (GtkButtonBoxClass),
78         NULL,           /* base_init */
79         NULL,           /* base_finalize */
80         (GClassInitFunc) gtk_button_box_class_init,
81         NULL,           /* class_finalize */
82         NULL,           /* class_data */
83         sizeof (GtkButtonBox),
84         0,               /* n_preallocs */
85         (GInstanceInitFunc) gtk_button_box_init,
86         NULL,           /* value_table */
87       };
88
89       button_box_type =
90         g_type_register_static (GTK_TYPE_BOX, "GtkButtonBox",
91                                 &button_box_info, G_TYPE_FLAG_ABSTRACT);
92     }
93
94   return button_box_type;
95 }
96
97 static void
98 gtk_button_box_class_init (GtkButtonBoxClass *class)
99 {
100   GtkWidgetClass *widget_class;
101   GObjectClass *gobject_class;
102   GtkContainerClass *container_class;
103
104   gobject_class = G_OBJECT_CLASS (class);
105   widget_class = (GtkWidgetClass*) class;
106   container_class = (GtkContainerClass*) class;
107
108   gobject_class->set_property = gtk_button_box_set_property;
109   gobject_class->get_property = gtk_button_box_get_property;
110
111   container_class->set_child_property = gtk_button_box_set_child_property;
112   container_class->get_child_property = gtk_button_box_get_child_property;
113   
114   /* FIXME we need to override the "spacing" property on GtkBox once
115    * libgobject allows that.
116    */
117
118   gtk_widget_class_install_style_property (widget_class,
119                                            g_param_spec_int ("child_min_width",
120                                                              P_("Minimum child width"),
121                                                              P_("Minimum width of buttons inside the box"),
122                                                              0,
123                                                              G_MAXINT,
124                                                              DEFAULT_CHILD_MIN_WIDTH,
125                                                              G_PARAM_READABLE));
126
127   gtk_widget_class_install_style_property (widget_class,
128                                            g_param_spec_int ("child_min_height",
129                                                              P_("Minimum child height"),
130                                                              P_("Minimum height of buttons inside the box"),
131                                                              0,
132                                                              G_MAXINT,
133                                                              DEFAULT_CHILD_MIN_HEIGHT,
134                                                              G_PARAM_READABLE));
135
136   gtk_widget_class_install_style_property (widget_class,
137                                            g_param_spec_int ("child_internal_pad_x",
138                                                              P_("Child internal width padding"),
139                                                              P_("Amount to increase child's size on either side"),
140                                                              0,
141                                                              G_MAXINT,
142                                                              DEFAULT_CHILD_IPAD_X,
143                                                              G_PARAM_READABLE));
144
145   gtk_widget_class_install_style_property (widget_class,
146                                            g_param_spec_int ("child_internal_pad_y",
147                                                              P_("Child internal height padding"),
148                                                              P_("Amount to increase child's size on the top and bottom"),
149                                                              0,
150                                                              G_MAXINT,
151                                                              DEFAULT_CHILD_IPAD_Y,
152                                                              G_PARAM_READABLE));
153   g_object_class_install_property (gobject_class,
154                                    PROP_LAYOUT_STYLE,
155                                    g_param_spec_enum ("layout_style",
156                                                       P_("Layout style"),
157                                                       P_("How to layout the buttons in the box. Possible values are default, spread, edge, start and end"),
158                                                       GTK_TYPE_BUTTON_BOX_STYLE,
159                                                       GTK_BUTTONBOX_DEFAULT_STYLE,
160                                                       G_PARAM_READWRITE));
161
162   gtk_container_class_install_child_property (container_class,
163                                               CHILD_PROP_SECONDARY,
164                                               g_param_spec_boolean ("secondary", 
165                                                                     P_("Secondary"),
166                                                                     P_("If TRUE, the child appears in a secondary group of children, suitable for, e.g., help buttons"),
167                                                                     FALSE,
168                                                                     G_PARAM_READWRITE));
169 }
170
171 static void
172 gtk_button_box_init (GtkButtonBox *button_box)
173 {
174   GTK_BOX (button_box)->spacing = 0;
175   button_box->child_min_width = GTK_BUTTONBOX_DEFAULT;
176   button_box->child_min_height = GTK_BUTTONBOX_DEFAULT;
177   button_box->child_ipad_x = GTK_BUTTONBOX_DEFAULT;
178   button_box->child_ipad_y = GTK_BUTTONBOX_DEFAULT;
179   button_box->layout_style = GTK_BUTTONBOX_DEFAULT_STYLE;
180 }
181
182 static void
183 gtk_button_box_set_property (GObject         *object,
184                              guint            prop_id,
185                              const GValue    *value,
186                              GParamSpec      *pspec)
187 {
188   switch (prop_id) 
189     {
190     case PROP_LAYOUT_STYLE:
191       gtk_button_box_set_layout (GTK_BUTTON_BOX (object),
192                                  g_value_get_enum (value));
193       break;
194     default:
195       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
196       break;
197     }
198 }
199
200 static void
201 gtk_button_box_get_property (GObject         *object,
202                              guint            prop_id,
203                              GValue          *value,
204                              GParamSpec      *pspec)
205 {
206   switch (prop_id)
207     {
208     case PROP_LAYOUT_STYLE:
209       g_value_set_enum (value, GTK_BUTTON_BOX (object)->layout_style);
210       break;
211     default:
212       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
213       break;
214     }
215 }
216
217 static void
218 gtk_button_box_set_child_property (GtkContainer    *container,
219                                    GtkWidget       *child,
220                                    guint            property_id,
221                                    const GValue    *value,
222                                    GParamSpec      *pspec)
223 {
224   switch (property_id)
225     {
226     case CHILD_PROP_SECONDARY:
227       gtk_button_box_set_child_secondary (GTK_BUTTON_BOX (container), child,
228                                           g_value_get_boolean (value));
229       break;
230     default:
231       GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
232       break;
233     }
234 }
235
236 static void
237 gtk_button_box_get_child_property (GtkContainer *container,
238                                    GtkWidget    *child,
239                                    guint         property_id,
240                                    GValue       *value,
241                                    GParamSpec   *pspec)
242 {
243   switch (property_id)
244     {
245     case CHILD_PROP_SECONDARY:
246       g_value_set_boolean (value, 
247                            gtk_button_box_get_child_secondary (GTK_BUTTON_BOX (container), 
248                                                                child));
249       break;
250     default:
251       GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
252       break;
253     }
254 }
255
256 /* set per widget values for spacing, child size and child internal padding */
257
258 void 
259 gtk_button_box_set_child_size (GtkButtonBox *widget, 
260                                gint width, gint height)
261 {
262   g_return_if_fail (GTK_IS_BUTTON_BOX (widget));
263
264   widget->child_min_width = width;
265   widget->child_min_height = height;
266 }
267
268 void 
269 gtk_button_box_set_child_ipadding (GtkButtonBox *widget,
270                                    gint ipad_x, gint ipad_y)
271 {
272   g_return_if_fail (GTK_IS_BUTTON_BOX (widget));
273
274   widget->child_ipad_x = ipad_x;
275   widget->child_ipad_y = ipad_y;
276 }
277
278 void
279 gtk_button_box_set_layout (GtkButtonBox      *widget, 
280                            GtkButtonBoxStyle  layout_style)
281 {
282   g_return_if_fail (GTK_IS_BUTTON_BOX (widget));
283   g_return_if_fail (layout_style >= GTK_BUTTONBOX_DEFAULT_STYLE &&
284                     layout_style <= GTK_BUTTONBOX_END);
285
286   if (widget->layout_style != layout_style)
287     {
288       widget->layout_style = layout_style;
289       g_object_notify (G_OBJECT (widget), "layout_style");
290       gtk_widget_queue_resize (GTK_WIDGET (widget));
291     }
292 }
293
294
295 /* get per widget values for spacing, child size and child internal padding */
296
297 void 
298 gtk_button_box_get_child_size (GtkButtonBox *widget,
299                                gint *width, gint *height)
300 {
301   g_return_if_fail (GTK_IS_BUTTON_BOX (widget));
302   g_return_if_fail (width != NULL);
303   g_return_if_fail (height != NULL);
304
305   *width  = widget->child_min_width;
306   *height = widget->child_min_height;
307 }
308
309 void
310 gtk_button_box_get_child_ipadding (GtkButtonBox *widget,
311                                    gint* ipad_x, gint *ipad_y)
312 {
313   g_return_if_fail (GTK_IS_BUTTON_BOX (widget));
314   g_return_if_fail (ipad_x != NULL);
315   g_return_if_fail (ipad_y != NULL);
316
317   *ipad_x = widget->child_ipad_x;
318   *ipad_y = widget->child_ipad_y;
319 }
320
321 GtkButtonBoxStyle 
322 gtk_button_box_get_layout (GtkButtonBox *widget)
323 {
324   g_return_val_if_fail (GTK_IS_BUTTON_BOX (widget), GTK_BUTTONBOX_SPREAD);
325   
326   return widget->layout_style;
327 }
328
329 /**
330  * gtk_button_box_get_child_secondary:
331  * @widget: a #GtkButtonBox
332  * @child: a child of @widget 
333  * 
334  * Returns whether @child should appear in a secondary group of children.
335  *
336  * Return value: whether @child should appear in a secondary group of children.
337  *
338  * Since: 2.4
339  **/
340 gboolean 
341 gtk_button_box_get_child_secondary (GtkButtonBox *widget,
342                                     GtkWidget    *child)
343 {
344   GList *list;
345   GtkBoxChild *child_info;
346
347   g_return_val_if_fail (GTK_IS_BUTTON_BOX (widget), FALSE);
348   g_return_val_if_fail (GTK_IS_WIDGET (child), FALSE);
349
350   child_info = NULL;
351   list = GTK_BOX (widget)->children;
352   while (list)
353     {
354       child_info = list->data;
355       if (child_info->widget == child)
356         break;
357
358       list = list->next;
359     }
360
361   g_return_val_if_fail (list != NULL, FALSE);
362
363   return child_info->is_secondary;
364 }
365
366 /**
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
371  *                button box.
372  *
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.
375  *
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 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.
385  **/
386 void 
387 gtk_button_box_set_child_secondary (GtkButtonBox *widget, 
388                                     GtkWidget    *child,
389                                     gboolean      is_secondary)
390 {
391   GList *list;
392   
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));
396
397   list = GTK_BOX (widget)->children;
398   while (list)
399     {
400       GtkBoxChild *child_info = list->data;
401       if (child_info->widget == child)
402         {
403           child_info->is_secondary = is_secondary;
404           break;
405         }
406
407       list = list->next;
408     }
409
410   gtk_widget_child_notify (child, "secondary");
411
412   if (GTK_WIDGET_VISIBLE (widget) && GTK_WIDGET_VISIBLE (child))
413     gtk_widget_queue_resize (child);
414 }
415
416 /* Ask children how much space they require and round up 
417    to match minimum size and internal padding.
418    Returns the size each single child should have. */
419 void
420 _gtk_button_box_child_requisition (GtkWidget *widget,
421                                    int       *nvis_children,
422                                    int       *nvis_secondaries,
423                                    int       *width,
424                                    int       *height)
425 {
426   GtkButtonBox *bbox;
427   GtkBoxChild *child;
428   GList *children;
429   gint nchildren;
430   gint nsecondaries;
431   gint needed_width;
432   gint needed_height;
433   GtkRequisition child_requisition;
434   gint ipad_w;
435   gint ipad_h;
436   gint width_default;
437   gint height_default;
438   gint ipad_x_default;
439   gint ipad_y_default;
440   
441   gint child_min_width;
442   gint child_min_height;
443   gint ipad_x;
444   gint ipad_y;
445   
446   g_return_if_fail (GTK_IS_BUTTON_BOX (widget));
447
448   bbox = GTK_BUTTON_BOX (widget);
449
450   gtk_widget_style_get (widget,
451                         "child_min_width",
452                         &width_default,
453                         "child_min_height",
454                         &height_default,
455                         "child_internal_pad_x",
456                         &ipad_x_default,
457                         "child_internal_pad_y",
458                         &ipad_y_default, NULL);
459   
460   child_min_width = bbox->child_min_width   != GTK_BUTTONBOX_DEFAULT
461           ? bbox->child_min_width : width_default;
462   child_min_height = bbox->child_min_height !=GTK_BUTTONBOX_DEFAULT
463           ? bbox->child_min_height : height_default;
464   ipad_x = bbox->child_ipad_x != GTK_BUTTONBOX_DEFAULT
465           ? bbox->child_ipad_x : ipad_x_default;
466   ipad_y = bbox->child_ipad_y != GTK_BUTTONBOX_DEFAULT
467           ? bbox->child_ipad_y : ipad_y_default;
468
469   nchildren = 0;
470   nsecondaries = 0;
471   children = GTK_BOX(bbox)->children;
472   needed_width = child_min_width;
473   needed_height = child_min_height;  
474   ipad_w = ipad_x * 2;
475   ipad_h = ipad_y * 2;
476   
477   while (children)
478     {
479       child = children->data;
480       children = children->next;
481
482       if (GTK_WIDGET_VISIBLE (child->widget))
483         {
484           nchildren += 1;
485           gtk_widget_size_request (child->widget, &child_requisition);
486           
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)
492             nsecondaries++;
493         }
494     }
495
496   if (nvis_children)
497     *nvis_children = nchildren;
498   if (nvis_secondaries)
499     *nvis_secondaries = nsecondaries;
500   if (width)
501     *width = needed_width;
502   if (height)
503     *height = needed_height;
504 }