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