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