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