]> Pileus Git - ~andy/gtk/blob - gtk/gtksizegroup.c
GtkTreeView: unseal private pointer
[~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 _GtkSizeGroupPriv
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   GtkSizeGroupPriv *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       GtkSizeGroupPriv *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   GTK_PRIVATE_SET_FLAG (widget, GTK_ALLOC_NEEDED);
188   GTK_PRIVATE_SET_FLAG (widget, GTK_REQUEST_NEEDED);
189   GTK_PRIVATE_SET_FLAG (widget, GTK_WIDTH_REQUEST_NEEDED);
190   GTK_PRIVATE_SET_FLAG (widget, GTK_HEIGHT_REQUEST_NEEDED);
191   
192   if (widget->parent)
193     _gtk_container_queue_resize (GTK_CONTAINER (widget->parent));
194   else if (gtk_widget_is_toplevel (widget) && GTK_IS_CONTAINER (widget))
195     _gtk_container_queue_resize (GTK_CONTAINER (widget));
196 }
197
198 static void
199 reset_group_sizes (GSList *groups)
200 {
201   GSList *tmp_list = groups;
202   while (tmp_list)
203     {
204       GtkSizeGroup *tmp_group = tmp_list->data;
205       GtkSizeGroupPriv *tmp_priv = tmp_group->priv;
206
207       tmp_priv->have_width = FALSE;
208       tmp_priv->have_height = FALSE;
209
210       tmp_list = tmp_list->next;
211     }
212 }
213
214 static void
215 queue_resize_on_widget (GtkWidget *widget,
216                         gboolean   check_siblings)
217 {
218   GtkWidget *parent = widget;
219   GSList *tmp_list;
220
221   while (parent)
222     {
223       GSList *widget_groups;
224       GSList *groups;
225       GSList *widgets;
226       
227       if (widget == parent && !check_siblings)
228         {
229           real_queue_resize (widget);
230           parent = parent->parent;
231           continue;
232         }
233       
234       widget_groups = get_size_groups (parent);
235       if (!widget_groups)
236         {
237           if (widget == parent)
238             real_queue_resize (widget);
239
240           parent = parent->parent;
241           continue;
242         }
243
244       groups = NULL;
245       widgets = NULL;
246           
247       add_widget_to_closure (parent, GTK_SIZE_GROUP_HORIZONTAL, &groups, &widgets);
248       g_slist_foreach (widgets, (GFunc)mark_unvisited, NULL);
249       g_slist_foreach (groups, (GFunc)mark_unvisited, NULL);
250
251       reset_group_sizes (groups);
252               
253       tmp_list = widgets;
254       while (tmp_list)
255         {
256           if (tmp_list->data == parent)
257             {
258               if (widget == parent)
259                 real_queue_resize (parent);
260             }
261           else if (tmp_list->data == widget)
262             {
263               g_warning ("A container and its child are part of this SizeGroup");
264             }
265           else
266             queue_resize_on_widget (tmp_list->data, FALSE);
267
268           tmp_list = tmp_list->next;
269         }
270       
271       g_slist_free (widgets);
272       g_slist_free (groups);
273               
274       groups = NULL;
275       widgets = NULL;
276               
277       add_widget_to_closure (parent, GTK_SIZE_GROUP_VERTICAL, &groups, &widgets);
278       g_slist_foreach (widgets, (GFunc)mark_unvisited, NULL);
279       g_slist_foreach (groups, (GFunc)mark_unvisited, NULL);
280
281       reset_group_sizes (groups);
282               
283       tmp_list = widgets;
284       while (tmp_list)
285         {
286           if (tmp_list->data == parent)
287             {
288               if (widget == parent)
289                 real_queue_resize (parent);
290             }
291           else if (tmp_list->data == widget)
292             {
293               g_warning ("A container and its child are part of this SizeGroup");
294             }
295           else
296             queue_resize_on_widget (tmp_list->data, FALSE);
297
298           tmp_list = tmp_list->next;
299         }
300       
301       g_slist_free (widgets);
302       g_slist_free (groups);
303       
304       parent = parent->parent;
305     }
306 }
307
308 static void
309 queue_resize_on_group (GtkSizeGroup *size_group)
310 {
311   GtkSizeGroupPriv *priv = size_group->priv;
312
313   if (priv->widgets)
314     queue_resize_on_widget (priv->widgets->data, TRUE);
315 }
316
317 static void
318 initialize_size_group_quarks (void)
319 {
320   if (!size_groups_quark)
321     {
322       size_groups_quark = g_quark_from_static_string (size_groups_tag);
323       visited_quark = g_quark_from_static_string (visited_tag);
324       bumping_quark = g_quark_from_static_string (bumping_tag);
325     }
326 }
327
328 static void
329 gtk_size_group_class_init (GtkSizeGroupClass *klass)
330 {
331   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
332
333   gobject_class->set_property = gtk_size_group_set_property;
334   gobject_class->get_property = gtk_size_group_get_property;
335   
336   g_object_class_install_property (gobject_class,
337                                    PROP_MODE,
338                                    g_param_spec_enum ("mode",
339                                                       P_("Mode"),
340                                                       P_("The directions in which the size group affects the requested sizes"
341                                                         " of its component widgets"),
342                                                       GTK_TYPE_SIZE_GROUP_MODE,
343                                                       GTK_SIZE_GROUP_HORIZONTAL,                                                      GTK_PARAM_READWRITE));
344
345   /**
346    * GtkSizeGroup:ignore-hidden:
347    *
348    * If %TRUE, unmapped widgets are ignored when determining 
349    * the size of the group.
350    *
351    * Since: 2.8
352    */
353   g_object_class_install_property (gobject_class,
354                                    PROP_IGNORE_HIDDEN,
355                                    g_param_spec_boolean ("ignore-hidden",
356                                                          P_("Ignore hidden"),
357                                                          P_("If TRUE, unmapped widgets are ignored "
358                                                             "when determining the size of the group"),
359                                                          FALSE,
360                                                          GTK_PARAM_READWRITE));
361
362   g_type_class_add_private (klass, sizeof (GtkSizeGroupPriv));
363
364   initialize_size_group_quarks ();
365 }
366
367 static void
368 gtk_size_group_init (GtkSizeGroup *size_group)
369 {
370   GtkSizeGroupPriv *priv;
371
372   size_group->priv = G_TYPE_INSTANCE_GET_PRIVATE (size_group,
373                                                   GTK_TYPE_SIZE_GROUP,
374                                                   GtkSizeGroupPriv);
375   priv = size_group->priv;
376
377   priv->widgets = NULL;
378   priv->mode = GTK_SIZE_GROUP_HORIZONTAL;
379   priv->have_width = 0;
380   priv->have_height = 0;
381   priv->ignore_hidden = 0;
382 }
383
384 static void
385 gtk_size_group_buildable_init (GtkBuildableIface *iface)
386 {
387   iface->custom_tag_start = gtk_size_group_buildable_custom_tag_start;
388   iface->custom_finished = gtk_size_group_buildable_custom_finished;
389 }
390
391 G_DEFINE_TYPE_WITH_CODE (GtkSizeGroup, gtk_size_group, G_TYPE_OBJECT,
392                          G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
393                                                 gtk_size_group_buildable_init))
394
395 static void
396 gtk_size_group_set_property (GObject      *object,
397                              guint         prop_id,
398                              const GValue *value,
399                              GParamSpec   *pspec)
400 {
401   GtkSizeGroup *size_group = GTK_SIZE_GROUP (object);
402
403   switch (prop_id)
404     {
405     case PROP_MODE:
406       gtk_size_group_set_mode (size_group, g_value_get_enum (value));
407       break;
408     case PROP_IGNORE_HIDDEN:
409       gtk_size_group_set_ignore_hidden (size_group, g_value_get_boolean (value));
410       break;
411     default:
412       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
413       break;
414     }
415 }
416
417 static void
418 gtk_size_group_get_property (GObject      *object,
419                              guint         prop_id,
420                              GValue       *value,
421                              GParamSpec   *pspec)
422 {
423   GtkSizeGroup *size_group = GTK_SIZE_GROUP (object);
424   GtkSizeGroupPriv *priv = size_group->priv;
425
426   switch (prop_id)
427     {
428     case PROP_MODE:
429       g_value_set_enum (value, priv->mode);
430       break;
431     case PROP_IGNORE_HIDDEN:
432       g_value_set_boolean (value, priv->ignore_hidden);
433       break;
434     default:
435       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
436       break;
437     }
438 }
439
440 /**
441  * gtk_size_group_new:
442  * @mode: the mode for the new size group.
443  * 
444  * Create a new #GtkSizeGroup.
445  
446  * Return value: a newly created #GtkSizeGroup
447  **/
448 GtkSizeGroup *
449 gtk_size_group_new (GtkSizeGroupMode mode)
450 {
451   GtkSizeGroup *size_group = g_object_new (GTK_TYPE_SIZE_GROUP, NULL);
452   GtkSizeGroupPriv *priv = size_group->priv;
453
454   priv->mode = mode;
455
456   return size_group;
457 }
458
459 /**
460  * gtk_size_group_set_mode:
461  * @size_group: a #GtkSizeGroup
462  * @mode: the mode to set for the size group.
463  * 
464  * Sets the #GtkSizeGroupMode of the size group. The mode of the size
465  * group determines whether the widgets in the size group should
466  * all have the same horizontal requisition (%GTK_SIZE_GROUP_MODE_HORIZONTAL)
467  * all have the same vertical requisition (%GTK_SIZE_GROUP_MODE_VERTICAL),
468  * or should all have the same requisition in both directions
469  * (%GTK_SIZE_GROUP_MODE_BOTH).
470  **/
471 void
472 gtk_size_group_set_mode (GtkSizeGroup     *size_group,
473                          GtkSizeGroupMode  mode)
474 {
475   GtkSizeGroupPriv *priv;
476
477   g_return_if_fail (GTK_IS_SIZE_GROUP (size_group));
478
479   priv = size_group->priv;
480
481   if (priv->mode != mode)
482     {
483       if (priv->mode != GTK_SIZE_GROUP_NONE)
484         queue_resize_on_group (size_group);
485       priv->mode = mode;
486       if (priv->mode != GTK_SIZE_GROUP_NONE)
487         queue_resize_on_group (size_group);
488
489       g_object_notify (G_OBJECT (size_group), "mode");
490     }
491 }
492
493 /**
494  * gtk_size_group_get_mode:
495  * @size_group: a #GtkSizeGroup
496  * 
497  * Gets the current mode of the size group. See gtk_size_group_set_mode().
498  * 
499  * Return value: the current mode of the size group.
500  **/
501 GtkSizeGroupMode
502 gtk_size_group_get_mode (GtkSizeGroup *size_group)
503 {
504   g_return_val_if_fail (GTK_IS_SIZE_GROUP (size_group), GTK_SIZE_GROUP_BOTH);
505
506   return size_group->priv->mode;
507 }
508
509 /**
510  * gtk_size_group_set_ignore_hidden:
511  * @size_group: a #GtkSizeGroup
512  * @ignore_hidden: whether unmapped widgets should be ignored
513  *   when calculating the size
514  * 
515  * Sets whether unmapped widgets should be ignored when
516  * calculating the size.
517  *
518  * Since: 2.8 
519  */
520 void
521 gtk_size_group_set_ignore_hidden (GtkSizeGroup *size_group,
522                                   gboolean      ignore_hidden)
523 {
524   GtkSizeGroupPriv *priv;
525
526   g_return_if_fail (GTK_IS_SIZE_GROUP (size_group));
527
528   priv = size_group->priv;
529
530   ignore_hidden = ignore_hidden != FALSE;
531
532   if (priv->ignore_hidden != ignore_hidden)
533     {
534       priv->ignore_hidden = ignore_hidden;
535
536       g_object_notify (G_OBJECT (size_group), "ignore-hidden");
537     }
538 }
539
540 /**
541  * gtk_size_group_get_ignore_hidden:
542  * @size_group: a #GtkSizeGroup
543  *
544  * Returns if invisible widgets are ignored when calculating the size.
545  *
546  * Returns: %TRUE if invisible widgets are ignored.
547  *
548  * Since: 2.8
549  */
550 gboolean
551 gtk_size_group_get_ignore_hidden (GtkSizeGroup *size_group)
552 {
553   g_return_val_if_fail (GTK_IS_SIZE_GROUP (size_group), FALSE);
554
555   return size_group->priv->ignore_hidden;
556 }
557
558 static void
559 gtk_size_group_widget_destroyed (GtkWidget    *widget,
560                                  GtkSizeGroup *size_group)
561 {
562   gtk_size_group_remove_widget (size_group, widget);
563 }
564
565 /**
566  * gtk_size_group_add_widget:
567  * @size_group: a #GtkSizeGroup
568  * @widget: the #GtkWidget to add
569  * 
570  * Adds a widget to a #GtkSizeGroup. In the future, the requisition
571  * of the widget will be determined as the maximum of its requisition
572  * and the requisition of the other widgets in the size group.
573  * Whether this applies horizontally, vertically, or in both directions
574  * depends on the mode of the size group. See gtk_size_group_set_mode().
575  *
576  * When the widget is destroyed or no longer referenced elsewhere, it will 
577  * be removed from the size group.
578  */
579 void
580 gtk_size_group_add_widget (GtkSizeGroup     *size_group,
581                            GtkWidget        *widget)
582 {
583   GtkSizeGroupPriv *priv;
584   GSList *groups;
585   
586   g_return_if_fail (GTK_IS_SIZE_GROUP (size_group));
587   g_return_if_fail (GTK_IS_WIDGET (widget));
588
589   priv = size_group->priv;
590
591   groups = get_size_groups (widget);
592
593   if (!g_slist_find (groups, size_group))
594     {
595       groups = g_slist_prepend (groups, size_group);
596       set_size_groups (widget, groups);
597
598       priv->widgets = g_slist_prepend (priv->widgets, widget);
599
600       g_signal_connect (widget, "destroy",
601                         G_CALLBACK (gtk_size_group_widget_destroyed),
602                         size_group);
603
604       g_object_ref (size_group);
605     }
606   
607   queue_resize_on_group (size_group);
608 }
609
610 /**
611  * gtk_size_group_remove_widget:
612  * @size_group: a #GtkSizeGroup
613  * @widget: the #GtkWidget to remove
614  * 
615  * Removes a widget from a #GtkSizeGroup.
616  **/
617 void
618 gtk_size_group_remove_widget (GtkSizeGroup *size_group,
619                               GtkWidget    *widget)
620 {
621   GtkSizeGroupPriv *priv;
622   GSList *groups;
623   
624   g_return_if_fail (GTK_IS_SIZE_GROUP (size_group));
625   g_return_if_fail (GTK_IS_WIDGET (widget));
626
627   priv = size_group->priv;
628
629   g_return_if_fail (g_slist_find (priv->widgets, widget));
630
631   g_signal_handlers_disconnect_by_func (widget,
632                                         gtk_size_group_widget_destroyed,
633                                         size_group);
634   
635   groups = get_size_groups (widget);
636   groups = g_slist_remove (groups, size_group);
637   set_size_groups (widget, groups);
638
639   priv->widgets = g_slist_remove (priv->widgets, widget);
640   queue_resize_on_group (size_group);
641   gtk_widget_queue_resize (widget);
642
643   g_object_unref (size_group);
644 }
645
646 /**
647  * gtk_size_group_get_widgets:
648  * @size_group: a #GtkSizeGroup
649  * 
650  * Returns the list of widgets associated with @size_group.
651  *
652  * Return value:  (element-type GtkWidget) (transfer none): a #GSList of
653  *   widgets. The list is owned by GTK+ and should not be modified.
654  *
655  * Since: 2.10
656  **/
657 GSList *
658 gtk_size_group_get_widgets (GtkSizeGroup *size_group)
659 {
660   return size_group->priv->widgets;
661 }
662
663 static gint
664 get_base_dimension (GtkWidget        *widget,
665                     GtkSizeGroupMode  mode)
666 {
667   GtkWidgetAuxInfo *aux_info = _gtk_widget_get_aux_info (widget, FALSE);
668
669   if (mode == GTK_SIZE_GROUP_HORIZONTAL)
670     {
671       if (aux_info && aux_info->width > 0)
672         return aux_info->width;
673       else
674         {
675           /* XXX Possibly we should be using natural values and not minimums here. */
676           gint width;
677
678           gtk_size_request_get_width (GTK_SIZE_REQUEST (widget), &width, NULL);
679
680           return width;
681         }
682     }
683   else
684     {
685       if (aux_info && aux_info->height > 0)
686         return aux_info->height;
687       else
688         {
689           /* XXX Possibly we should be using natural values and not minimums here. */
690           gint height;
691
692           gtk_size_request_get_height (GTK_SIZE_REQUEST (widget), &height, NULL);
693
694           return height;
695         }
696     }
697 }
698
699 static gint
700 compute_dimension (GtkWidget        *widget,
701                    GtkSizeGroupMode  mode,
702                    gint              widget_requisition)
703 {
704   GSList *widgets = NULL;
705   GSList *groups = NULL;
706   GSList *tmp_list;
707   gint result = 0;
708
709   add_widget_to_closure (widget, mode, &groups, &widgets);
710
711   g_slist_foreach (widgets, (GFunc)mark_unvisited, NULL);
712   g_slist_foreach (groups, (GFunc)mark_unvisited, NULL);
713   
714   g_slist_foreach (widgets, (GFunc)g_object_ref, NULL);
715   
716   if (!groups)
717     {
718       result = widget_requisition;
719     }
720   else
721     {
722       GtkSizeGroup *group = groups->data;
723       GtkSizeGroupPriv *priv = group->priv;
724
725       if (mode == GTK_SIZE_GROUP_HORIZONTAL && priv->have_width)
726         result = priv->requisition.width;
727       else if (mode == GTK_SIZE_GROUP_VERTICAL && priv->have_height)
728         result = priv->requisition.height;
729       else
730         {
731           tmp_list = widgets;
732           while (tmp_list)
733             {
734               GtkWidget *tmp_widget = tmp_list->data;
735               gint dimension;
736
737               if (tmp_widget == widget)
738                 dimension = widget_requisition;
739               else
740                 dimension = get_base_dimension (tmp_widget, mode);
741
742               if (gtk_widget_get_mapped (tmp_widget) || !priv->ignore_hidden)
743                 {
744                   if (dimension > result)
745                     result = dimension;
746                 }
747
748               tmp_list = tmp_list->next;
749             }
750
751           tmp_list = groups;
752           while (tmp_list)
753             {
754               GtkSizeGroup *tmp_group = tmp_list->data;
755               GtkSizeGroupPriv *tmp_priv = tmp_group->priv;
756
757               if (mode == GTK_SIZE_GROUP_HORIZONTAL)
758                 {
759                   tmp_priv->have_width = TRUE;
760                   tmp_priv->requisition.width = result;
761                 }
762               else
763                 {
764                   tmp_priv->have_height = TRUE;
765                   tmp_priv->requisition.height = result;
766                 }
767               
768               tmp_list = tmp_list->next;
769             }
770         }
771     }
772
773   g_slist_foreach (widgets, (GFunc)g_object_unref, NULL);
774
775   g_slist_free (widgets);
776   g_slist_free (groups);
777
778   return result;
779 }
780
781 /**
782  * _gtk_size_group_bump_requisition:
783  * @widget: a #GtkWidget
784  * @mode: either %GTK_SIZE_GROUP_HORIZONTAL or %GTK_SIZE_GROUP_VERTICAL, depending
785  *        on the dimension in which to bump the size.
786  *
787  * Refreshes the sizegroup while returning the groups requested
788  * value in the dimension @mode.
789  *
790  * This function is used to update sizegroup minimum size information
791  * in multiple passes from the new #GtkSizeRequest manager.
792  */
793 gint
794 _gtk_size_group_bump_requisition (GtkWidget        *widget,
795                                   GtkSizeGroupMode  mode,
796                                   gint              widget_requisition)
797 {
798   gint result = widget_requisition;
799
800   if (!is_bumping (widget))
801     {
802       GtkWidgetAuxInfo *aux_info = 
803         _gtk_widget_get_aux_info (widget, FALSE);
804
805       /* Avoid recursion here */
806       mark_bumping (widget, TRUE);
807
808       if (get_size_groups (widget))
809         {
810           if (aux_info)
811             {
812               if (mode == GTK_SIZE_GROUP_HORIZONTAL)
813                 result = compute_dimension (widget, mode, MAX (aux_info->width, widget_requisition));
814               else 
815                 result = compute_dimension (widget, mode, MAX (aux_info->height, widget_requisition));
816             }
817           else
818             result = compute_dimension (widget, mode, widget_requisition);
819         }
820       else if (aux_info)
821         {
822           if (mode == GTK_SIZE_GROUP_HORIZONTAL)
823             result = MAX (aux_info->width, widget_requisition);
824           else 
825             result = MAX (aux_info->height, widget_requisition);
826         }
827       mark_bumping (widget, FALSE);
828     }
829   return result;
830 }
831
832
833
834 /**
835  * _gtk_size_group_queue_resize:
836  * @widget: a #GtkWidget
837  * 
838  * Queue a resize on a widget, and on all other widgets grouped with this widget.
839  **/
840 void
841 _gtk_size_group_queue_resize (GtkWidget *widget)
842 {
843   initialize_size_group_quarks ();
844
845   queue_resize_on_widget (widget, TRUE);
846 }
847
848 typedef struct {
849   GObject *object;
850   GSList *items;
851 } GSListSubParserData;
852
853 static void
854 size_group_start_element (GMarkupParseContext *context,
855                           const gchar         *element_name,
856                           const gchar        **names,
857                           const gchar        **values,
858                           gpointer            user_data,
859                           GError            **error)
860 {
861   guint i;
862   GSListSubParserData *data = (GSListSubParserData*)user_data;
863
864   if (strcmp (element_name, "widget") == 0)
865     for (i = 0; names[i]; i++)
866       if (strcmp (names[i], "name") == 0)
867         data->items = g_slist_prepend (data->items, g_strdup (values[i]));
868   else if (strcmp (element_name, "widgets") == 0)
869     return;
870   else
871     g_warning ("Unsupported type tag for GtkSizeGroup: %s\n",
872                element_name);
873
874 }
875
876 static const GMarkupParser size_group_parser =
877   {
878     size_group_start_element
879   };
880
881 static gboolean
882 gtk_size_group_buildable_custom_tag_start (GtkBuildable  *buildable,
883                                            GtkBuilder    *builder,
884                                            GObject       *child,
885                                            const gchar   *tagname,
886                                            GMarkupParser *parser,
887                                            gpointer      *data)
888 {
889   GSListSubParserData *parser_data;
890
891   if (child)
892     return FALSE;
893
894   if (strcmp (tagname, "widgets") == 0)
895     {
896       parser_data = g_slice_new0 (GSListSubParserData);
897       parser_data->items = NULL;
898       parser_data->object = G_OBJECT (buildable);
899
900       *parser = size_group_parser;
901       *data = parser_data;
902       return TRUE;
903     }
904
905   return FALSE;
906 }
907
908 static void
909 gtk_size_group_buildable_custom_finished (GtkBuildable  *buildable,
910                                           GtkBuilder    *builder,
911                                           GObject       *child,
912                                           const gchar   *tagname,
913                                           gpointer       user_data)
914 {
915   GSList *l;
916   GSListSubParserData *data;
917   GObject *object;
918
919   if (strcmp (tagname, "widgets"))
920     return;
921   
922   data = (GSListSubParserData*)user_data;
923   data->items = g_slist_reverse (data->items);
924
925   for (l = data->items; l; l = l->next)
926     {
927       object = gtk_builder_get_object (builder, l->data);
928       if (!object)
929         {
930           g_warning ("Unknown object %s specified in sizegroup %s",
931                      (const gchar*)l->data,
932                      gtk_buildable_get_name (GTK_BUILDABLE (data->object)));
933           continue;
934         }
935       gtk_size_group_add_widget (GTK_SIZE_GROUP (data->object),
936                                  GTK_WIDGET (object));
937       g_free (l->data);
938     }
939   g_slist_free (data->items);
940   g_slice_free (GSListSubParserData, data);
941 }