]> Pileus Git - ~andy/gtk/blob - gtk/gtksizegroup.c
1669fdea723b193bdf789207aac9977f290f0179
[~andy/gtk] / gtk / gtksizegroup.c
1 /* GTK - The GIMP Toolkit
2  * gtksizegroup.c: 
3  * Copyright (C) 2001 Red Hat Software
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library. If not, see <http://www.gnu.org/licenses/>.
17  */
18
19 #include "config.h"
20 #include <string.h>
21
22 #include "gtkbuildable.h"
23 #include "gtkcontainer.h"
24 #include "gtkintl.h"
25 #include "gtktypebuiltins.h"
26 #include "gtkprivate.h"
27 #include "gtksizegroup-private.h"
28 #include "gtkwidgetprivate.h"
29 #include "gtkcontainerprivate.h"
30
31
32 /**
33  * SECTION:gtksizegroup
34  * @Short_description: Grouping widgets so they request the same size
35  * @Title: GtkSizeGroup
36  *
37  * #GtkSizeGroup provides a mechanism for grouping a number of widgets
38  * together so they all request the same amount of space.  This is
39  * typically useful when you want a column of widgets to have the same
40  * size, but you can't use a #GtkGrid widget.
41  *
42  * In detail, the size requested for each widget in a #GtkSizeGroup is
43  * the maximum of the sizes that would have been requested for each
44  * widget in the size group if they were not in the size group. The mode
45  * of the size group (see gtk_size_group_set_mode()) determines whether
46  * this applies to the horizontal size, the vertical size, or both sizes.
47  *
48  * Note that size groups only affect the amount of space requested, not
49  * the size that the widgets finally receive. If you want the widgets in
50  * a #GtkSizeGroup to actually be the same size, you need to pack them in
51  * such a way that they get the size they request and not more. For
52  * example, if you are packing your widgets into a table, you would not
53  * include the %GTK_FILL flag.
54  *
55  * #GtkSizeGroup objects are referenced by each widget in the size group,
56  * so once you have added all widgets to a #GtkSizeGroup, you can drop
57  * the initial reference to the size group with g_object_unref(). If the
58  * widgets in the size group are subsequently destroyed, then they will
59  * be removed from the size group and drop their references on the size
60  * group; when all widgets have been removed, the size group will be
61  * freed.
62  *
63  * Widgets can be part of multiple size groups; GTK+ will compute the
64  * horizontal size of a widget from the horizontal requisition of all
65  * widgets that can be reached from the widget by a chain of size groups
66  * of type %GTK_SIZE_GROUP_HORIZONTAL or %GTK_SIZE_GROUP_BOTH, and the
67  * vertical size from the vertical requisition of all widgets that can be
68  * reached from the widget by a chain of size groups of type
69  * %GTK_SIZE_GROUP_VERTICAL or %GTK_SIZE_GROUP_BOTH.
70  *
71  * Note that only non-contextual sizes of every widget are ever consulted
72  * by size groups (since size groups have no knowledge of what size a widget
73  * will be allocated in one dimension, it cannot derive how much height
74  * a widget will receive for a given width). When grouping widgets that
75  * trade height for width in mode %GTK_SIZE_GROUP_VERTICAL or %GTK_SIZE_GROUP_BOTH:
76  * the height for the minimum width will be the requested height for all
77  * widgets in the group. The same is of course true when horizontally grouping
78  * width for height widgets.
79  *
80  * Widgets that trade height-for-width should set a reasonably large minimum width
81  * by way of #GtkLabel:width-chars for instance. Widgets with static sizes as well
82  * as widgets that grow (such as ellipsizing text) need no such considerations.
83  *
84  * <refsect2 id="GtkSizeGroup-BUILDER-UI">
85  * <title>GtkSizeGroup as GtkBuildable</title>
86  * <para>
87  * Size groups can be specified in a UI definition by placing an
88  * &lt;object&gt; element with <literal>class="GtkSizeGroup"</literal>
89  * somewhere in the UI definition. The widgets that belong to the
90  * size group are specified by a &lt;widgets&gt; element that may
91  * contain multiple &lt;widget&gt; elements, one for each member
92  * of the size group. The name attribute gives the id of the widget.
93  *
94  * <example>
95  * <title>A UI definition fragment with GtkSizeGroup</title>
96  * <programlisting><![CDATA[
97  * <object class="GtkSizeGroup">
98  *   <property name="mode">GTK_SIZE_GROUP_HORIZONTAL</property>
99  *   <widgets>
100  *     <widget name="radio1"/>
101  *     <widget name="radio2"/>
102  *   </widgets>
103  * </object>
104  * ]]></programlisting>
105  * </example>
106  * </para>
107  * </refsect2>
108  */
109
110
111 struct _GtkSizeGroupPrivate
112 {
113   GSList         *widgets;
114
115   guint8          mode;
116
117   guint           ignore_hidden : 1;
118   guint           visited       : 1;
119 };
120
121 enum {
122   PROP_0,
123   PROP_MODE,
124   PROP_IGNORE_HIDDEN
125 };
126
127 static void gtk_size_group_set_property (GObject      *object,
128                                          guint         prop_id,
129                                          const GValue *value,
130                                          GParamSpec   *pspec);
131 static void gtk_size_group_get_property (GObject      *object,
132                                          guint         prop_id,
133                                          GValue       *value,
134                                          GParamSpec   *pspec);
135
136 static void add_group_to_closure  (GtkSizeGroup      *group,
137                                    GtkSizeGroupMode   mode,
138                                    GSList           **groups,
139                                    GSList           **widgets);
140 static void add_widget_to_closure (GtkWidget         *widget,
141                                    GtkSizeGroupMode   mode,
142                                    GSList           **groups,
143                                    GSList           **widgets);
144
145 /* GtkBuildable */
146 static void gtk_size_group_buildable_init (GtkBuildableIface *iface);
147 static gboolean gtk_size_group_buildable_custom_tag_start (GtkBuildable  *buildable,
148                                                            GtkBuilder    *builder,
149                                                            GObject       *child,
150                                                            const gchar   *tagname,
151                                                            GMarkupParser *parser,
152                                                            gpointer      *data);
153 static void gtk_size_group_buildable_custom_finished (GtkBuildable  *buildable,
154                                                       GtkBuilder    *builder,
155                                                       GObject       *child,
156                                                       const gchar   *tagname,
157                                                       gpointer       user_data);
158
159 static void
160 mark_group_unvisited (GtkSizeGroup *group)
161 {
162   group->priv->visited = FALSE;
163 }
164
165 static void
166 mark_widget_unvisited (GtkWidget *widget)
167 {
168   _gtk_widget_set_sizegroup_visited (widget, FALSE);
169 }
170
171 static void
172 add_group_to_closure (GtkSizeGroup    *group,
173                       GtkSizeGroupMode mode,
174                       GSList         **groups,
175                       GSList         **widgets)
176 {
177   GtkSizeGroupPrivate *priv = group->priv;
178   GSList *tmp_widgets;
179   
180   if (priv->visited)
181     return;
182
183   *groups = g_slist_prepend (*groups, group);
184   priv->visited = TRUE;
185
186   tmp_widgets = priv->widgets;
187   while (tmp_widgets)
188     {
189       GtkWidget *tmp_widget = tmp_widgets->data;
190       
191       add_widget_to_closure (tmp_widget, mode, groups, widgets);
192       
193       tmp_widgets = tmp_widgets->next;
194     }
195 }
196
197 static void
198 add_widget_to_closure (GtkWidget       *widget,
199                        GtkSizeGroupMode mode,
200                        GSList         **groups,
201                        GSList         **widgets)
202 {
203   GSList *tmp_groups;
204
205   if (_gtk_widget_get_sizegroup_visited (widget))
206     return;
207
208   *widgets = g_slist_prepend (*widgets, widget);
209   _gtk_widget_set_sizegroup_visited (widget, TRUE);
210
211   tmp_groups = _gtk_widget_get_sizegroups (widget);
212   while (tmp_groups)
213     {
214       GtkSizeGroup        *tmp_group = tmp_groups->data;
215       GtkSizeGroupPrivate *tmp_priv  = tmp_group->priv;
216
217       if (tmp_priv->mode == GTK_SIZE_GROUP_BOTH || tmp_priv->mode == mode)
218         add_group_to_closure (tmp_group, mode, groups, widgets);
219
220       tmp_groups = tmp_groups->next;
221     }
222 }
223
224 static void
225 real_queue_resize (GtkWidget          *widget,
226                    GtkQueueResizeFlags flags)
227 {
228   GtkWidget *container;
229
230   _gtk_widget_set_alloc_needed (widget, TRUE);
231   _gtk_widget_set_width_request_needed (widget, TRUE);
232   _gtk_widget_set_height_request_needed (widget, TRUE);
233
234   container = gtk_widget_get_parent (widget);
235   if (!container &&
236       gtk_widget_is_toplevel (widget) && GTK_IS_CONTAINER (widget))
237     container = widget;
238
239   if (container)
240     {
241       if (flags & GTK_QUEUE_RESIZE_INVALIDATE_ONLY)
242         _gtk_container_resize_invalidate (GTK_CONTAINER (container));
243       else
244         _gtk_container_queue_resize (GTK_CONTAINER (container));
245     }
246 }
247
248 static void
249 queue_resize_on_widget (GtkWidget          *widget,
250                         gboolean            check_siblings,
251                         GtkQueueResizeFlags flags)
252 {
253   GtkWidget *parent = widget;
254   GSList *tmp_list;
255
256   while (parent)
257     {
258       GSList *widget_groups;
259       GSList *groups;
260       GSList *widgets;
261       
262       if (widget == parent && !check_siblings)
263         {
264           real_queue_resize (widget, flags);
265           parent = gtk_widget_get_parent (parent);
266           continue;
267         }
268       
269       widget_groups = _gtk_widget_get_sizegroups (parent);
270       if (!widget_groups)
271         {
272           if (widget == parent)
273             real_queue_resize (widget, flags);
274
275           parent = gtk_widget_get_parent (parent);
276           continue;
277         }
278
279       groups = NULL;
280       widgets = NULL;
281           
282       add_widget_to_closure (parent, GTK_SIZE_GROUP_HORIZONTAL, &groups, &widgets);
283       g_slist_foreach (widgets, (GFunc)mark_widget_unvisited, NULL);
284       g_slist_foreach (groups, (GFunc)mark_group_unvisited, NULL);
285
286       tmp_list = widgets;
287       while (tmp_list)
288         {
289           if (tmp_list->data == parent)
290             {
291               if (widget == parent)
292                 real_queue_resize (parent, flags);
293             }
294           else if (tmp_list->data == widget)
295             {
296               g_warning ("A container and its child are part of this SizeGroup");
297             }
298           else
299             queue_resize_on_widget (tmp_list->data, FALSE, flags);
300
301           tmp_list = tmp_list->next;
302         }
303       
304       g_slist_free (widgets);
305       g_slist_free (groups);
306               
307       groups = NULL;
308       widgets = NULL;
309               
310       add_widget_to_closure (parent, GTK_SIZE_GROUP_VERTICAL, &groups, &widgets);
311       g_slist_foreach (widgets, (GFunc)mark_widget_unvisited, NULL);
312       g_slist_foreach (groups, (GFunc)mark_group_unvisited, NULL);
313
314       tmp_list = widgets;
315       while (tmp_list)
316         {
317           if (tmp_list->data == parent)
318             {
319               if (widget == parent)
320                 real_queue_resize (parent, flags);
321             }
322           else if (tmp_list->data == widget)
323             {
324               g_warning ("A container and its child are part of this SizeGroup");
325             }
326           else
327             queue_resize_on_widget (tmp_list->data, FALSE, flags);
328
329           tmp_list = tmp_list->next;
330         }
331       
332       g_slist_free (widgets);
333       g_slist_free (groups);
334
335       parent = gtk_widget_get_parent (parent);
336     }
337 }
338
339 static void
340 queue_resize_on_group (GtkSizeGroup       *size_group)
341 {
342   GtkSizeGroupPrivate *priv = size_group->priv;
343
344   if (priv->widgets)
345     queue_resize_on_widget (priv->widgets->data, TRUE, 0);
346 }
347
348 static void
349 gtk_size_group_class_init (GtkSizeGroupClass *klass)
350 {
351   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
352
353   gobject_class->set_property = gtk_size_group_set_property;
354   gobject_class->get_property = gtk_size_group_get_property;
355   
356   g_object_class_install_property (gobject_class,
357                                    PROP_MODE,
358                                    g_param_spec_enum ("mode",
359                                                       P_("Mode"),
360                                                       P_("The directions in which the size group affects the requested sizes"
361                                                         " of its component widgets"),
362                                                       GTK_TYPE_SIZE_GROUP_MODE,
363                                                       GTK_SIZE_GROUP_HORIZONTAL,                                                      GTK_PARAM_READWRITE));
364
365   /**
366    * GtkSizeGroup:ignore-hidden:
367    *
368    * If %TRUE, unmapped widgets are ignored when determining 
369    * the size of the group.
370    *
371    * Since: 2.8
372    */
373   g_object_class_install_property (gobject_class,
374                                    PROP_IGNORE_HIDDEN,
375                                    g_param_spec_boolean ("ignore-hidden",
376                                                          P_("Ignore hidden"),
377                                                          P_("If TRUE, unmapped widgets are ignored "
378                                                             "when determining the size of the group"),
379                                                          FALSE,
380                                                          GTK_PARAM_READWRITE));
381
382   g_type_class_add_private (klass, sizeof (GtkSizeGroupPrivate));
383 }
384
385 static void
386 gtk_size_group_init (GtkSizeGroup *size_group)
387 {
388   GtkSizeGroupPrivate *priv;
389
390   size_group->priv = G_TYPE_INSTANCE_GET_PRIVATE (size_group,
391                                                   GTK_TYPE_SIZE_GROUP,
392                                                   GtkSizeGroupPrivate);
393   priv = size_group->priv;
394
395   priv->widgets = NULL;
396   priv->mode = GTK_SIZE_GROUP_HORIZONTAL;
397   priv->ignore_hidden = FALSE;
398   priv->visited  = FALSE;
399 }
400
401 static void
402 gtk_size_group_buildable_init (GtkBuildableIface *iface)
403 {
404   iface->custom_tag_start = gtk_size_group_buildable_custom_tag_start;
405   iface->custom_finished = gtk_size_group_buildable_custom_finished;
406 }
407
408 G_DEFINE_TYPE_WITH_CODE (GtkSizeGroup, gtk_size_group, G_TYPE_OBJECT,
409                          G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
410                                                 gtk_size_group_buildable_init))
411
412 static void
413 gtk_size_group_set_property (GObject      *object,
414                              guint         prop_id,
415                              const GValue *value,
416                              GParamSpec   *pspec)
417 {
418   GtkSizeGroup *size_group = GTK_SIZE_GROUP (object);
419
420   switch (prop_id)
421     {
422     case PROP_MODE:
423       gtk_size_group_set_mode (size_group, g_value_get_enum (value));
424       break;
425     case PROP_IGNORE_HIDDEN:
426       gtk_size_group_set_ignore_hidden (size_group, g_value_get_boolean (value));
427       break;
428     default:
429       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
430       break;
431     }
432 }
433
434 static void
435 gtk_size_group_get_property (GObject      *object,
436                              guint         prop_id,
437                              GValue       *value,
438                              GParamSpec   *pspec)
439 {
440   GtkSizeGroup *size_group = GTK_SIZE_GROUP (object);
441   GtkSizeGroupPrivate *priv = size_group->priv;
442
443   switch (prop_id)
444     {
445     case PROP_MODE:
446       g_value_set_enum (value, priv->mode);
447       break;
448     case PROP_IGNORE_HIDDEN:
449       g_value_set_boolean (value, priv->ignore_hidden);
450       break;
451     default:
452       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
453       break;
454     }
455 }
456
457 /**
458  * gtk_size_group_new:
459  * @mode: the mode for the new size group.
460  * 
461  * Create a new #GtkSizeGroup.
462  
463  * Return value: a newly created #GtkSizeGroup
464  **/
465 GtkSizeGroup *
466 gtk_size_group_new (GtkSizeGroupMode mode)
467 {
468   GtkSizeGroup *size_group = g_object_new (GTK_TYPE_SIZE_GROUP, NULL);
469   GtkSizeGroupPrivate *priv = size_group->priv;
470
471   priv->mode = mode;
472
473   return size_group;
474 }
475
476 /**
477  * gtk_size_group_set_mode:
478  * @size_group: a #GtkSizeGroup
479  * @mode: the mode to set for the size group.
480  * 
481  * Sets the #GtkSizeGroupMode of the size group. The mode of the size
482  * group determines whether the widgets in the size group should
483  * all have the same horizontal requisition (%GTK_SIZE_GROUP_HORIZONTAL)
484  * all have the same vertical requisition (%GTK_SIZE_GROUP_VERTICAL),
485  * or should all have the same requisition in both directions
486  * (%GTK_SIZE_GROUP_BOTH).
487  **/
488 void
489 gtk_size_group_set_mode (GtkSizeGroup     *size_group,
490                          GtkSizeGroupMode  mode)
491 {
492   GtkSizeGroupPrivate *priv;
493
494   g_return_if_fail (GTK_IS_SIZE_GROUP (size_group));
495
496   priv = size_group->priv;
497
498   if (priv->mode != mode)
499     {
500       if (priv->mode != GTK_SIZE_GROUP_NONE)
501         queue_resize_on_group (size_group);
502       priv->mode = mode;
503       if (priv->mode != GTK_SIZE_GROUP_NONE)
504         queue_resize_on_group (size_group);
505
506       g_object_notify (G_OBJECT (size_group), "mode");
507     }
508 }
509
510 /**
511  * gtk_size_group_get_mode:
512  * @size_group: a #GtkSizeGroup
513  * 
514  * Gets the current mode of the size group. See gtk_size_group_set_mode().
515  * 
516  * Return value: the current mode of the size group.
517  **/
518 GtkSizeGroupMode
519 gtk_size_group_get_mode (GtkSizeGroup *size_group)
520 {
521   g_return_val_if_fail (GTK_IS_SIZE_GROUP (size_group), GTK_SIZE_GROUP_BOTH);
522
523   return size_group->priv->mode;
524 }
525
526 /**
527  * gtk_size_group_set_ignore_hidden:
528  * @size_group: a #GtkSizeGroup
529  * @ignore_hidden: whether unmapped widgets should be ignored
530  *   when calculating the size
531  * 
532  * Sets whether unmapped widgets should be ignored when
533  * calculating the size.
534  *
535  * Since: 2.8 
536  */
537 void
538 gtk_size_group_set_ignore_hidden (GtkSizeGroup *size_group,
539                                   gboolean      ignore_hidden)
540 {
541   GtkSizeGroupPrivate *priv;
542
543   g_return_if_fail (GTK_IS_SIZE_GROUP (size_group));
544
545   priv = size_group->priv;
546
547   ignore_hidden = ignore_hidden != FALSE;
548
549   if (priv->ignore_hidden != ignore_hidden)
550     {
551       priv->ignore_hidden = ignore_hidden;
552
553       g_object_notify (G_OBJECT (size_group), "ignore-hidden");
554     }
555 }
556
557 /**
558  * gtk_size_group_get_ignore_hidden:
559  * @size_group: a #GtkSizeGroup
560  *
561  * Returns if invisible widgets are ignored when calculating the size.
562  *
563  * Returns: %TRUE if invisible widgets are ignored.
564  *
565  * Since: 2.8
566  */
567 gboolean
568 gtk_size_group_get_ignore_hidden (GtkSizeGroup *size_group)
569 {
570   g_return_val_if_fail (GTK_IS_SIZE_GROUP (size_group), FALSE);
571
572   return size_group->priv->ignore_hidden;
573 }
574
575 static void
576 gtk_size_group_widget_destroyed (GtkWidget    *widget,
577                                  GtkSizeGroup *size_group)
578 {
579   gtk_size_group_remove_widget (size_group, widget);
580 }
581
582 /**
583  * gtk_size_group_add_widget:
584  * @size_group: a #GtkSizeGroup
585  * @widget: the #GtkWidget to add
586  * 
587  * Adds a widget to a #GtkSizeGroup. In the future, the requisition
588  * of the widget will be determined as the maximum of its requisition
589  * and the requisition of the other widgets in the size group.
590  * Whether this applies horizontally, vertically, or in both directions
591  * depends on the mode of the size group. See gtk_size_group_set_mode().
592  *
593  * When the widget is destroyed or no longer referenced elsewhere, it will 
594  * be removed from the size group.
595  */
596 void
597 gtk_size_group_add_widget (GtkSizeGroup     *size_group,
598                            GtkWidget        *widget)
599 {
600   GtkSizeGroupPrivate *priv;
601   GSList *groups;
602   
603   g_return_if_fail (GTK_IS_SIZE_GROUP (size_group));
604   g_return_if_fail (GTK_IS_WIDGET (widget));
605
606   priv = size_group->priv;
607
608   groups = _gtk_widget_get_sizegroups (widget);
609
610   if (!g_slist_find (groups, size_group))
611     {
612       _gtk_widget_add_sizegroup (widget, size_group);
613
614       priv->widgets = g_slist_prepend (priv->widgets, widget);
615
616       g_signal_connect (widget, "destroy",
617                         G_CALLBACK (gtk_size_group_widget_destroyed),
618                         size_group);
619
620       g_object_ref (size_group);
621     }
622   
623   queue_resize_on_group (size_group);
624 }
625
626 /**
627  * gtk_size_group_remove_widget:
628  * @size_group: a #GtkSizeGroup
629  * @widget: the #GtkWidget to remove
630  * 
631  * Removes a widget from a #GtkSizeGroup.
632  **/
633 void
634 gtk_size_group_remove_widget (GtkSizeGroup *size_group,
635                               GtkWidget    *widget)
636 {
637   GtkSizeGroupPrivate *priv;
638   
639   g_return_if_fail (GTK_IS_SIZE_GROUP (size_group));
640   g_return_if_fail (GTK_IS_WIDGET (widget));
641
642   priv = size_group->priv;
643
644   g_return_if_fail (g_slist_find (priv->widgets, widget));
645
646   g_signal_handlers_disconnect_by_func (widget,
647                                         gtk_size_group_widget_destroyed,
648                                         size_group);
649   
650   _gtk_widget_remove_sizegroup (widget, size_group);
651
652   priv->widgets = g_slist_remove (priv->widgets, widget);
653   queue_resize_on_group (size_group);
654   gtk_widget_queue_resize (widget);
655
656   g_object_unref (size_group);
657 }
658
659 /**
660  * gtk_size_group_get_widgets:
661  * @size_group: a #GtkSizeGroup
662  * 
663  * Returns the list of widgets associated with @size_group.
664  *
665  * Return value:  (element-type GtkWidget) (transfer none): a #GSList of
666  *   widgets. The list is owned by GTK+ and should not be modified.
667  *
668  * Since: 2.10
669  **/
670 GSList *
671 gtk_size_group_get_widgets (GtkSizeGroup *size_group)
672 {
673   return size_group->priv->widgets;
674 }
675
676 static void
677 compute_dimension (GtkWidget        *widget,
678                    GtkSizeGroupMode  mode,
679                    gint             *minimum, /* in-out */
680                    gint             *natural) /* in-out */
681 {
682   GSList *widgets = NULL;
683   GSList *groups = NULL;
684   GSList *tmp_list;
685   gint    min_result = 0, nat_result = 0;
686
687   add_widget_to_closure (widget, mode, &groups, &widgets);
688   g_slist_foreach (widgets, (GFunc)mark_widget_unvisited, NULL);
689   g_slist_foreach (groups, (GFunc)mark_group_unvisited, NULL);
690
691   g_slist_foreach (widgets, (GFunc)g_object_ref, NULL);
692   
693   if (!groups)
694     {
695       min_result = *minimum;
696       nat_result = *natural;
697     }
698   else
699     {
700       GtkSizeGroup *group = groups->data;
701       GtkSizeGroupPrivate *priv = group->priv;
702
703       tmp_list = widgets;
704       while (tmp_list)
705         {
706           GtkWidget *tmp_widget = tmp_list->data;
707           gint min_dimension, nat_dimension;
708
709           if (tmp_widget == widget)
710             {
711               min_dimension = *minimum;
712               nat_dimension = *natural;
713             }
714           else if (!gtk_widget_get_mapped (tmp_widget) && priv->ignore_hidden)
715             {
716               min_dimension = 0;
717               nat_dimension = 0;
718             }
719           else
720             {
721               if (mode == GTK_SIZE_GROUP_HORIZONTAL)
722                 gtk_widget_get_preferred_width (tmp_widget, &min_dimension, &nat_dimension);
723               else
724                 gtk_widget_get_preferred_height (tmp_widget, &min_dimension, &nat_dimension);
725             }
726
727           min_result = MAX (min_result, min_dimension);
728           nat_result = MAX (nat_result, nat_dimension);
729
730           tmp_list = tmp_list->next;
731         }
732     }
733
734   g_slist_foreach (widgets, (GFunc)g_object_unref, NULL);
735
736   g_slist_free (widgets);
737   g_slist_free (groups);
738
739   *minimum = min_result;
740   *natural = nat_result;
741 }
742
743 /**
744  * _gtk_size_group_bump_requisition:
745  * @widget: a #GtkWidget
746  * @mode: either %GTK_SIZE_GROUP_HORIZONTAL or %GTK_SIZE_GROUP_VERTICAL, depending
747  *        on the dimension in which to bump the size.
748  * @minimum: a pointer to the widget's minimum size
749  * @natural: a pointer to the widget's natural size
750  *
751  * Refreshes the sizegroup while returning the groups requested
752  * value in the dimension @mode.
753  *
754  * This function is used both to update sizegroup minimum and natural size 
755  * information and widget minimum and natural sizes in multiple passes from 
756  * the size request apis.
757  */
758 void
759 _gtk_size_group_bump_requisition (GtkWidget        *widget,
760                                   GtkSizeGroupMode  mode,
761                                   gint             *minimum,
762                                   gint             *natural)
763 {
764   if (!_gtk_widget_get_sizegroup_bumping (widget))
765     {
766       /* Avoid recursion here */
767       _gtk_widget_set_sizegroup_bumping (widget, TRUE);
768
769       if (_gtk_widget_get_sizegroups (widget))
770         compute_dimension (widget, mode, minimum, natural);
771
772       _gtk_widget_set_sizegroup_bumping (widget, FALSE);
773     }
774 }
775
776
777
778 /**
779  * _gtk_size_group_queue_resize:
780  * @widget: a #GtkWidget
781  * 
782  * Queue a resize on a widget, and on all other widgets grouped with this widget.
783  **/
784 void
785 _gtk_size_group_queue_resize (GtkWidget           *widget,
786                               GtkQueueResizeFlags  flags)
787 {
788   queue_resize_on_widget (widget, TRUE, flags);
789 }
790
791 typedef struct {
792   GObject *object;
793   GSList *items;
794 } GSListSubParserData;
795
796 static void
797 size_group_start_element (GMarkupParseContext *context,
798                           const gchar         *element_name,
799                           const gchar        **names,
800                           const gchar        **values,
801                           gpointer            user_data,
802                           GError            **error)
803 {
804   guint i;
805   GSListSubParserData *data = (GSListSubParserData*)user_data;
806
807   if (strcmp (element_name, "widget") == 0)
808     for (i = 0; names[i]; i++)
809       if (strcmp (names[i], "name") == 0)
810         data->items = g_slist_prepend (data->items, g_strdup (values[i]));
811   else if (strcmp (element_name, "widgets") == 0)
812     return;
813   else
814     g_warning ("Unsupported type tag for GtkSizeGroup: %s\n",
815                element_name);
816
817 }
818
819 static const GMarkupParser size_group_parser =
820   {
821     size_group_start_element
822   };
823
824 static gboolean
825 gtk_size_group_buildable_custom_tag_start (GtkBuildable  *buildable,
826                                            GtkBuilder    *builder,
827                                            GObject       *child,
828                                            const gchar   *tagname,
829                                            GMarkupParser *parser,
830                                            gpointer      *data)
831 {
832   GSListSubParserData *parser_data;
833
834   if (child)
835     return FALSE;
836
837   if (strcmp (tagname, "widgets") == 0)
838     {
839       parser_data = g_slice_new0 (GSListSubParserData);
840       parser_data->items = NULL;
841       parser_data->object = G_OBJECT (buildable);
842
843       *parser = size_group_parser;
844       *data = parser_data;
845       return TRUE;
846     }
847
848   return FALSE;
849 }
850
851 static void
852 gtk_size_group_buildable_custom_finished (GtkBuildable  *buildable,
853                                           GtkBuilder    *builder,
854                                           GObject       *child,
855                                           const gchar   *tagname,
856                                           gpointer       user_data)
857 {
858   GSList *l;
859   GSListSubParserData *data;
860   GObject *object;
861
862   if (strcmp (tagname, "widgets"))
863     return;
864   
865   data = (GSListSubParserData*)user_data;
866   data->items = g_slist_reverse (data->items);
867
868   for (l = data->items; l; l = l->next)
869     {
870       object = gtk_builder_get_object (builder, l->data);
871       if (!object)
872         {
873           g_warning ("Unknown object %s specified in sizegroup %s",
874                      (const gchar*)l->data,
875                      gtk_buildable_get_name (GTK_BUILDABLE (data->object)));
876           continue;
877         }
878       gtk_size_group_add_widget (GTK_SIZE_GROUP (data->object),
879                                  GTK_WIDGET (object));
880       g_free (l->data);
881     }
882   g_slist_free (data->items);
883   g_slice_free (GSListSubParserData, data);
884 }