1 /* GTK - The GIMP Toolkit
3 * Copyright (C) 2001 Red Hat Software
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.
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.
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.
22 #include "gtkcontainer.h"
24 #include "gtkprivate.h"
25 #include "gtksizegroup.h"
33 static void gtk_size_group_set_property (GObject *object,
37 static void gtk_size_group_get_property (GObject *object,
42 static void add_group_to_closure (GtkSizeGroup *group,
43 GtkSizeGroupMode mode,
46 static void add_widget_to_closure (GtkWidget *widget,
47 GtkSizeGroupMode mode,
51 static GQuark size_groups_quark;
52 static const gchar size_groups_tag[] = "gtk-size-groups";
55 get_size_groups (GtkWidget *widget)
57 if (!size_groups_quark)
58 size_groups_quark = g_quark_from_string (size_groups_tag);
60 return g_object_get_qdata (G_OBJECT (widget), size_groups_quark);
64 set_size_groups (GtkWidget *widget,
67 if (!size_groups_quark)
68 size_groups_quark = g_quark_from_string (size_groups_tag);
70 g_object_set_qdata (G_OBJECT (widget), size_groups_quark, groups);
74 add_group_to_closure (GtkSizeGroup *group,
75 GtkSizeGroupMode mode,
81 *groups = g_slist_prepend (*groups, group);
83 tmp_widgets = group->widgets;
86 GtkWidget *tmp_widget = tmp_widgets->data;
88 if (!g_slist_find (*widgets, tmp_widget))
89 add_widget_to_closure (tmp_widget, mode, groups, widgets);
91 tmp_widgets = tmp_widgets->next;
96 add_widget_to_closure (GtkWidget *widget,
97 GtkSizeGroupMode mode,
103 *widgets = g_slist_prepend (*widgets, widget);
105 tmp_groups = get_size_groups (widget);
108 GtkSizeGroup *tmp_group = tmp_groups->data;
110 if ((tmp_group->mode == GTK_SIZE_GROUP_BOTH || tmp_group->mode == mode) &&
111 !g_slist_find (*groups, tmp_group))
112 add_group_to_closure (tmp_group, mode, groups, widgets);
114 tmp_groups = tmp_groups->next;
119 real_queue_resize (GtkWidget *widget)
121 GTK_PRIVATE_SET_FLAG (widget, GTK_ALLOC_NEEDED);
122 GTK_PRIVATE_SET_FLAG (widget, GTK_REQUEST_NEEDED);
125 _gtk_container_queue_resize (GTK_CONTAINER (widget->parent));
126 else if (GTK_WIDGET_TOPLEVEL (widget) && GTK_IS_CONTAINER (widget))
127 _gtk_container_queue_resize (GTK_CONTAINER (widget));
131 reset_group_sizes (GSList *groups)
133 GSList *tmp_list = groups;
136 GtkSizeGroup *tmp_group = tmp_list->data;
138 tmp_group->have_width = FALSE;
139 tmp_group->have_height = FALSE;
141 tmp_list = tmp_list->next;
146 queue_resize_on_widget (GtkWidget *widget,
147 gboolean check_siblings)
149 GtkWidget *parent = widget;
154 GSList *widget_groups;
158 if (widget == parent && !check_siblings)
160 real_queue_resize (widget);
161 parent = parent->parent;
165 widget_groups = get_size_groups (parent);
168 if (widget == parent)
169 real_queue_resize (widget);
171 parent = parent->parent;
178 add_widget_to_closure (parent, GTK_SIZE_GROUP_HORIZONTAL, &groups, &widgets);
179 reset_group_sizes (groups);
184 if (tmp_list->data == parent)
186 if (widget == parent)
187 real_queue_resize (parent);
190 queue_resize_on_widget (tmp_list->data, FALSE);
192 tmp_list = tmp_list->next;
195 g_slist_free (widgets);
196 g_slist_free (groups);
201 add_widget_to_closure (parent, GTK_SIZE_GROUP_VERTICAL, &groups, &widgets);
202 reset_group_sizes (groups);
207 if (tmp_list->data == parent)
209 if (widget == parent)
210 real_queue_resize (parent);
213 queue_resize_on_widget (tmp_list->data, FALSE);
215 tmp_list = tmp_list->next;
218 g_slist_free (widgets);
219 g_slist_free (groups);
221 parent = parent->parent;
226 queue_resize_on_group (GtkSizeGroup *size_group)
228 if (size_group->widgets)
229 queue_resize_on_widget (size_group->widgets->data, TRUE);
233 gtk_size_group_class_init (GtkSizeGroupClass *klass)
235 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
237 gobject_class->set_property = gtk_size_group_set_property;
238 gobject_class->get_property = gtk_size_group_get_property;
240 g_object_class_install_property (gobject_class,
242 g_param_spec_enum ("mode",
244 P_("The directions in which the size group affects the requested sizes"
245 " of its component widgets"),
246 GTK_TYPE_SIZE_GROUP_MODE,
247 GTK_SIZE_GROUP_HORIZONTAL,
252 gtk_size_group_init (GtkSizeGroup *size_group)
254 size_group->widgets = NULL;
255 size_group->mode = GTK_SIZE_GROUP_HORIZONTAL;
256 size_group->have_width = 0;
257 size_group->have_height = 0;
261 gtk_size_group_get_type (void)
263 static GType size_group_type = 0;
265 if (!size_group_type)
267 static const GTypeInfo size_group_info =
269 sizeof (GtkSizeGroupClass),
270 NULL, /* base_init */
271 NULL, /* base_finalize */
272 (GClassInitFunc) gtk_size_group_class_init,
273 NULL, /* class_finalize */
274 NULL, /* class_data */
275 sizeof (GtkSizeGroup),
276 16, /* n_preallocs */
277 (GInstanceInitFunc) gtk_size_group_init,
280 size_group_type = g_type_register_static (G_TYPE_OBJECT, "GtkSizeGroup",
281 &size_group_info, 0);
284 return size_group_type;
288 gtk_size_group_set_property (GObject *object,
293 GtkSizeGroup *size_group = GTK_SIZE_GROUP (object);
298 gtk_size_group_set_mode (size_group, g_value_get_enum (value));
301 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
307 gtk_size_group_get_property (GObject *object,
312 GtkSizeGroup *size_group = GTK_SIZE_GROUP (object);
317 g_value_set_enum (value, size_group->mode);
320 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
326 * gtk_size_group_new:
327 * @mode: the mode for the new size group.
329 * Create a new #GtkSizeGroup.
331 * Return value: a newly created #GtkSizeGroup
334 gtk_size_group_new (GtkSizeGroupMode mode)
336 GtkSizeGroup *size_group = g_object_new (GTK_TYPE_SIZE_GROUP, NULL);
338 size_group->mode = mode;
344 * gtk_size_group_set_mode:
345 * @size_group: a #GtkSizeGroup
346 * @mode: the mode to set for the size group.
348 * Sets the #GtkSizeGroupMode of the size group. The mode of the size
349 * group determines whether the widgets in the size group should
350 * all have the same horizontal requisition (%GTK_SIZE_GROUP_MODE_HORIZONTAL)
351 * all have the same vertical requisition (%GTK_SIZE_GROUP_MODE_VERTICAL),
352 * or should all have the same requisition in both directions
353 * (%GTK_SIZE_GROUP_MODE_BOTH).
356 gtk_size_group_set_mode (GtkSizeGroup *size_group,
357 GtkSizeGroupMode mode)
359 g_return_if_fail (GTK_IS_SIZE_GROUP (size_group));
361 if (size_group->mode != mode)
363 if (size_group->mode != GTK_SIZE_GROUP_NONE)
364 queue_resize_on_group (size_group);
365 size_group->mode = mode;
366 if (size_group->mode != GTK_SIZE_GROUP_NONE)
367 queue_resize_on_group (size_group);
369 g_object_notify (G_OBJECT (size_group), "mode");
374 * gtk_size_group_get_mode:
375 * @size_group: a #GtkSizeGroup
377 * Gets the current mode of the size group. See gtk_size_group_set_mode().
379 * Return value: the current mode of the size group.
382 gtk_size_group_get_mode (GtkSizeGroup *size_group)
384 g_return_val_if_fail (GTK_IS_SIZE_GROUP (size_group), GTK_SIZE_GROUP_BOTH);
386 return size_group->mode;
390 gtk_size_group_widget_destroyed (GtkWidget *widget,
391 GtkSizeGroup *size_group)
393 gtk_size_group_remove_widget (size_group, widget);
397 * gtk_size_group_add_widget:
398 * @size_group: a #GtkSizeGroup
399 * @widget: the #GtkWidget to add
401 * Adds a widget to a #GtkSizeGroup. In the future, the requisition
402 * of the widget will be determined as the maximum of its requisition
403 * and the requisition of the other widgets in the size group.
404 * Whether this applies horizontally, vertically, or in both directions
405 * depends on the mode of the size group. See gtk_size_group_set_mode().
408 gtk_size_group_add_widget (GtkSizeGroup *size_group,
413 g_return_if_fail (GTK_IS_SIZE_GROUP (size_group));
414 g_return_if_fail (GTK_IS_WIDGET (widget));
416 groups = get_size_groups (widget);
418 if (!g_slist_find (groups, size_group))
420 groups = g_slist_prepend (groups, size_group);
421 set_size_groups (widget, groups);
423 size_group->widgets = g_slist_prepend (size_group->widgets, widget);
425 g_signal_connect (widget, "destroy",
426 G_CALLBACK (gtk_size_group_widget_destroyed),
429 g_object_ref (size_group);
432 queue_resize_on_group (size_group);
436 * gtk_size_group_remove_widget:
437 * @size_group: a #GtkSizeGrup
438 * @widget: the #GtkWidget to remove
440 * Removes a widget from a #GtkSizeGroup.
443 gtk_size_group_remove_widget (GtkSizeGroup *size_group,
448 g_return_if_fail (GTK_IS_SIZE_GROUP (size_group));
449 g_return_if_fail (GTK_IS_WIDGET (widget));
450 g_return_if_fail (g_slist_find (size_group->widgets, widget));
452 g_signal_handlers_disconnect_by_func (widget,
453 gtk_size_group_widget_destroyed,
456 groups = get_size_groups (widget);
457 groups = g_slist_remove (groups, size_group);
458 set_size_groups (widget, groups);
460 size_group->widgets = g_slist_remove (size_group->widgets, widget);
461 queue_resize_on_group (size_group);
462 gtk_widget_queue_resize (widget);
464 g_object_unref (size_group);
468 get_base_dimension (GtkWidget *widget,
469 GtkSizeGroupMode mode)
471 GtkWidgetAuxInfo *aux_info = _gtk_widget_get_aux_info (widget, FALSE);
473 if (mode == GTK_SIZE_GROUP_HORIZONTAL)
475 if (aux_info && aux_info->width > 0)
476 return aux_info->width;
478 return widget->requisition.width;
482 if (aux_info && aux_info->height > 0)
483 return aux_info->height;
485 return widget->requisition.height;
490 do_size_request (GtkWidget *widget)
492 if (GTK_WIDGET_REQUEST_NEEDED (widget))
494 gtk_widget_ensure_style (widget);
495 GTK_PRIVATE_UNSET_FLAG (widget, GTK_REQUEST_NEEDED);
496 g_signal_emit_by_name (widget,
498 &widget->requisition);
503 compute_base_dimension (GtkWidget *widget,
504 GtkSizeGroupMode mode)
506 do_size_request (widget);
508 return get_base_dimension (widget, mode);
512 compute_dimension (GtkWidget *widget,
513 GtkSizeGroupMode mode)
515 GSList *widgets = NULL;
516 GSList *groups = NULL;
520 add_widget_to_closure (widget, mode, &groups, &widgets);
522 g_slist_foreach (widgets, (GFunc)g_object_ref, NULL);
526 result = compute_base_dimension (widget, mode);
530 GtkSizeGroup *group = groups->data;
532 if (mode == GTK_SIZE_GROUP_HORIZONTAL && group->have_width)
533 result = group->requisition.width;
534 else if (mode == GTK_SIZE_GROUP_VERTICAL && group->have_height)
535 result = group->requisition.height;
541 GtkWidget *tmp_widget = tmp_list->data;
543 gint dimension = compute_base_dimension (tmp_widget, mode);
545 if (dimension > result)
548 tmp_list = tmp_list->next;
554 GtkSizeGroup *tmp_group = tmp_list->data;
556 if (mode == GTK_SIZE_GROUP_HORIZONTAL)
558 tmp_group->have_width = TRUE;
559 tmp_group->requisition.width = result;
563 tmp_group->have_height = TRUE;
564 tmp_group->requisition.height = result;
567 tmp_list = tmp_list->next;
572 g_slist_foreach (widgets, (GFunc)g_object_unref, NULL);
574 g_slist_free (widgets);
575 g_slist_free (groups);
581 get_dimension (GtkWidget *widget,
582 GtkSizeGroupMode mode)
584 GSList *widgets = NULL;
585 GSList *groups = NULL;
588 add_widget_to_closure (widget, mode, &groups, &widgets);
592 result = get_base_dimension (widget, mode);
596 GtkSizeGroup *group = groups->data;
598 if (mode == GTK_SIZE_GROUP_HORIZONTAL && group->have_width)
599 result = group->requisition.width;
600 else if (mode == GTK_SIZE_GROUP_VERTICAL && group->have_height)
601 result = group->requisition.height;
604 g_slist_free (widgets);
605 g_slist_free (groups);
611 get_fast_child_requisition (GtkWidget *widget,
612 GtkRequisition *requisition)
614 GtkWidgetAuxInfo *aux_info = _gtk_widget_get_aux_info (widget, FALSE);
616 *requisition = widget->requisition;
620 if (aux_info->width > 0)
621 requisition->width = aux_info->width;
622 if (aux_info && aux_info->height > 0)
623 requisition->height = aux_info->height;
628 * _gtk_size_group_get_child_requisition:
629 * @widget: a #GtkWidget
630 * @requisition: location to store computed requisition.
632 * Retrieve the "child requisition" of the widget, taking account grouping
633 * of the widget's requisition with other widgets.
636 _gtk_size_group_get_child_requisition (GtkWidget *widget,
637 GtkRequisition *requisition)
641 if (get_size_groups (widget))
643 requisition->width = get_dimension (widget, GTK_SIZE_GROUP_HORIZONTAL);
644 requisition->height = get_dimension (widget, GTK_SIZE_GROUP_VERTICAL);
646 /* Only do the full computation if we actually have size groups */
649 get_fast_child_requisition (widget, requisition);
654 * _gtk_size_group_compute_requisition:
655 * @widget: a #GtkWidget
656 * @requisition: location to store computed requisition.
658 * Compute the requisition of a widget taking into account grouping of
659 * the widget's requisition with other widgets.
662 _gtk_size_group_compute_requisition (GtkWidget *widget,
663 GtkRequisition *requisition)
668 if (get_size_groups (widget))
670 /* Only do the full computation if we actually have size groups */
672 width = compute_dimension (widget, GTK_SIZE_GROUP_HORIZONTAL);
673 height = compute_dimension (widget, GTK_SIZE_GROUP_VERTICAL);
677 requisition->width = width;
678 requisition->height = height;
683 do_size_request (widget);
686 get_fast_child_requisition (widget, requisition);
691 * _gtk_size_group_queue_resize:
692 * @widget: a #GtkWidget
694 * Queue a resize on a widget, and on all other widgets grouped with this widget.
697 _gtk_size_group_queue_resize (GtkWidget *widget)
699 queue_resize_on_widget (widget, TRUE);
702 #define __GTK_SIZE_GROUP_C__
703 #include "gtkaliasdef.c"