gtksizegroup.c 21.3 KB
Newer Older
Cody Russell's avatar
Cody Russell committed
1
/* GTK - The GIMP Toolkit
2 3 4 5 6 7 8 9 10 11 12 13 14 15
 * 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
Javier Jardón's avatar
Javier Jardón committed
16
 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
17 18
 */

19
#include "config.h"
Johan Dahlin's avatar
Johan Dahlin committed
20
#include <string.h>
Emmanuele Bassi's avatar
Emmanuele Bassi committed
21 22

#include "gtkbuildable.h"
23 24
#include "gtkcontainer.h"
#include "gtkintl.h"
25
#include "gtktypebuiltins.h"
26
#include "gtkprivate.h"
27
#include "gtksizegroup-private.h"
28
#include "gtksizerequestcacheprivate.h"
Emmanuele Bassi's avatar
Emmanuele Bassi committed
29
#include "gtkwidgetprivate.h"
30
#include "gtkcontainerprivate.h"
31

32 33 34 35 36 37 38 39 40

/**
 * 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
41
 * size, but you can’t use a #GtkGrid widget.
42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
 *
 * 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.
 *
85
 * # GtkSizeGroup as GtkBuildable
Matthias Clasen's avatar
Matthias Clasen committed
86
 *
87 88 89 90 91
 * 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.
92
 *
Matthias Clasen's avatar
Matthias Clasen committed
93
 * An example of a UI definition fragment with GtkSizeGroup:
94
 * |[
95 96 97 98 99 100 101
 * <object class="GtkSizeGroup">
 *   <property name="mode">GTK_SIZE_GROUP_HORIZONTAL</property>
 *   <widgets>
 *     <widget name="radio1"/>
 *     <widget name="radio2"/>
 *   </widgets>
 * </object>
102
 * ]|
103 104 105
 */


106
struct _GtkSizeGroupPrivate
107 108 109 110 111 112 113 114
{
  GSList         *widgets;

  guint8          mode;

  guint           ignore_hidden : 1;
};

115 116
enum {
  PROP_0,
117 118
  PROP_MODE,
  PROP_IGNORE_HIDDEN
119 120 121 122 123 124 125 126 127 128 129
};

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);

Johan Dahlin's avatar
Johan Dahlin committed
130 131 132 133 134 135 136 137 138 139 140 141 142 143
/* 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);

144 145 146 147
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));

148 149 150 151 152
G_DEFINE_TYPE_WITH_CODE (GtkSizeGroup, gtk_size_group, G_TYPE_OBJECT,
                         G_ADD_PRIVATE (GtkSizeGroup)
			 G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
						gtk_size_group_buildable_init))

153
static void
154 155
add_widget_to_closure (GHashTable     *widgets,
                       GHashTable     *groups,
156 157
                       GtkWidget      *widget,
		       GtkOrientation  orientation)
158
{
159
  GSList *tmp_groups, *tmp_widgets;
160
  gboolean hidden;
161

162
  if (g_hash_table_lookup (widgets, widget))
Benjamin Otte's avatar
Benjamin Otte committed
163 164
    return;

165
  g_hash_table_add (widgets, widget);
166
  hidden = !gtk_widget_is_visible (widget);
167

Benjamin Otte's avatar
Benjamin Otte committed
168
  for (tmp_groups = _gtk_widget_get_sizegroups (widget); tmp_groups; tmp_groups = tmp_groups->next)
169
    {
170 171
      GtkSizeGroup        *tmp_group = tmp_groups->data;
      GtkSizeGroupPrivate *tmp_priv  = tmp_group->priv;
172

173 174 175
      if (g_hash_table_lookup (groups, tmp_group))
        continue;

176 177 178
      if (tmp_priv->ignore_hidden && hidden)
        continue;

179
      if (!(tmp_priv->mode & (1 << orientation)))
180 181
        continue;

182
      g_hash_table_add (groups, tmp_group);
183

184
      for (tmp_widgets = tmp_priv->widgets; tmp_widgets; tmp_widgets = tmp_widgets->next)
185
        add_widget_to_closure (widgets, groups, tmp_widgets->data, orientation);
186 187 188
    }
}

189
GHashTable *
190 191
_gtk_size_group_get_widget_peers (GtkWidget      *for_widget,
                                  GtkOrientation  orientation)
192
{
193 194 195 196
  GHashTable *widgets, *groups;

  widgets = g_hash_table_new (g_direct_hash, g_direct_equal);
  groups = g_hash_table_new (g_direct_hash, g_direct_equal);
197

198
  add_widget_to_closure (widgets, groups, for_widget, orientation);
199

200
  g_hash_table_unref (groups);
201

202
  return widgets;
203 204
}
                                     
205
static void
206 207
real_queue_resize (GtkWidget          *widget,
		   GtkQueueResizeFlags flags)
208
{
209
  GtkWidget *container;
210

211
  _gtk_widget_set_alloc_needed (widget, TRUE);
212
  _gtk_size_request_cache_clear (_gtk_widget_peek_request_cache (widget));
213

214 215 216 217 218 219 220 221 222 223 224 225
  container = gtk_widget_get_parent (widget);
  if (!container &&
      gtk_widget_is_toplevel (widget) && GTK_IS_CONTAINER (widget))
    container = widget;

  if (container)
    {
      if (flags & GTK_QUEUE_RESIZE_INVALIDATE_ONLY)
	_gtk_container_resize_invalidate (GTK_CONTAINER (container));
      else
	_gtk_container_queue_resize (GTK_CONTAINER (container));
    }
226 227 228
}

static void
229 230 231
queue_resize_on_widget (GtkWidget          *widget,
			gboolean            check_siblings,
			GtkQueueResizeFlags flags)
232 233 234 235 236 237
{
  GtkWidget *parent = widget;

  while (parent)
    {
      GSList *widget_groups;
238 239 240
      GHashTable *widgets;
      GHashTableIter iter;
      gpointer current;
241 242 243
      
      if (widget == parent && !check_siblings)
	{
244
	  real_queue_resize (widget, flags);
245
          parent = gtk_widget_get_parent (parent);
246 247 248
	  continue;
	}
      
249
      widget_groups = _gtk_widget_get_sizegroups (parent);
250 251 252
      if (!widget_groups)
	{
	  if (widget == parent)
253
	    real_queue_resize (widget, flags);
254

255
          parent = gtk_widget_get_parent (parent);
256 257 258
	  continue;
	}

259
      widgets = _gtk_size_group_get_widget_peers (parent, GTK_ORIENTATION_HORIZONTAL);
260

261 262
      g_hash_table_iter_init (&iter, widgets);
      while (g_hash_table_iter_next (&iter, &current, NULL))
263
	{
264
	  if (current == parent)
265 266
	    {
	      if (widget == parent)
267
		real_queue_resize (parent, flags);
268
	    }
269
	  else if (current == widget)
270 271 272
            {
              g_warning ("A container and its child are part of this SizeGroup");
            }
273
	  else
274
	    queue_resize_on_widget (current, FALSE, flags);
275 276
	}
      
277 278
      g_hash_table_destroy (widgets);
      
279
      widgets = _gtk_size_group_get_widget_peers (parent, GTK_ORIENTATION_VERTICAL);
280

281 282
      g_hash_table_iter_init (&iter, widgets);
      while (g_hash_table_iter_next (&iter, &current, NULL))
283
	{
284
	  if (current == parent)
285 286
	    {
	      if (widget == parent)
287
		real_queue_resize (parent, flags);
288
	    }
289
	  else if (current == widget)
290 291 292
            {
              g_warning ("A container and its child are part of this SizeGroup");
            }
293
	  else
294
	    queue_resize_on_widget (current, FALSE, flags);
295 296
	}
      
297
      g_hash_table_destroy (widgets);
298 299

      parent = gtk_widget_get_parent (parent);
300 301 302 303
    }
}

static void
304
queue_resize_on_group (GtkSizeGroup       *size_group)
305
{
306
  GtkSizeGroupPrivate *priv = size_group->priv;
307 308

  if (priv->widgets)
309
    queue_resize_on_widget (priv->widgets->data, TRUE, 0);
310 311 312 313 314 315 316 317 318 319 320
}

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,
321 322 323 324 325 326 327 328
                                   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|G_PARAM_EXPLICIT_NOTIFY));
329 330 331 332

  /**
   * GtkSizeGroup:ignore-hidden:
   *
333
   * If %TRUE, unmapped widgets are ignored when determining 
334 335 336 337 338
   * the size of the group.
   *
   * Since: 2.8
   */
  g_object_class_install_property (gobject_class,
339 340 341 342 343 344 345
                                   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|G_PARAM_EXPLICIT_NOTIFY));
346 347 348 349 350
}

static void
gtk_size_group_init (GtkSizeGroup *size_group)
{
351
  GtkSizeGroupPrivate *priv;
352

353
  size_group->priv = gtk_size_group_get_instance_private (size_group);
354 355 356 357
  priv = size_group->priv;

  priv->widgets = NULL;
  priv->mode = GTK_SIZE_GROUP_HORIZONTAL;
358
  priv->ignore_hidden = FALSE;
359 360
}

Johan Dahlin's avatar
Johan Dahlin committed
361 362 363 364 365 366 367
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;
}

368 369 370 371 372 373 374 375 376 377 378 379 380
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;
381 382 383
    case PROP_IGNORE_HIDDEN:
      gtk_size_group_set_ignore_hidden (size_group, g_value_get_boolean (value));
      break;
384 385 386 387 388 389 390 391 392 393 394 395 396
    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);
397
  GtkSizeGroupPrivate *priv = size_group->priv;
398 399 400 401

  switch (prop_id)
    {
    case PROP_MODE:
402
      g_value_set_enum (value, priv->mode);
403
      break;
404
    case PROP_IGNORE_HIDDEN:
405
      g_value_set_boolean (value, priv->ignore_hidden);
406
      break;
407 408 409 410 411 412 413 414 415 416 417 418
    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.
 
419
 * Returns: a newly created #GtkSizeGroup
420 421
 **/
GtkSizeGroup *
422
gtk_size_group_new (GtkSizeGroupMode mode)
423 424
{
  GtkSizeGroup *size_group = g_object_new (GTK_TYPE_SIZE_GROUP, NULL);
425
  GtkSizeGroupPrivate *priv = size_group->priv;
426

427
  priv->mode = mode;
428 429 430 431 432 433 434 435 436 437 438

  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
Matthias Clasen's avatar
Matthias Clasen committed
439 440
 * all have the same horizontal requisition (%GTK_SIZE_GROUP_HORIZONTAL)
 * all have the same vertical requisition (%GTK_SIZE_GROUP_VERTICAL),
441
 * or should all have the same requisition in both directions
Matthias Clasen's avatar
Matthias Clasen committed
442
 * (%GTK_SIZE_GROUP_BOTH).
443 444 445 446 447
 **/
void
gtk_size_group_set_mode (GtkSizeGroup     *size_group,
			 GtkSizeGroupMode  mode)
{
448
  GtkSizeGroupPrivate *priv;
449

450 451
  g_return_if_fail (GTK_IS_SIZE_GROUP (size_group));

452 453 454
  priv = size_group->priv;

  if (priv->mode != mode)
455
    {
456
      if (priv->mode != GTK_SIZE_GROUP_NONE)
457
	queue_resize_on_group (size_group);
458 459
      priv->mode = mode;
      if (priv->mode != GTK_SIZE_GROUP_NONE)
460 461 462
	queue_resize_on_group (size_group);

      g_object_notify (G_OBJECT (size_group), "mode");
463 464 465 466 467 468 469 470 471
    }
}

/**
 * gtk_size_group_get_mode:
 * @size_group: a #GtkSizeGroup
 * 
 * Gets the current mode of the size group. See gtk_size_group_set_mode().
 * 
472
 * Returns: the current mode of the size group.
473 474 475 476 477 478
 **/
GtkSizeGroupMode
gtk_size_group_get_mode (GtkSizeGroup *size_group)
{
  g_return_val_if_fail (GTK_IS_SIZE_GROUP (size_group), GTK_SIZE_GROUP_BOTH);

479
  return size_group->priv->mode;
480 481
}

482 483 484
/**
 * gtk_size_group_set_ignore_hidden:
 * @size_group: a #GtkSizeGroup
485
 * @ignore_hidden: whether unmapped widgets should be ignored
486 487
 *   when calculating the size
 * 
488
 * Sets whether unmapped widgets should be ignored when
489 490 491 492 493 494 495 496
 * calculating the size.
 *
 * Since: 2.8 
 */
void
gtk_size_group_set_ignore_hidden (GtkSizeGroup *size_group,
				  gboolean      ignore_hidden)
{
497
  GtkSizeGroupPrivate *priv;
498

499
  g_return_if_fail (GTK_IS_SIZE_GROUP (size_group));
500 501 502

  priv = size_group->priv;

503 504
  ignore_hidden = ignore_hidden != FALSE;

505
  if (priv->ignore_hidden != ignore_hidden)
506
    {
507
      priv->ignore_hidden = ignore_hidden;
508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527

      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);

528
  return size_group->priv->ignore_hidden;
529 530
}

531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547
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().
Matthias Clasen's avatar
Matthias Clasen committed
548 549 550 551
 *
 * When the widget is destroyed or no longer referenced elsewhere, it will 
 * be removed from the size group.
 */
552 553 554 555
void
gtk_size_group_add_widget (GtkSizeGroup     *size_group,
			   GtkWidget        *widget)
{
556
  GtkSizeGroupPrivate *priv;
557 558 559 560
  GSList *groups;
  
  g_return_if_fail (GTK_IS_SIZE_GROUP (size_group));
  g_return_if_fail (GTK_IS_WIDGET (widget));
561 562 563

  priv = size_group->priv;

564
  groups = _gtk_widget_get_sizegroups (widget);
565

566
  if (!g_slist_find (groups, size_group))
567
    {
568
      _gtk_widget_add_sizegroup (widget, size_group);
569

570
      priv->widgets = g_slist_prepend (priv->widgets, widget);
571

Manish Singh's avatar
Manish Singh committed
572 573 574
      g_signal_connect (widget, "destroy",
			G_CALLBACK (gtk_size_group_widget_destroyed),
			size_group);
575

Manish Singh's avatar
Manish Singh committed
576
      g_object_ref (size_group);
577 578 579 580 581 582 583
    }
  
  queue_resize_on_group (size_group);
}

/**
 * gtk_size_group_remove_widget:
Matthias Clasen's avatar
Matthias Clasen committed
584
 * @size_group: a #GtkSizeGroup
585 586 587 588 589
 * @widget: the #GtkWidget to remove
 * 
 * Removes a widget from a #GtkSizeGroup.
 **/
void
590 591
gtk_size_group_remove_widget (GtkSizeGroup *size_group,
			      GtkWidget    *widget)
592
{
593
  GtkSizeGroupPrivate *priv;
594 595 596
  
  g_return_if_fail (GTK_IS_SIZE_GROUP (size_group));
  g_return_if_fail (GTK_IS_WIDGET (widget));
597 598 599 600

  priv = size_group->priv;

  g_return_if_fail (g_slist_find (priv->widgets, widget));
601

Manish Singh's avatar
Manish Singh committed
602 603 604
  g_signal_handlers_disconnect_by_func (widget,
					gtk_size_group_widget_destroyed,
					size_group);
605
  
606
  _gtk_widget_remove_sizegroup (widget, size_group);
607

608
  priv->widgets = g_slist_remove (priv->widgets, widget);
609 610 611
  queue_resize_on_group (size_group);
  gtk_widget_queue_resize (widget);

Manish Singh's avatar
Manish Singh committed
612
  g_object_unref (size_group);
613 614
}

615 616
/**
 * gtk_size_group_get_widgets:
Matthias Clasen's avatar
Matthias Clasen committed
617
 * @size_group: a #GtkSizeGroup
618 619 620
 * 
 * Returns the list of widgets associated with @size_group.
 *
621
 * Returns:  (element-type GtkWidget) (transfer none): a #GSList of
622
 *   widgets. The list is owned by GTK+ and should not be modified.
623 624 625 626 627 628
 *
 * Since: 2.10
 **/
GSList *
gtk_size_group_get_widgets (GtkSizeGroup *size_group)
{
629
  return size_group->priv->widgets;
630 631
}

632 633 634 635 636 637 638
/**
 * _gtk_size_group_queue_resize:
 * @widget: a #GtkWidget
 * 
 * Queue a resize on a widget, and on all other widgets grouped with this widget.
 **/
void
639 640
_gtk_size_group_queue_resize (GtkWidget           *widget,
			      GtkQueueResizeFlags  flags)
641
{
642
  queue_resize_on_widget (widget, TRUE, flags);
643
}
644

Johan Dahlin's avatar
Johan Dahlin committed
645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661
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)
662 663 664 665 666 667 668
    {
      for (i = 0; names[i]; i++)
        {
          if (strcmp (names[i], "name") == 0)
            data->items = g_slist_prepend (data->items, g_strdup (values[i]));
        }
    }
Johan Dahlin's avatar
Johan Dahlin committed
669
  else if (strcmp (element_name, "widgets") == 0)
670 671 672
    {
      return;
    }
Johan Dahlin's avatar
Johan Dahlin committed
673
  else
674 675 676 677
    {
      g_warning ("Unsupported type tag for GtkSizeGroup: %s\n",
                 element_name);
    }
Johan Dahlin's avatar
Johan Dahlin committed
678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745
}

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);
}