X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;f=gtk%2Fgtksizegroup.c;h=e31dfa84eb1755b30b53f228eeff280941a16989;hb=6767541ead7cc150d1dd066d3b84d85559500c28;hp=be24f64869c2233cc2c7be4b69d373eae1fcacc4;hpb=2cc059a0e7ee382606351355780b53ae6a7229d9;p=~andy%2Fgtk diff --git a/gtk/gtksizegroup.c b/gtk/gtksizegroup.c index be24f6486..e31dfa84e 100644 --- a/gtk/gtksizegroup.c +++ b/gtk/gtksizegroup.c @@ -13,9 +13,7 @@ * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. + * License along with this library. If not, see . */ #include "config.h" @@ -24,21 +22,99 @@ #include "gtkbuildable.h" #include "gtkcontainer.h" #include "gtkintl.h" +#include "gtktypebuiltins.h" #include "gtkprivate.h" #include "gtksizegroup-private.h" +#include "gtksizerequestcacheprivate.h" #include "gtkwidgetprivate.h" +#include "gtkcontainerprivate.h" + + +/** + * SECTION:gtksizegroup + * @Short_description: Grouping widgets so they request the same size + * @Title: GtkSizeGroup + * + * #GtkSizeGroup provides a mechanism for grouping a number of widgets + * together so they all request the same amount of space. This is + * typically useful when you want a column of widgets to have the same + * size, but you can't use a #GtkGrid widget. + * + * In detail, the size requested for each widget in a #GtkSizeGroup is + * the maximum of the sizes that would have been requested for each + * widget in the size group if they were not in the size group. The mode + * of the size group (see gtk_size_group_set_mode()) determines whether + * this applies to the horizontal size, the vertical size, or both sizes. + * + * Note that size groups only affect the amount of space requested, not + * the size that the widgets finally receive. If you want the widgets in + * a #GtkSizeGroup to actually be the same size, you need to pack them in + * such a way that they get the size they request and not more. For + * example, if you are packing your widgets into a table, you would not + * include the %GTK_FILL flag. + * + * #GtkSizeGroup objects are referenced by each widget in the size group, + * so once you have added all widgets to a #GtkSizeGroup, you can drop + * the initial reference to the size group with g_object_unref(). If the + * widgets in the size group are subsequently destroyed, then they will + * be removed from the size group and drop their references on the size + * group; when all widgets have been removed, the size group will be + * freed. + * + * Widgets can be part of multiple size groups; GTK+ will compute the + * horizontal size of a widget from the horizontal requisition of all + * widgets that can be reached from the widget by a chain of size groups + * of type %GTK_SIZE_GROUP_HORIZONTAL or %GTK_SIZE_GROUP_BOTH, and the + * vertical size from the vertical requisition of all widgets that can be + * reached from the widget by a chain of size groups of type + * %GTK_SIZE_GROUP_VERTICAL or %GTK_SIZE_GROUP_BOTH. + * + * Note that only non-contextual sizes of every widget are ever consulted + * by size groups (since size groups have no knowledge of what size a widget + * will be allocated in one dimension, it cannot derive how much height + * a widget will receive for a given width). When grouping widgets that + * trade height for width in mode %GTK_SIZE_GROUP_VERTICAL or %GTK_SIZE_GROUP_BOTH: + * the height for the minimum width will be the requested height for all + * widgets in the group. The same is of course true when horizontally grouping + * width for height widgets. + * + * Widgets that trade height-for-width should set a reasonably large minimum width + * by way of #GtkLabel:width-chars for instance. Widgets with static sizes as well + * as widgets that grow (such as ellipsizing text) need no such considerations. + * + * + * GtkSizeGroup as GtkBuildable + * + * Size groups can be specified in a UI definition by placing an + * <object> element with class="GtkSizeGroup" + * somewhere in the UI definition. The widgets that belong to the + * size group are specified by a <widgets> element that may + * contain multiple <widget> elements, one for each member + * of the size group. The name attribute gives the id of the widget. + * + * + * A UI definition fragment with GtkSizeGroup + * + * GTK_SIZE_GROUP_HORIZONTAL + * + * + * + * + * + * ]]> + * + * + * + */ + struct _GtkSizeGroupPrivate { - GtkRequisition minimum_req; - GtkRequisition natural_req; - GSList *widgets; guint8 mode; - guint have_width : 1; - guint have_height : 1; guint ignore_hidden : 1; }; @@ -57,15 +133,6 @@ static void gtk_size_group_get_property (GObject *object, GValue *value, GParamSpec *pspec); -static void add_group_to_closure (GtkSizeGroup *group, - GtkSizeGroupMode mode, - GSList **groups, - GSList **widgets); -static void add_widget_to_closure (GtkWidget *widget, - GtkSizeGroupMode mode, - GSList **groups, - GSList **widgets); - /* GtkBuildable */ static void gtk_size_group_buildable_init (GtkBuildableIface *iface); static gboolean gtk_size_group_buildable_custom_tag_start (GtkBuildable *buildable, @@ -80,107 +147,62 @@ static void gtk_size_group_buildable_custom_finished (GtkBuildable *buildable, const gchar *tagname, gpointer user_data); -static GQuark size_groups_quark; -static const gchar size_groups_tag[] = "gtk-size-groups"; - -static GQuark visited_quark; -static const gchar visited_tag[] = "gtk-size-group-visited"; - -static GQuark bumping_quark; -static const gchar bumping_tag[] = "gtk-size-group-bumping"; - -static GSList * -get_size_groups (GtkWidget *widget) -{ - return g_object_get_qdata (G_OBJECT (widget), size_groups_quark); -} +G_STATIC_ASSERT (GTK_SIZE_GROUP_HORIZONTAL == (1 << GTK_ORIENTATION_HORIZONTAL)); +G_STATIC_ASSERT (GTK_SIZE_GROUP_VERTICAL == (1 << GTK_ORIENTATION_VERTICAL)); +G_STATIC_ASSERT (GTK_SIZE_GROUP_BOTH == (GTK_SIZE_GROUP_HORIZONTAL | GTK_SIZE_GROUP_VERTICAL)); static void -set_size_groups (GtkWidget *widget, - GSList *groups) +add_widget_to_closure (GHashTable *widgets, + GHashTable *groups, + GtkWidget *widget, + GtkOrientation orientation) { - g_object_set_qdata (G_OBJECT (widget), size_groups_quark, groups); -} + GSList *tmp_groups, *tmp_widgets; + gboolean hidden; -static void -mark_visited (gpointer object) -{ - g_object_set_qdata (object, visited_quark, "visited"); -} + if (g_hash_table_lookup (widgets, widget)) + return; -static void -mark_unvisited (gpointer object) -{ - g_object_set_qdata (object, visited_quark, NULL); -} + g_hash_table_add (widgets, widget); + hidden = !gtk_widget_is_visible (widget); -static gboolean -is_visited (gpointer object) -{ - return g_object_get_qdata (object, visited_quark) != NULL; -} + for (tmp_groups = _gtk_widget_get_sizegroups (widget); tmp_groups; tmp_groups = tmp_groups->next) + { + GtkSizeGroup *tmp_group = tmp_groups->data; + GtkSizeGroupPrivate *tmp_priv = tmp_group->priv; -static void -mark_bumping (gpointer object, gboolean bumping) -{ - g_object_set_qdata (object, bumping_quark, bumping ? "bumping" : NULL); -} + if (g_hash_table_lookup (groups, tmp_group)) + continue; -static gboolean -is_bumping (gpointer object) -{ - return g_object_get_qdata (object, bumping_quark) != NULL; -} + if (tmp_priv->ignore_hidden && hidden) + continue; -static void -add_group_to_closure (GtkSizeGroup *group, - GtkSizeGroupMode mode, - GSList **groups, - GSList **widgets) -{ - GtkSizeGroupPrivate *priv = group->priv; - GSList *tmp_widgets; - - *groups = g_slist_prepend (*groups, group); - mark_visited (group); + if (!(tmp_priv->mode & (1 << orientation))) + continue; - tmp_widgets = priv->widgets; - while (tmp_widgets) - { - GtkWidget *tmp_widget = tmp_widgets->data; - - if (!is_visited (tmp_widget)) - add_widget_to_closure (tmp_widget, mode, groups, widgets); - - tmp_widgets = tmp_widgets->next; + g_hash_table_add (groups, tmp_group); + + for (tmp_widgets = tmp_priv->widgets; tmp_widgets; tmp_widgets = tmp_widgets->next) + add_widget_to_closure (widgets, groups, tmp_widgets->data, orientation); } } -static void -add_widget_to_closure (GtkWidget *widget, - GtkSizeGroupMode mode, - GSList **groups, - GSList **widgets) +GHashTable * +_gtk_size_group_get_widget_peers (GtkWidget *for_widget, + GtkOrientation orientation) { - GSList *tmp_groups; + GHashTable *widgets, *groups; - *widgets = g_slist_prepend (*widgets, widget); - mark_visited (widget); + widgets = g_hash_table_new (g_direct_hash, g_direct_equal); + groups = g_hash_table_new (g_direct_hash, g_direct_equal); - tmp_groups = get_size_groups (widget); - while (tmp_groups) - { - GtkSizeGroup *tmp_group = tmp_groups->data; - GtkSizeGroupPrivate *tmp_priv = tmp_group->priv; + add_widget_to_closure (widgets, groups, for_widget, orientation); - if ((tmp_priv->mode == GTK_SIZE_GROUP_BOTH || tmp_priv->mode == mode) && - !is_visited (tmp_group)) - add_group_to_closure (tmp_group, mode, groups, widgets); + g_hash_table_unref (groups); - tmp_groups = tmp_groups->next; - } + return widgets; } - + static void real_queue_resize (GtkWidget *widget, GtkQueueResizeFlags flags) @@ -188,8 +210,7 @@ real_queue_resize (GtkWidget *widget, GtkWidget *container; _gtk_widget_set_alloc_needed (widget, TRUE); - _gtk_widget_set_width_request_needed (widget, TRUE); - _gtk_widget_set_height_request_needed (widget, TRUE); + _gtk_size_request_cache_clear (_gtk_widget_peek_request_cache (widget)); container = gtk_widget_get_parent (widget); if (!container && @@ -205,35 +226,19 @@ real_queue_resize (GtkWidget *widget, } } -static void -reset_group_sizes (GSList *groups) -{ - GSList *tmp_list = groups; - while (tmp_list) - { - GtkSizeGroup *tmp_group = tmp_list->data; - GtkSizeGroupPrivate *tmp_priv = tmp_group->priv; - - tmp_priv->have_width = FALSE; - tmp_priv->have_height = FALSE; - - tmp_list = tmp_list->next; - } -} - static void queue_resize_on_widget (GtkWidget *widget, gboolean check_siblings, GtkQueueResizeFlags flags) { GtkWidget *parent = widget; - GSList *tmp_list; while (parent) { GSList *widget_groups; - GSList *groups; - GSList *widgets; + GHashTable *widgets; + GHashTableIter iter; + gpointer current; if (widget == parent && !check_siblings) { @@ -242,7 +247,7 @@ queue_resize_on_widget (GtkWidget *widget, continue; } - widget_groups = get_size_groups (parent); + widget_groups = _gtk_widget_get_sizegroups (parent); if (!widget_groups) { if (widget == parent) @@ -252,65 +257,45 @@ queue_resize_on_widget (GtkWidget *widget, continue; } - groups = NULL; - widgets = NULL; - - add_widget_to_closure (parent, GTK_SIZE_GROUP_HORIZONTAL, &groups, &widgets); - g_slist_foreach (widgets, (GFunc)mark_unvisited, NULL); - g_slist_foreach (groups, (GFunc)mark_unvisited, NULL); - - reset_group_sizes (groups); - - tmp_list = widgets; - while (tmp_list) + widgets = _gtk_size_group_get_widget_peers (parent, GTK_ORIENTATION_HORIZONTAL); + + g_hash_table_iter_init (&iter, widgets); + while (g_hash_table_iter_next (&iter, ¤t, NULL)) { - if (tmp_list->data == parent) + if (current == parent) { if (widget == parent) real_queue_resize (parent, flags); } - else if (tmp_list->data == widget) + else if (current == widget) { g_warning ("A container and its child are part of this SizeGroup"); } else - queue_resize_on_widget (tmp_list->data, FALSE, flags); - - tmp_list = tmp_list->next; + queue_resize_on_widget (current, FALSE, flags); } - g_slist_free (widgets); - g_slist_free (groups); - - groups = NULL; - widgets = NULL; - - add_widget_to_closure (parent, GTK_SIZE_GROUP_VERTICAL, &groups, &widgets); - g_slist_foreach (widgets, (GFunc)mark_unvisited, NULL); - g_slist_foreach (groups, (GFunc)mark_unvisited, NULL); - - reset_group_sizes (groups); - - tmp_list = widgets; - while (tmp_list) + g_hash_table_destroy (widgets); + + widgets = _gtk_size_group_get_widget_peers (parent, GTK_ORIENTATION_VERTICAL); + + g_hash_table_iter_init (&iter, widgets); + while (g_hash_table_iter_next (&iter, ¤t, NULL)) { - if (tmp_list->data == parent) + if (current == parent) { if (widget == parent) real_queue_resize (parent, flags); } - else if (tmp_list->data == widget) + else if (current == widget) { g_warning ("A container and its child are part of this SizeGroup"); } else - queue_resize_on_widget (tmp_list->data, FALSE, flags); - - tmp_list = tmp_list->next; + queue_resize_on_widget (current, FALSE, flags); } - g_slist_free (widgets); - g_slist_free (groups); + g_hash_table_destroy (widgets); parent = gtk_widget_get_parent (parent); } @@ -325,17 +310,6 @@ queue_resize_on_group (GtkSizeGroup *size_group) queue_resize_on_widget (priv->widgets->data, TRUE, 0); } -static void -initialize_size_group_quarks (void) -{ - if (!size_groups_quark) - { - size_groups_quark = g_quark_from_static_string (size_groups_tag); - visited_quark = g_quark_from_static_string (visited_tag); - bumping_quark = g_quark_from_static_string (bumping_tag); - } -} - static void gtk_size_group_class_init (GtkSizeGroupClass *klass) { @@ -371,8 +345,6 @@ gtk_size_group_class_init (GtkSizeGroupClass *klass) GTK_PARAM_READWRITE)); g_type_class_add_private (klass, sizeof (GtkSizeGroupPrivate)); - - initialize_size_group_quarks (); } static void @@ -387,9 +359,7 @@ gtk_size_group_init (GtkSizeGroup *size_group) priv->widgets = NULL; priv->mode = GTK_SIZE_GROUP_HORIZONTAL; - priv->have_width = 0; - priv->have_height = 0; - priv->ignore_hidden = 0; + priv->ignore_hidden = FALSE; } static void @@ -474,10 +444,10 @@ gtk_size_group_new (GtkSizeGroupMode mode) * * Sets the #GtkSizeGroupMode of the size group. The mode of the size * group determines whether the widgets in the size group should - * all have the same horizontal requisition (%GTK_SIZE_GROUP_MODE_HORIZONTAL) - * all have the same vertical requisition (%GTK_SIZE_GROUP_MODE_VERTICAL), + * all have the same horizontal requisition (%GTK_SIZE_GROUP_HORIZONTAL) + * all have the same vertical requisition (%GTK_SIZE_GROUP_VERTICAL), * or should all have the same requisition in both directions - * (%GTK_SIZE_GROUP_MODE_BOTH). + * (%GTK_SIZE_GROUP_BOTH). **/ void gtk_size_group_set_mode (GtkSizeGroup *size_group, @@ -599,12 +569,11 @@ gtk_size_group_add_widget (GtkSizeGroup *size_group, priv = size_group->priv; - groups = get_size_groups (widget); + groups = _gtk_widget_get_sizegroups (widget); if (!g_slist_find (groups, size_group)) { - groups = g_slist_prepend (groups, size_group); - set_size_groups (widget, groups); + _gtk_widget_add_sizegroup (widget, size_group); priv->widgets = g_slist_prepend (priv->widgets, widget); @@ -630,7 +599,6 @@ gtk_size_group_remove_widget (GtkSizeGroup *size_group, GtkWidget *widget) { GtkSizeGroupPrivate *priv; - GSList *groups; g_return_if_fail (GTK_IS_SIZE_GROUP (size_group)); g_return_if_fail (GTK_IS_WIDGET (widget)); @@ -643,9 +611,7 @@ gtk_size_group_remove_widget (GtkSizeGroup *size_group, gtk_size_group_widget_destroyed, size_group); - groups = get_size_groups (widget); - groups = g_slist_remove (groups, size_group); - set_size_groups (widget, groups); + _gtk_widget_remove_sizegroup (widget, size_group); priv->widgets = g_slist_remove (priv->widgets, widget); queue_resize_on_group (size_group); @@ -671,143 +637,6 @@ gtk_size_group_get_widgets (GtkSizeGroup *size_group) return size_group->priv->widgets; } -static void -compute_dimension (GtkWidget *widget, - GtkSizeGroupMode mode, - gint *minimum, /* in-out */ - gint *natural) /* in-out */ -{ - GSList *widgets = NULL; - GSList *groups = NULL; - GSList *tmp_list; - gint min_result = 0, nat_result = 0; - - add_widget_to_closure (widget, mode, &groups, &widgets); - - g_slist_foreach (widgets, (GFunc)mark_unvisited, NULL); - g_slist_foreach (groups, (GFunc)mark_unvisited, NULL); - - g_slist_foreach (widgets, (GFunc)g_object_ref, NULL); - - if (!groups) - { - min_result = *minimum; - nat_result = *natural; - } - else - { - GtkSizeGroup *group = groups->data; - GtkSizeGroupPrivate *priv = group->priv; - - if (mode == GTK_SIZE_GROUP_HORIZONTAL && priv->have_width) - { - min_result = priv->minimum_req.width; - nat_result = priv->natural_req.width; - } - else if (mode == GTK_SIZE_GROUP_VERTICAL && priv->have_height) - { - min_result = priv->minimum_req.height; - nat_result = priv->natural_req.height; - } - else - { - tmp_list = widgets; - while (tmp_list) - { - GtkWidget *tmp_widget = tmp_list->data; - gint min_dimension, nat_dimension; - - if (tmp_widget == widget) - { - min_dimension = *minimum; - nat_dimension = *natural; - } - else - { - if (mode == GTK_SIZE_GROUP_HORIZONTAL) - gtk_widget_get_preferred_width (tmp_widget, &min_dimension, &nat_dimension); - else - gtk_widget_get_preferred_height (tmp_widget, &min_dimension, &nat_dimension); - } - - if (gtk_widget_get_mapped (tmp_widget) || !priv->ignore_hidden) - { - min_result = MAX (min_result, min_dimension); - nat_result = MAX (nat_result, nat_dimension); - } - - tmp_list = tmp_list->next; - } - - tmp_list = groups; - while (tmp_list) - { - GtkSizeGroup *tmp_group = tmp_list->data; - GtkSizeGroupPrivate *tmp_priv = tmp_group->priv; - - if (mode == GTK_SIZE_GROUP_HORIZONTAL) - { - tmp_priv->have_width = TRUE; - tmp_priv->minimum_req.width = min_result; - tmp_priv->natural_req.width = nat_result; - } - else - { - tmp_priv->have_height = TRUE; - - tmp_priv->minimum_req.height = min_result; - tmp_priv->natural_req.height = nat_result; - } - - tmp_list = tmp_list->next; - } - } - } - - g_slist_foreach (widgets, (GFunc)g_object_unref, NULL); - - g_slist_free (widgets); - g_slist_free (groups); - - *minimum = min_result; - *natural = nat_result; -} - -/** - * _gtk_size_group_bump_requisition: - * @widget: a #GtkWidget - * @mode: either %GTK_SIZE_GROUP_HORIZONTAL or %GTK_SIZE_GROUP_VERTICAL, depending - * on the dimension in which to bump the size. - * @minimum: a pointer to the widget's minimum size - * @natural: a pointer to the widget's natural size - * - * Refreshes the sizegroup while returning the groups requested - * value in the dimension @mode. - * - * This function is used both to update sizegroup minimum and natural size - * information and widget minimum and natural sizes in multiple passes from - * the size request apis. - */ -void -_gtk_size_group_bump_requisition (GtkWidget *widget, - GtkSizeGroupMode mode, - gint *minimum, - gint *natural) -{ - if (!is_bumping (widget)) - { - /* Avoid recursion here */ - mark_bumping (widget, TRUE); - - if (get_size_groups (widget)) - compute_dimension (widget, mode, minimum, natural); - - mark_bumping (widget, FALSE); - } -} - - - /** * _gtk_size_group_queue_resize: * @widget: a #GtkWidget @@ -818,8 +647,6 @@ void _gtk_size_group_queue_resize (GtkWidget *widget, GtkQueueResizeFlags flags) { - initialize_size_group_quarks (); - queue_resize_on_widget (widget, TRUE, flags); }