gtk2/gtk/gtksizegroup.c
Tristan Van Berkom 8432f54c51 Added _gtk_size_group_bump_requisition()
Added a function to update sizegroups in multiple passes, this
way the width and height can be updated in the sizegroups after
querying the extended layout implementor for these.

Implemented this in GtkExtendedLayout, sizegroups should be working reasonably now.
2010-04-14 18:07:27 -04:00

1003 lines
25 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 "gtkalias.h"
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);
GTK_PRIVATE_SET_FLAG (widget, GTK_WIDTH_REQUEST_NEEDED);
GTK_PRIVATE_SET_FLAG (widget, GTK_HEIGHT_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 ();
}
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 gint
get_base_dimension (GtkWidget *widget,
GtkSizeGroupMode mode)
{
GtkWidgetAuxInfo *aux_info = _gtk_widget_get_aux_info (widget, FALSE);
if (mode == GTK_SIZE_GROUP_HORIZONTAL)
{
if (aux_info && aux_info->width > 0)
return aux_info->width;
else
return widget->requisition.width;
}
else
{
if (aux_info && aux_info->height > 0)
return aux_info->height;
else
return widget->requisition.height;
}
}
static void
do_size_request (GtkWidget *widget, gint width, gint height)
{
if (GTK_WIDGET_REQUEST_NEEDED (widget))
{
gtk_widget_ensure_style (widget);
GTK_PRIVATE_UNSET_FLAG (widget, GTK_REQUEST_NEEDED);
g_signal_emit_by_name (widget,
"size-request",
&widget->requisition);
}
/* Also update size groups from _gtk_size_group_bump_requisition() */
widget->requisition.width = MAX (widget->requisition.width, width);
widget->requisition.height = MAX (widget->requisition.height, height);
}
/* NOTE: This is only ever called for either mode horizontal or mode vertical
* but never as both.
*/
static gint
compute_base_dimension (GtkWidget *widget,
GtkSizeGroupMode mode,
gint minimum)
{
if (mode == GTK_SIZE_GROUP_HORIZONTAL)
do_size_request (widget, minimum, -1);
else /* (mode == GTK_SIZE_GROUP_VERTICAL) */
do_size_request (widget, -1, minimum);
return get_base_dimension (widget, mode);
}
/* NOTE: This is only ever called for either mode horizontal or mode vertical
* but never as both.
*/
static gint
compute_dimension (GtkWidget *widget,
GtkSizeGroupMode mode,
gint minimum)
{
GSList *widgets = NULL;
GSList *groups = NULL;
GSList *tmp_list;
gint 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)
{
result = compute_base_dimension (widget, mode, minimum);
}
else
{
GtkSizeGroup *group = groups->data;
if (mode == GTK_SIZE_GROUP_HORIZONTAL && group->have_width)
result = group->requisition.width;
else if (mode == GTK_SIZE_GROUP_VERTICAL && group->have_height)
result = group->requisition.height;
else
{
tmp_list = widgets;
while (tmp_list)
{
GtkWidget *tmp_widget = tmp_list->data;
gint dimension = compute_base_dimension (tmp_widget, mode, minimum);
if (gtk_widget_get_mapped (tmp_widget) || !group->ignore_hidden)
{
if (dimension > result)
result = dimension;
}
tmp_list = tmp_list->next;
}
tmp_list = groups;
while (tmp_list)
{
GtkSizeGroup *tmp_group = tmp_list->data;
if (mode == GTK_SIZE_GROUP_HORIZONTAL)
{
tmp_group->have_width = TRUE;
tmp_group->requisition.width = MAX (result, minimum);
}
else
{
tmp_group->have_height = TRUE;
tmp_group->requisition.height = MAX (result, minimum);
}
tmp_list = tmp_list->next;
}
}
}
g_slist_foreach (widgets, (GFunc)g_object_unref, NULL);
g_slist_free (widgets);
g_slist_free (groups);
return result;
}
static gint
get_dimension (GtkWidget *widget,
GtkSizeGroupMode mode)
{
GSList *widgets = NULL;
GSList *groups = NULL;
gint 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);
if (!groups)
{
result = get_base_dimension (widget, mode);
}
else
{
GtkSizeGroup *group = groups->data;
if (mode == GTK_SIZE_GROUP_HORIZONTAL && group->have_width)
result = group->requisition.width;
else if (mode == GTK_SIZE_GROUP_VERTICAL && group->have_height)
result = group->requisition.height;
}
g_slist_free (widgets);
g_slist_free (groups);
return result;
}
static void
get_fast_child_requisition (GtkWidget *widget,
GtkRequisition *requisition)
{
GtkWidgetAuxInfo *aux_info = _gtk_widget_get_aux_info (widget, FALSE);
*requisition = widget->requisition;
if (aux_info)
{
if (aux_info->width > 0)
requisition->width = aux_info->width;
if (aux_info && aux_info->height > 0)
requisition->height = aux_info->height;
}
}
/**
* _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.
**/
void
_gtk_size_group_get_child_requisition (GtkWidget *widget,
GtkRequisition *requisition)
{
initialize_size_group_quarks ();
if (requisition)
{
if (get_size_groups (widget))
{
requisition->width = get_dimension (widget, GTK_SIZE_GROUP_HORIZONTAL);
requisition->height = get_dimension (widget, GTK_SIZE_GROUP_VERTICAL);
/* Only do the full computation if we actually have size groups */
}
else
get_fast_child_requisition (widget, requisition);
}
}
/**
* _gtk_size_group_compute_requisition:
* @widget: a #GtkWidget
* @requisition: location to store computed requisition.
*
* Compute the requisition of a widget taking into account grouping of
* the widget's requisition with other widgets.
*
* This is used by #GtkExtendedLayout to obtain minimum value
* caps for all values cached and returned to parent containers,
* then the extended layout while making its own sizes in multiple
* passes updates sizegroups with _gtk_size_group_bump_requisition().
*/
void
_gtk_size_group_compute_requisition (GtkWidget *widget,
GtkRequisition *requisition)
{
gint width;
gint height;
initialize_size_group_quarks ();
if (get_size_groups (widget))
{
/* Only do the full computation if we actually have size groups */
width = compute_dimension (widget, GTK_SIZE_GROUP_HORIZONTAL, -1);
height = compute_dimension (widget, GTK_SIZE_GROUP_VERTICAL, -1);
if (requisition)
{
requisition->width = width;
requisition->height = height;
}
}
else
{
do_size_request (widget, -1, -1);
if (requisition)
get_fast_child_requisition (widget, requisition);
}
}
/**
* _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.
* @size: The new base size in @mode's dimension for the group.
*
* This function is used to update sizegroup minimum size information
* in multiple passes from the new #GtkExtendedLayout manager.
*/
void
_gtk_size_group_bump_requisition (GtkWidget *widget,
GtkSizeGroupMode mode,
gint size)
{
initialize_size_group_quarks ();
if (get_size_groups (widget))
{
if (mode == GTK_SIZE_GROUP_HORIZONTAL)
compute_dimension (widget, GTK_SIZE_GROUP_HORIZONTAL, size);
else
compute_dimension (widget, GTK_SIZE_GROUP_VERTICAL, size);
}
else
{
if (mode == GTK_SIZE_GROUP_HORIZONTAL)
do_size_request (widget, size, -1);
else
do_size_request (widget, -1, 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"