gtk2/gtk/gtksizegroup.c
Tristan Van Berkom b3303727ad Fixed regression in GtkImage size requests
Fixed gtksizegroup.c:do_size_request() to never force an initial requisition
as some widgets expect it to remain unchanged across resizes (GtkImage
with pixbufs/filenames assigned is one of these cases).
2010-04-08 22:05:36 -04:00

1076 lines
29 KiB
C

/* GTK - The GIMP Toolkit
* gtksizegroup.c:
* Copyright (C) 2001 Red Hat Software
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* 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.
*/
#include "config.h"
#include <string.h>
#include "gtkcontainer.h"
#include "gtkintl.h"
#include "gtkprivate.h"
#include "gtksizegroup.h"
#include "gtkbuildable.h"
#include "gtkextendedlayout.h"
#include "gtkalias.h"
#define GTK_SIZE_GROUP_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GTK_TYPE_SIZE_GROUP, GtkSizeGroupPrivate))
typedef struct _GtkSizeGroupPrivate GtkSizeGroupPrivate;
struct _GtkSizeGroupPrivate
{
GtkRequisition natural_size;
};
enum {
PROP_0,
PROP_MODE,
PROP_IGNORE_HIDDEN
};
static void gtk_size_group_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec);
static void gtk_size_group_get_property (GObject *object,
guint prop_id,
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,
GtkBuilder *builder,
GObject *child,
const gchar *tagname,
GMarkupParser *parser,
gpointer *data);
static void gtk_size_group_buildable_custom_finished (GtkBuildable *buildable,
GtkBuilder *builder,
GObject *child,
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 GSList *
get_size_groups (GtkWidget *widget)
{
return g_object_get_qdata (G_OBJECT (widget), size_groups_quark);
}
static void
set_size_groups (GtkWidget *widget,
GSList *groups)
{
g_object_set_qdata (G_OBJECT (widget), size_groups_quark, groups);
}
static void
mark_visited (gpointer object)
{
g_object_set_qdata (object, visited_quark, "visited");
}
static void
mark_unvisited (gpointer object)
{
g_object_set_qdata (object, visited_quark, NULL);
}
static gboolean
is_visited (gpointer object)
{
return g_object_get_qdata (object, visited_quark) != NULL;
}
static void
add_group_to_closure (GtkSizeGroup *group,
GtkSizeGroupMode mode,
GSList **groups,
GSList **widgets)
{
GSList *tmp_widgets;
*groups = g_slist_prepend (*groups, group);
mark_visited (group);
tmp_widgets = group->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;
}
}
static void
add_widget_to_closure (GtkWidget *widget,
GtkSizeGroupMode mode,
GSList **groups,
GSList **widgets)
{
GSList *tmp_groups;
*widgets = g_slist_prepend (*widgets, widget);
mark_visited (widget);
tmp_groups = get_size_groups (widget);
while (tmp_groups)
{
GtkSizeGroup *tmp_group = tmp_groups->data;
if ((tmp_group->mode == GTK_SIZE_GROUP_BOTH || tmp_group->mode == mode) &&
!is_visited (tmp_group))
add_group_to_closure (tmp_group, mode, groups, widgets);
tmp_groups = tmp_groups->next;
}
}
static void
real_queue_resize (GtkWidget *widget)
{
GTK_PRIVATE_SET_FLAG (widget, GTK_ALLOC_NEEDED);
GTK_PRIVATE_SET_FLAG (widget, GTK_REQUEST_NEEDED);
if (widget->parent)
_gtk_container_queue_resize (GTK_CONTAINER (widget->parent));
else if (gtk_widget_is_toplevel (widget) && GTK_IS_CONTAINER (widget))
_gtk_container_queue_resize (GTK_CONTAINER (widget));
}
static void
reset_group_sizes (GSList *groups)
{
GSList *tmp_list = groups;
while (tmp_list)
{
GtkSizeGroup *tmp_group = tmp_list->data;
tmp_group->have_width = FALSE;
tmp_group->have_height = FALSE;
tmp_list = tmp_list->next;
}
}
static void
queue_resize_on_widget (GtkWidget *widget,
gboolean check_siblings)
{
GtkWidget *parent = widget;
GSList *tmp_list;
while (parent)
{
GSList *widget_groups;
GSList *groups;
GSList *widgets;
if (widget == parent && !check_siblings)
{
real_queue_resize (widget);
parent = parent->parent;
continue;
}
widget_groups = get_size_groups (parent);
if (!widget_groups)
{
if (widget == parent)
real_queue_resize (widget);
parent = parent->parent;
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)
{
if (tmp_list->data == parent)
{
if (widget == parent)
real_queue_resize (parent);
}
else if (tmp_list->data == widget)
{
g_warning ("A container and its child are part of this SizeGroup");
}
else
queue_resize_on_widget (tmp_list->data, FALSE);
tmp_list = tmp_list->next;
}
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)
{
if (tmp_list->data == parent)
{
if (widget == parent)
real_queue_resize (parent);
}
else if (tmp_list->data == widget)
{
g_warning ("A container and its child are part of this SizeGroup");
}
else
queue_resize_on_widget (tmp_list->data, FALSE);
tmp_list = tmp_list->next;
}
g_slist_free (widgets);
g_slist_free (groups);
parent = parent->parent;
}
}
static void
queue_resize_on_group (GtkSizeGroup *size_group)
{
if (size_group->widgets)
queue_resize_on_widget (size_group->widgets->data, TRUE);
}
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);
}
}
static void
gtk_size_group_class_init (GtkSizeGroupClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
gobject_class->set_property = gtk_size_group_set_property;
gobject_class->get_property = gtk_size_group_get_property;
g_object_class_install_property (gobject_class,
PROP_MODE,
g_param_spec_enum ("mode",
P_("Mode"),
P_("The directions in which the size group affects the requested sizes"
" of its component widgets"),
GTK_TYPE_SIZE_GROUP_MODE,
GTK_SIZE_GROUP_HORIZONTAL, GTK_PARAM_READWRITE));
/**
* GtkSizeGroup:ignore-hidden:
*
* If %TRUE, unmapped widgets are ignored when determining
* the size of the group.
*
* Since: 2.8
*/
g_object_class_install_property (gobject_class,
PROP_IGNORE_HIDDEN,
g_param_spec_boolean ("ignore-hidden",
P_("Ignore hidden"),
P_("If TRUE, unmapped widgets are ignored "
"when determining the size of the group"),
FALSE,
GTK_PARAM_READWRITE));
initialize_size_group_quarks ();
g_type_class_add_private (klass, sizeof (GtkSizeGroupPrivate));
}
static void
gtk_size_group_init (GtkSizeGroup *size_group)
{
size_group->widgets = NULL;
size_group->mode = GTK_SIZE_GROUP_HORIZONTAL;
size_group->have_width = 0;
size_group->have_height = 0;
size_group->ignore_hidden = 0;
}
static void
gtk_size_group_buildable_init (GtkBuildableIface *iface)
{
iface->custom_tag_start = gtk_size_group_buildable_custom_tag_start;
iface->custom_finished = gtk_size_group_buildable_custom_finished;
}
G_DEFINE_TYPE_WITH_CODE (GtkSizeGroup, gtk_size_group, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
gtk_size_group_buildable_init))
static void
gtk_size_group_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GtkSizeGroup *size_group = GTK_SIZE_GROUP (object);
switch (prop_id)
{
case PROP_MODE:
gtk_size_group_set_mode (size_group, g_value_get_enum (value));
break;
case PROP_IGNORE_HIDDEN:
gtk_size_group_set_ignore_hidden (size_group, g_value_get_boolean (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gtk_size_group_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GtkSizeGroup *size_group = GTK_SIZE_GROUP (object);
switch (prop_id)
{
case PROP_MODE:
g_value_set_enum (value, size_group->mode);
break;
case PROP_IGNORE_HIDDEN:
g_value_set_boolean (value, size_group->ignore_hidden);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
/**
* gtk_size_group_new:
* @mode: the mode for the new size group.
*
* Create a new #GtkSizeGroup.
* Return value: a newly created #GtkSizeGroup
**/
GtkSizeGroup *
gtk_size_group_new (GtkSizeGroupMode mode)
{
GtkSizeGroup *size_group = g_object_new (GTK_TYPE_SIZE_GROUP, NULL);
size_group->mode = mode;
return size_group;
}
/**
* gtk_size_group_set_mode:
* @size_group: a #GtkSizeGroup
* @mode: the mode to set for the size group.
*
* 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),
* or should all have the same requisition in both directions
* (%GTK_SIZE_GROUP_MODE_BOTH).
**/
void
gtk_size_group_set_mode (GtkSizeGroup *size_group,
GtkSizeGroupMode mode)
{
g_return_if_fail (GTK_IS_SIZE_GROUP (size_group));
if (size_group->mode != mode)
{
if (size_group->mode != GTK_SIZE_GROUP_NONE)
queue_resize_on_group (size_group);
size_group->mode = mode;
if (size_group->mode != GTK_SIZE_GROUP_NONE)
queue_resize_on_group (size_group);
g_object_notify (G_OBJECT (size_group), "mode");
}
}
/**
* gtk_size_group_get_mode:
* @size_group: a #GtkSizeGroup
*
* Gets the current mode of the size group. See gtk_size_group_set_mode().
*
* Return value: the current mode of the size group.
**/
GtkSizeGroupMode
gtk_size_group_get_mode (GtkSizeGroup *size_group)
{
g_return_val_if_fail (GTK_IS_SIZE_GROUP (size_group), GTK_SIZE_GROUP_BOTH);
return size_group->mode;
}
/**
* gtk_size_group_set_ignore_hidden:
* @size_group: a #GtkSizeGroup
* @ignore_hidden: whether unmapped widgets should be ignored
* when calculating the size
*
* Sets whether unmapped widgets should be ignored when
* calculating the size.
*
* Since: 2.8
*/
void
gtk_size_group_set_ignore_hidden (GtkSizeGroup *size_group,
gboolean ignore_hidden)
{
g_return_if_fail (GTK_IS_SIZE_GROUP (size_group));
ignore_hidden = ignore_hidden != FALSE;
if (size_group->ignore_hidden != ignore_hidden)
{
size_group->ignore_hidden = ignore_hidden;
g_object_notify (G_OBJECT (size_group), "ignore-hidden");
}
}
/**
* gtk_size_group_get_ignore_hidden:
* @size_group: a #GtkSizeGroup
*
* Returns if invisible widgets are ignored when calculating the size.
*
* Returns: %TRUE if invisible widgets are ignored.
*
* Since: 2.8
*/
gboolean
gtk_size_group_get_ignore_hidden (GtkSizeGroup *size_group)
{
g_return_val_if_fail (GTK_IS_SIZE_GROUP (size_group), FALSE);
return size_group->ignore_hidden;
}
static void
gtk_size_group_widget_destroyed (GtkWidget *widget,
GtkSizeGroup *size_group)
{
gtk_size_group_remove_widget (size_group, widget);
}
/**
* gtk_size_group_add_widget:
* @size_group: a #GtkSizeGroup
* @widget: the #GtkWidget to add
*
* Adds a widget to a #GtkSizeGroup. In the future, the requisition
* of the widget will be determined as the maximum of its requisition
* and the requisition of the other widgets in the size group.
* Whether this applies horizontally, vertically, or in both directions
* depends on the mode of the size group. See gtk_size_group_set_mode().
*
* When the widget is destroyed or no longer referenced elsewhere, it will
* be removed from the size group.
*/
void
gtk_size_group_add_widget (GtkSizeGroup *size_group,
GtkWidget *widget)
{
GSList *groups;
g_return_if_fail (GTK_IS_SIZE_GROUP (size_group));
g_return_if_fail (GTK_IS_WIDGET (widget));
groups = get_size_groups (widget);
if (!g_slist_find (groups, size_group))
{
groups = g_slist_prepend (groups, size_group);
set_size_groups (widget, groups);
size_group->widgets = g_slist_prepend (size_group->widgets, widget);
g_signal_connect (widget, "destroy",
G_CALLBACK (gtk_size_group_widget_destroyed),
size_group);
g_object_ref (size_group);
}
queue_resize_on_group (size_group);
}
/**
* gtk_size_group_remove_widget:
* @size_group: a #GtkSizeGrup
* @widget: the #GtkWidget to remove
*
* Removes a widget from a #GtkSizeGroup.
**/
void
gtk_size_group_remove_widget (GtkSizeGroup *size_group,
GtkWidget *widget)
{
GSList *groups;
g_return_if_fail (GTK_IS_SIZE_GROUP (size_group));
g_return_if_fail (GTK_IS_WIDGET (widget));
g_return_if_fail (g_slist_find (size_group->widgets, widget));
g_signal_handlers_disconnect_by_func (widget,
gtk_size_group_widget_destroyed,
size_group);
groups = get_size_groups (widget);
groups = g_slist_remove (groups, size_group);
set_size_groups (widget, groups);
size_group->widgets = g_slist_remove (size_group->widgets, widget);
queue_resize_on_group (size_group);
gtk_widget_queue_resize (widget);
g_object_unref (size_group);
}
/**
* gtk_size_group_get_widgets:
* @size_group: a #GtkSizeGrup
*
* Returns the list of widgets associated with @size_group.
*
* Return value: (element-type GtkWidget) (transfer none): a #GSList of
* widgets. The list is owned by GTK+ and should not be modified.
*
* Since: 2.10
**/
GSList *
gtk_size_group_get_widgets (GtkSizeGroup *size_group)
{
return size_group->widgets;
}
static void
get_base_dimensions (GtkWidget *widget,
GtkSizeGroupMode mode,
gint *minimum_size,
gint *natural_size)
{
GtkWidgetAuxInfo *aux_info = _gtk_widget_get_aux_info (widget, FALSE);
if (mode == GTK_SIZE_GROUP_HORIZONTAL)
{
if (minimum_size)
{
if (aux_info && aux_info->width > 0)
*minimum_size = aux_info->width;
else
*minimum_size = widget->requisition.width;
}
if (natural_size)
{
if (aux_info)
*natural_size = aux_info->natural_size.width;
else
*natural_size = widget->requisition.width;
}
}
else
{
if (minimum_size)
{
if (aux_info && aux_info->height > 0)
*minimum_size = aux_info->height;
else
*minimum_size = widget->requisition.height;
}
if (natural_size)
{
if (aux_info)
*natural_size = aux_info->natural_size.height;
else
*natural_size = widget->requisition.height;
}
}
}
static void
do_size_request (GtkWidget *widget)
{
if (GTK_WIDGET_REQUEST_NEEDED (widget))
{
GtkWidgetAuxInfo *aux_info = _gtk_widget_get_aux_info (widget, TRUE);
GtkRequisition extended_minimum;
gtk_widget_ensure_style (widget);
GTK_PRIVATE_UNSET_FLAG (widget, GTK_REQUEST_NEEDED);
/* First, allow client code to; extended classes or signal connections; to
* modify the initial size request.
*
* Note here that there is no convention of filling the argument or widget->requisition,
* so we have no choice but to fire size request with this pointer.
*/
g_signal_emit_by_name (widget, "size-request", &widget->requisition);
/* Now get the extended layout minimum and natural size
*/
extended_minimum.width = 0;
extended_minimum.height = 0;
GTK_EXTENDED_LAYOUT_GET_IFACE
(widget)->get_desired_size (GTK_EXTENDED_LAYOUT (widget),
&extended_minimum,
&aux_info->natural_size);
/* Base the base widget requisition on both the size-requst and the extended layout size
*/
widget->requisition.width = MAX (widget->requisition.width, extended_minimum.width);
widget->requisition.height = MAX (widget->requisition.height, extended_minimum.height);
/* Additionally allow a "size-request" to overflow the natural size.
*/
aux_info->natural_size.width = MAX (aux_info->natural_size.width, widget->requisition.width);
aux_info->natural_size.height = MAX (aux_info->natural_size.height, widget->requisition.height);
/* Assert that pure extended layout cases return initial minimum sizes smaller or equal
* to their possible natural size.
*
* Note that this only determines the return of gtk_widget_get_desired_size() and caches
* the initial hints. Height for width cases will further be addressed in containers
* using gtk_extended_layout_get_height_for_width().
*/
g_assert (widget->requisition.width <= aux_info->natural_size.width);
g_assert (widget->requisition.height <= aux_info->natural_size.height);
}
}
static void
compute_base_dimensions (GtkWidget *widget,
GtkSizeGroupMode mode,
gint *minimum_size,
gint *natural_size)
{
do_size_request (widget);
get_base_dimensions (widget, mode, minimum_size, natural_size);
}
static void
compute_dimension (GtkWidget *widget,
GtkSizeGroupMode mode,
gint *minimum_size,
gint *natural_size)
{
GSList *widgets = NULL;
GSList *groups = NULL;
GSList *tmp_list;
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)
{
compute_base_dimensions (widget, mode, minimum_size, natural_size);
}
else
{
GtkSizeGroup *group = groups->data;
GtkSizeGroupPrivate *priv = GTK_SIZE_GROUP_GET_PRIVATE (group);
gint result_minimum_size = 0;
gint result_natural_size = 0;
if (mode == GTK_SIZE_GROUP_HORIZONTAL && group->have_width)
{
result_minimum_size = group->requisition.width;
result_natural_size = priv->natural_size.width;
}
else if (mode == GTK_SIZE_GROUP_VERTICAL && group->have_height)
{
result_minimum_size = group->requisition.height;
result_natural_size = priv->natural_size.height;
}
else
{
tmp_list = widgets;
while (tmp_list)
{
GtkWidget *tmp_widget = tmp_list->data;
gint tmp_widget_minimum_size;
gint tmp_widget_natural_size;
compute_base_dimensions (tmp_widget, mode,
&tmp_widget_minimum_size,
&tmp_widget_natural_size);
if (gtk_widget_get_mapped (tmp_widget) || !group->ignore_hidden)
{
if (result_minimum_size < tmp_widget_minimum_size)
result_minimum_size = tmp_widget_minimum_size;
if (result_natural_size < tmp_widget_natural_size)
result_natural_size = tmp_widget_natural_size;
}
tmp_list = tmp_list->next;
}
tmp_list = groups;
while (tmp_list)
{
GtkSizeGroup *tmp_group = tmp_list->data;
GtkSizeGroupPrivate *tmp_priv = GTK_SIZE_GROUP_GET_PRIVATE (tmp_group);
if (mode == GTK_SIZE_GROUP_HORIZONTAL)
{
tmp_group->have_width = TRUE;
tmp_group->requisition.width = result_minimum_size;
tmp_priv->natural_size.width = result_natural_size;
}
else
{
tmp_group->have_height = TRUE;
tmp_group->requisition.height = result_minimum_size;
tmp_priv->natural_size.height = result_natural_size;
}
tmp_list = tmp_list->next;
}
}
if (minimum_size)
*minimum_size = result_minimum_size;
if (natural_size)
*natural_size = result_natural_size;
}
g_slist_foreach (widgets, (GFunc)g_object_unref, NULL);
g_slist_free (widgets);
g_slist_free (groups);
}
static void
get_dimensions (GtkWidget *widget,
GtkSizeGroupMode mode,
gint *minimum_size,
gint *natural_size)
{
GSList *widgets = NULL;
GSList *groups = NULL;
add_widget_to_closure (widget, mode, &groups, &widgets);
g_slist_foreach (widgets, (GFunc)mark_unvisited, NULL);
g_slist_foreach (groups, (GFunc)mark_unvisited, NULL);
if (!groups)
{
get_base_dimensions (widget, mode, minimum_size, natural_size);
}
else
{
GtkSizeGroup *group = groups->data;
GtkSizeGroupPrivate *priv = GTK_SIZE_GROUP_GET_PRIVATE (group);
if (mode == GTK_SIZE_GROUP_HORIZONTAL && group->have_width)
{
if (minimum_size)
*minimum_size = group->requisition.width;
if (natural_size)
*natural_size = priv->natural_size.width;
}
else if (mode == GTK_SIZE_GROUP_VERTICAL && group->have_height)
{
if (minimum_size)
*minimum_size = group->requisition.height;
if (natural_size)
*natural_size = priv->natural_size.height;
}
}
g_slist_free (widgets);
g_slist_free (groups);
}
static void
get_fast_size (GtkWidget *widget,
GtkRequisition *minimum_size,
GtkRequisition *natural_size)
{
GtkWidgetAuxInfo *aux_info = _gtk_widget_get_aux_info (widget, FALSE);
if (minimum_size)
{
*minimum_size = widget->requisition;
if (aux_info)
{
if (aux_info->width > 0)
minimum_size->width = aux_info->width;
if (aux_info->height > 0)
minimum_size->height = aux_info->height;
}
}
if (natural_size)
{
if (aux_info)
{
*natural_size = aux_info->natural_size;
/* Explicit size request sets the baseline for natural size
* as well as the minimum size
*/
if (aux_info->width > natural_size->width)
natural_size->width = aux_info->width;
if (aux_info->height > natural_size->height)
natural_size->height = aux_info->height;
}
else
*natural_size = widget->requisition;
}
}
/**
* _gtk_size_group_get_child_requisition:
* @widget: a #GtkWidget
* @requisition: location to store computed requisition.
*
* Retrieve the "child requisition" of the widget, taking account grouping
* of the widget's requisition with other widgets.
*
* Deprecated: 3.0: Use _gtk_size_group_compute_desired_size() instead
**/
void
_gtk_size_group_get_child_requisition (GtkWidget *widget,
GtkRequisition *requisition)
{
initialize_size_group_quarks ();
if (requisition)
{
if (get_size_groups (widget))
{
get_dimensions (widget, GTK_SIZE_GROUP_HORIZONTAL, &requisition->width, NULL);
get_dimensions (widget, GTK_SIZE_GROUP_VERTICAL, &requisition->height, NULL);
/* Only do the full computation if we actually have size groups */
}
else
get_fast_size (widget, requisition, NULL);
}
}
/**
* _gtk_size_group_compute_desired_size:
* @widget: a #GtkWidget
* @minimum_size: location to store computed minimum size
* @natural_size: location to store computed natural size
*
* Compute the desired size of a widget taking into account grouping of
* the widget's requisition with other widgets.
**/
void
_gtk_size_group_compute_desired_size (GtkWidget *widget,
GtkRequisition *minimum_size,
GtkRequisition *natural_size)
{
initialize_size_group_quarks ();
if (get_size_groups (widget))
{
/* Only do the full computation if we actually have size groups */
compute_dimension (widget, GTK_SIZE_GROUP_HORIZONTAL,
minimum_size ? &minimum_size->width : NULL,
natural_size ? &natural_size->width : NULL);
compute_dimension (widget, GTK_SIZE_GROUP_VERTICAL,
minimum_size ? &minimum_size->height : NULL,
natural_size ? &natural_size->height : NULL);
}
else
{
do_size_request (widget);
get_fast_size (widget, minimum_size, natural_size);
}
}
/**
* _gtk_size_group_queue_resize:
* @widget: a #GtkWidget
*
* Queue a resize on a widget, and on all other widgets grouped with this widget.
**/
void
_gtk_size_group_queue_resize (GtkWidget *widget)
{
initialize_size_group_quarks ();
queue_resize_on_widget (widget, TRUE);
}
typedef struct {
GObject *object;
GSList *items;
} GSListSubParserData;
static void
size_group_start_element (GMarkupParseContext *context,
const gchar *element_name,
const gchar **names,
const gchar **values,
gpointer user_data,
GError **error)
{
guint i;
GSListSubParserData *data = (GSListSubParserData*)user_data;
if (strcmp (element_name, "widget") == 0)
for (i = 0; names[i]; i++)
if (strcmp (names[i], "name") == 0)
data->items = g_slist_prepend (data->items, g_strdup (values[i]));
else if (strcmp (element_name, "widgets") == 0)
return;
else
g_warning ("Unsupported type tag for GtkSizeGroup: %s\n",
element_name);
}
static const GMarkupParser size_group_parser =
{
size_group_start_element
};
static gboolean
gtk_size_group_buildable_custom_tag_start (GtkBuildable *buildable,
GtkBuilder *builder,
GObject *child,
const gchar *tagname,
GMarkupParser *parser,
gpointer *data)
{
GSListSubParserData *parser_data;
if (child)
return FALSE;
if (strcmp (tagname, "widgets") == 0)
{
parser_data = g_slice_new0 (GSListSubParserData);
parser_data->items = NULL;
parser_data->object = G_OBJECT (buildable);
*parser = size_group_parser;
*data = parser_data;
return TRUE;
}
return FALSE;
}
static void
gtk_size_group_buildable_custom_finished (GtkBuildable *buildable,
GtkBuilder *builder,
GObject *child,
const gchar *tagname,
gpointer user_data)
{
GSList *l;
GSListSubParserData *data;
GObject *object;
if (strcmp (tagname, "widgets"))
return;
data = (GSListSubParserData*)user_data;
data->items = g_slist_reverse (data->items);
for (l = data->items; l; l = l->next)
{
object = gtk_builder_get_object (builder, l->data);
if (!object)
{
g_warning ("Unknown object %s specified in sizegroup %s",
(const gchar*)l->data,
gtk_buildable_get_name (GTK_BUILDABLE (data->object)));
continue;
}
gtk_size_group_add_widget (GTK_SIZE_GROUP (data->object),
GTK_WIDGET (object));
g_free (l->data);
}
g_slist_free (data->items);
g_slice_free (GSListSubParserData, data);
}
#define __GTK_SIZE_GROUP_C__
#include "gtkaliasdef.c"