]> Pileus Git - ~andy/gtk/blob - gtk/gtksizegroup.c
cc16476e816177fef645b0eb8db431dd9854f283
[~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 #include "gtksizerequest.h"
29
30
31
32 struct _GtkSizeGroupPrivate
33 {
34   GtkRequisition  requisition;
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 gint
666 get_base_dimension (GtkWidget        *widget,
667                     GtkSizeGroupMode  mode)
668 {
669   if (mode == GTK_SIZE_GROUP_HORIZONTAL)
670     {
671       /* XXX Possibly we should be using natural values and not minimums here. */
672       gint width;
673
674       gtk_size_request_get_width (GTK_SIZE_REQUEST (widget), &width, NULL);
675
676       return width;
677     }
678   else
679     {
680       /* XXX Possibly we should be using natural values and not minimums here. */
681       gint height;
682
683       gtk_size_request_get_height (GTK_SIZE_REQUEST (widget), &height, NULL);
684
685       return height;
686     }
687 }
688
689 static gint
690 compute_dimension (GtkWidget        *widget,
691                    GtkSizeGroupMode  mode,
692                    gint              widget_requisition)
693 {
694   GSList *widgets = NULL;
695   GSList *groups = NULL;
696   GSList *tmp_list;
697   gint result = 0;
698
699   add_widget_to_closure (widget, mode, &groups, &widgets);
700
701   g_slist_foreach (widgets, (GFunc)mark_unvisited, NULL);
702   g_slist_foreach (groups, (GFunc)mark_unvisited, NULL);
703   
704   g_slist_foreach (widgets, (GFunc)g_object_ref, NULL);
705   
706   if (!groups)
707     {
708       result = widget_requisition;
709     }
710   else
711     {
712       GtkSizeGroup *group = groups->data;
713       GtkSizeGroupPrivate *priv = group->priv;
714
715       if (mode == GTK_SIZE_GROUP_HORIZONTAL && priv->have_width)
716         result = priv->requisition.width;
717       else if (mode == GTK_SIZE_GROUP_VERTICAL && priv->have_height)
718         result = priv->requisition.height;
719       else
720         {
721           tmp_list = widgets;
722           while (tmp_list)
723             {
724               GtkWidget *tmp_widget = tmp_list->data;
725               gint dimension;
726
727               if (tmp_widget == widget)
728                 dimension = widget_requisition;
729               else
730                 dimension = get_base_dimension (tmp_widget, mode);
731
732               if (gtk_widget_get_mapped (tmp_widget) || !priv->ignore_hidden)
733                 {
734                   if (dimension > result)
735                     result = dimension;
736                 }
737
738               tmp_list = tmp_list->next;
739             }
740
741           tmp_list = groups;
742           while (tmp_list)
743             {
744               GtkSizeGroup *tmp_group = tmp_list->data;
745               GtkSizeGroupPrivate *tmp_priv = tmp_group->priv;
746
747               if (mode == GTK_SIZE_GROUP_HORIZONTAL)
748                 {
749                   tmp_priv->have_width = TRUE;
750                   tmp_priv->requisition.width = result;
751                 }
752               else
753                 {
754                   tmp_priv->have_height = TRUE;
755                   tmp_priv->requisition.height = result;
756                 }
757               
758               tmp_list = tmp_list->next;
759             }
760         }
761     }
762
763   g_slist_foreach (widgets, (GFunc)g_object_unref, NULL);
764
765   g_slist_free (widgets);
766   g_slist_free (groups);
767
768   return result;
769 }
770
771 /**
772  * _gtk_size_group_bump_requisition:
773  * @widget: a #GtkWidget
774  * @mode: either %GTK_SIZE_GROUP_HORIZONTAL or %GTK_SIZE_GROUP_VERTICAL, depending
775  *        on the dimension in which to bump the size.
776  *
777  * Refreshes the sizegroup while returning the groups requested
778  * value in the dimension @mode.
779  *
780  * This function is used to update sizegroup minimum size information
781  * in multiple passes from the new #GtkSizeRequest manager.
782  */
783 gint
784 _gtk_size_group_bump_requisition (GtkWidget        *widget,
785                                   GtkSizeGroupMode  mode,
786                                   gint              widget_requisition)
787 {
788   gint result = widget_requisition;
789
790   if (!is_bumping (widget))
791     {
792       /* Avoid recursion here */
793       mark_bumping (widget, TRUE);
794
795       if (get_size_groups (widget))
796         {
797           result = compute_dimension (widget, mode, widget_requisition);
798         }
799
800       mark_bumping (widget, FALSE);
801     }
802   return result;
803 }
804
805
806
807 /**
808  * _gtk_size_group_queue_resize:
809  * @widget: a #GtkWidget
810  * 
811  * Queue a resize on a widget, and on all other widgets grouped with this widget.
812  **/
813 void
814 _gtk_size_group_queue_resize (GtkWidget *widget)
815 {
816   initialize_size_group_quarks ();
817
818   queue_resize_on_widget (widget, TRUE);
819 }
820
821 typedef struct {
822   GObject *object;
823   GSList *items;
824 } GSListSubParserData;
825
826 static void
827 size_group_start_element (GMarkupParseContext *context,
828                           const gchar         *element_name,
829                           const gchar        **names,
830                           const gchar        **values,
831                           gpointer            user_data,
832                           GError            **error)
833 {
834   guint i;
835   GSListSubParserData *data = (GSListSubParserData*)user_data;
836
837   if (strcmp (element_name, "widget") == 0)
838     for (i = 0; names[i]; i++)
839       if (strcmp (names[i], "name") == 0)
840         data->items = g_slist_prepend (data->items, g_strdup (values[i]));
841   else if (strcmp (element_name, "widgets") == 0)
842     return;
843   else
844     g_warning ("Unsupported type tag for GtkSizeGroup: %s\n",
845                element_name);
846
847 }
848
849 static const GMarkupParser size_group_parser =
850   {
851     size_group_start_element
852   };
853
854 static gboolean
855 gtk_size_group_buildable_custom_tag_start (GtkBuildable  *buildable,
856                                            GtkBuilder    *builder,
857                                            GObject       *child,
858                                            const gchar   *tagname,
859                                            GMarkupParser *parser,
860                                            gpointer      *data)
861 {
862   GSListSubParserData *parser_data;
863
864   if (child)
865     return FALSE;
866
867   if (strcmp (tagname, "widgets") == 0)
868     {
869       parser_data = g_slice_new0 (GSListSubParserData);
870       parser_data->items = NULL;
871       parser_data->object = G_OBJECT (buildable);
872
873       *parser = size_group_parser;
874       *data = parser_data;
875       return TRUE;
876     }
877
878   return FALSE;
879 }
880
881 static void
882 gtk_size_group_buildable_custom_finished (GtkBuildable  *buildable,
883                                           GtkBuilder    *builder,
884                                           GObject       *child,
885                                           const gchar   *tagname,
886                                           gpointer       user_data)
887 {
888   GSList *l;
889   GSListSubParserData *data;
890   GObject *object;
891
892   if (strcmp (tagname, "widgets"))
893     return;
894   
895   data = (GSListSubParserData*)user_data;
896   data->items = g_slist_reverse (data->items);
897
898   for (l = data->items; l; l = l->next)
899     {
900       object = gtk_builder_get_object (builder, l->data);
901       if (!object)
902         {
903           g_warning ("Unknown object %s specified in sizegroup %s",
904                      (const gchar*)l->data,
905                      gtk_buildable_get_name (GTK_BUILDABLE (data->object)));
906           continue;
907         }
908       gtk_size_group_add_widget (GTK_SIZE_GROUP (data->object),
909                                  GTK_WIDGET (object));
910       g_free (l->data);
911     }
912   g_slist_free (data->items);
913   g_slice_free (GSListSubParserData, data);
914 }