~ubuntu-branches/ubuntu/oneiric/rhythmbox/oneiric

« back to all changes in this revision

Viewing changes to widgets/eggwrapbox.c

  • Committer: Bazaar Package Importer
  • Author(s): Rico Tzschichholz
  • Date: 2011-07-29 16:41:38 UTC
  • mto: This revision was merged to the branch mainline in revision 191.
  • Revision ID: james.westby@ubuntu.com-20110729164138-wwicy8nqalm18ck7
Tags: upstream-2.90.1~20110802
ImportĀ upstreamĀ versionĀ 2.90.1~20110802

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* eggwrapbox.c
 
2
 * Copyright (C) 2007-2010 Openismus GmbH
 
3
 *
 
4
 * Authors:
 
5
 *      Tristan Van Berkom <tristanvb@openismus.com>
 
6
 *
 
7
 * This library is free software; you can redistribute it and/or
 
8
 * modify it under the terms of the GNU Library General Public
 
9
 * License as published by the Free Software Foundation; either
 
10
 * version 2 of the License, or (at your option) any later version.
 
11
 *
 
12
 * This library is distributed in the hope that it will be useful,
 
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
15
 * Library General Public License for more details.
 
16
 *
 
17
 * You should have received a copy of the GNU Library General Public
 
18
 * License along with this library; if not, write to the
 
19
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 
20
 * Boston, MA 02111-1307, USA.
 
21
 */
 
22
 
 
23
 
 
24
/**
 
25
 * SECTION:eggwrapbox
 
26
 * @Short_Description: A container that wraps its children
 
27
 * @Title: EggWrapBox
 
28
 *
 
29
 * #EggWrapBox positions child widgets in sequence according to its
 
30
 * orientation. For instance, with the horizontal orientation, the widgets
 
31
 * will be arranged from left to right, starting a new row under the
 
32
 * previous row when necessary. Reducing the width in this case will
 
33
 * require more rows, so a larger height will be requested.
 
34
 *
 
35
 * Likewise, with the vertical orientation, the widgets will be arranged
 
36
 * from top to bottom, starting a new column to the right when necessary.
 
37
 * Reducing the height will require more columns, so a larger width will be
 
38
 * requested.
 
39
 *
 
40
 * Unlike a GtkTable, the child widgets do not need to align in a grid.
 
41
 */
 
42
 
 
43
#ifdef HAVE_CONFIG_H
 
44
#include <config.h>
 
45
#endif
 
46
 
 
47
#include "eggwrapbox.h"
 
48
#include "eggwrapbox-enums.h"
 
49
 
 
50
#define P_(msgid) (msgid)
 
51
#define GTK_PARAM_READWRITE (G_PARAM_READABLE | G_PARAM_WRITABLE)
 
52
 
 
53
 
 
54
typedef struct _EggWrapBoxChild  EggWrapBoxChild;
 
55
 
 
56
enum {
 
57
  PROP_0,
 
58
  PROP_ORIENTATION,
 
59
  PROP_ALLOCATION_MODE,
 
60
  PROP_HORIZONTAL_SPREADING,
 
61
  PROP_VERTICAL_SPREADING,
 
62
  PROP_HORIZONTAL_SPACING,
 
63
  PROP_VERTICAL_SPACING,
 
64
  PROP_MINIMUM_LINE_CHILDREN,
 
65
  PROP_NATURAL_LINE_CHILDREN
 
66
};
 
67
 
 
68
enum
 
69
{
 
70
  CHILD_PROP_0,
 
71
  CHILD_PROP_PACKING
 
72
};
 
73
 
 
74
struct _EggWrapBoxPrivate
 
75
{
 
76
  GtkOrientation        orientation;
 
77
  EggWrapAllocationMode mode;
 
78
  EggWrapBoxSpreading   horizontal_spreading;
 
79
  EggWrapBoxSpreading   vertical_spreading;
 
80
 
 
81
  guint16               vertical_spacing;
 
82
  guint16               horizontal_spacing;
 
83
 
 
84
  guint16               minimum_line_children;
 
85
  guint16               natural_line_children;
 
86
 
 
87
  GList                *children;
 
88
};
 
89
 
 
90
struct _EggWrapBoxChild
 
91
{
 
92
  GtkWidget        *widget;
 
93
 
 
94
  EggWrapBoxPacking packing;
 
95
};
 
96
 
 
97
/* GObjectClass */
 
98
static void egg_wrap_box_get_property         (GObject             *object,
 
99
                                               guint                prop_id,
 
100
                                               GValue              *value,
 
101
                                               GParamSpec          *pspec);
 
102
static void egg_wrap_box_set_property         (GObject             *object,
 
103
                                               guint                prop_id,
 
104
                                               const GValue        *value,
 
105
                                               GParamSpec          *pspec);
 
106
 
 
107
/* GtkWidgetClass */
 
108
static void egg_wrap_box_size_allocate        (GtkWidget           *widget,
 
109
                                               GtkAllocation       *allocation);
 
110
 
 
111
/* GtkContainerClass */
 
112
static void egg_wrap_box_add                  (GtkContainer        *container,
 
113
                                               GtkWidget           *widget);
 
114
static void egg_wrap_box_remove               (GtkContainer        *container,
 
115
                                               GtkWidget           *widget);
 
116
static void egg_wrap_box_forall               (GtkContainer        *container,
 
117
                                               gboolean             include_internals,
 
118
                                               GtkCallback          callback,
 
119
                                               gpointer             callback_data);
 
120
static void egg_wrap_box_set_child_property   (GtkContainer        *container,
 
121
                                               GtkWidget           *child,
 
122
                                               guint                property_id,
 
123
                                               const GValue        *value,
 
124
                                               GParamSpec          *pspec);
 
125
static void egg_wrap_box_get_child_property   (GtkContainer        *container,
 
126
                                               GtkWidget           *child,
 
127
                                               guint                property_id,
 
128
                                               GValue              *value,
 
129
                                               GParamSpec          *pspec);
 
130
static GType egg_wrap_box_child_type          (GtkContainer        *container);
 
131
 
 
132
 
 
133
/* GtkWidget      */
 
134
static GtkSizeRequestMode egg_wrap_box_get_request_mode (GtkWidget           *widget);
 
135
static void egg_wrap_box_get_preferred_width            (GtkWidget           *widget,
 
136
                                                         gint                *minimum_size,
 
137
                                                         gint                *natural_size);
 
138
static void egg_wrap_box_get_preferred_height           (GtkWidget           *widget,
 
139
                                                         gint                *minimum_size,
 
140
                                                         gint                *natural_size);
 
141
static void egg_wrap_box_get_preferred_height_for_width (GtkWidget           *box,
 
142
                                                         gint                 width,
 
143
                                                         gint                *minimum_height,
 
144
                                                         gint                *natural_height);
 
145
static void egg_wrap_box_get_preferred_width_for_height (GtkWidget           *box,
 
146
                                                         gint                 width,
 
147
                                                         gint                *minimum_height,
 
148
                                                         gint                *natural_height);
 
149
 
 
150
 
 
151
G_DEFINE_TYPE_WITH_CODE (EggWrapBox, egg_wrap_box, GTK_TYPE_CONTAINER,
 
152
                         G_IMPLEMENT_INTERFACE (GTK_TYPE_ORIENTABLE, NULL))
 
153
 
 
154
 
 
155
#define ORIENTATION_SPREADING(box)                                      \
 
156
  (((EggWrapBox *)(box))->priv->orientation == GTK_ORIENTATION_HORIZONTAL ? \
 
157
   ((EggWrapBox *)(box))->priv->horizontal_spreading :                  \
 
158
   ((EggWrapBox *)(box))->priv->vertical_spreading)
 
159
 
 
160
#define OPPOSING_ORIENTATION_SPREADING(box)                             \
 
161
  (((EggWrapBox *)(box))->priv->orientation == GTK_ORIENTATION_HORIZONTAL ? \
 
162
   ((EggWrapBox *)(box))->priv->vertical_spreading :                    \
 
163
   ((EggWrapBox *)(box))->priv->horizontal_spreading)
 
164
 
 
165
 
 
166
 
 
167
static void
 
168
egg_wrap_box_class_init (EggWrapBoxClass *class)
 
169
{
 
170
  GObjectClass      *gobject_class    = G_OBJECT_CLASS (class);
 
171
  GtkWidgetClass    *widget_class     = GTK_WIDGET_CLASS (class);
 
172
  GtkContainerClass *container_class  = GTK_CONTAINER_CLASS (class);
 
173
 
 
174
  gobject_class->get_property         = egg_wrap_box_get_property;
 
175
  gobject_class->set_property         = egg_wrap_box_set_property;
 
176
 
 
177
  widget_class->size_allocate         = egg_wrap_box_size_allocate;
 
178
  widget_class->get_request_mode      = egg_wrap_box_get_request_mode;
 
179
  widget_class->get_preferred_width   = egg_wrap_box_get_preferred_width;
 
180
  widget_class->get_preferred_height  = egg_wrap_box_get_preferred_height;
 
181
  widget_class->get_preferred_height_for_width = egg_wrap_box_get_preferred_height_for_width;
 
182
  widget_class->get_preferred_width_for_height = egg_wrap_box_get_preferred_width_for_height;
 
183
 
 
184
  container_class->add                = egg_wrap_box_add;
 
185
  container_class->remove             = egg_wrap_box_remove;
 
186
  container_class->forall             = egg_wrap_box_forall;
 
187
  container_class->child_type         = egg_wrap_box_child_type;
 
188
  container_class->set_child_property = egg_wrap_box_set_child_property;
 
189
  container_class->get_child_property = egg_wrap_box_get_child_property;
 
190
  gtk_container_class_handle_border_width (container_class);
 
191
 
 
192
  /* GObjectClass properties */
 
193
  g_object_class_override_property (gobject_class, PROP_ORIENTATION, "orientation");
 
194
 
 
195
  /**
 
196
   * EggWrapBox:allocation-mode:
 
197
   *
 
198
   * The #EggWrapAllocationMode to use.
 
199
   */
 
200
  g_object_class_install_property (gobject_class,
 
201
                                   PROP_ALLOCATION_MODE,
 
202
                                   g_param_spec_uint ("allocation-mode",
 
203
                                                      P_("Allocation Mode"),
 
204
                                                      P_("The allocation mode to use"),
 
205
                                                      0, EGG_WRAP_ALLOCATE_HOMOGENEOUS,
 
206
                                                      EGG_WRAP_ALLOCATE_FREE,
 
207
                                                      GTK_PARAM_READWRITE));
 
208
 
 
209
  /**
 
210
   * EggWrapBox:horizontal-spreading:
 
211
   *
 
212
   * The #EggWrapBoxSpreading to used to define what is done with extra
 
213
   * space in a given orientation.
 
214
   */
 
215
  g_object_class_install_property (gobject_class,
 
216
                                   PROP_HORIZONTAL_SPREADING,
 
217
                                   g_param_spec_uint ("horizontal-spreading",
 
218
                                                      P_("Horizontal Spreading"),
 
219
                                                      P_("The spreading mode to use horizontally"),
 
220
                                                      0, EGG_WRAP_BOX_SPREAD_EXPAND,
 
221
                                                      EGG_WRAP_BOX_SPREAD_START,
 
222
                                                      GTK_PARAM_READWRITE));
 
223
 
 
224
  /**
 
225
   * EggWrapBox:vertical-spreading:
 
226
   *
 
227
   * The #EggWrapBoxSpreading to used to define what is done with extra
 
228
   * space in a given orientation.
 
229
   */
 
230
  g_object_class_install_property (gobject_class,
 
231
                                   PROP_VERTICAL_SPREADING,
 
232
                                   g_param_spec_uint ("vertical-spreading",
 
233
                                                      P_("Vertical Spreading"),
 
234
                                                      P_("The spreading mode to use vertically"),
 
235
                                                      0, EGG_WRAP_BOX_SPREAD_EXPAND,
 
236
                                                      EGG_WRAP_BOX_SPREAD_START,
 
237
                                                      GTK_PARAM_READWRITE));
 
238
 
 
239
 
 
240
  /**
 
241
   * EggWrapBox:minimum-line-children:
 
242
   *
 
243
   * The minimum number of children to allocate consecutively in the given orientation.
 
244
   *
 
245
   * <note><para>Setting the minimum children per line ensures
 
246
   * that a reasonably small height will be requested
 
247
   * for the overall minimum width of the box.</para></note>
 
248
   *
 
249
   */
 
250
  g_object_class_install_property (gobject_class,
 
251
                                   PROP_MINIMUM_LINE_CHILDREN,
 
252
                                   g_param_spec_uint ("minimum-line-children",
 
253
                                                      P_("Minimum Line Children"),
 
254
                                                      P_("The minimum number of children to allocate "
 
255
                                                        "consecutively in the given orientation."),
 
256
                                                      0,
 
257
                                                      65535,
 
258
                                                      0,
 
259
                                                      GTK_PARAM_READWRITE));
 
260
 
 
261
  /**
 
262
   * EggWrapBox:natural-line-children:
 
263
   *
 
264
   * The maximum amount of children to request space for consecutively in the given orientation.
 
265
   *
 
266
   */
 
267
  g_object_class_install_property (gobject_class,
 
268
                                   PROP_NATURAL_LINE_CHILDREN,
 
269
                                   g_param_spec_uint ("natural-line-children",
 
270
                                                      P_("Natural Line Children"),
 
271
                                                      P_("The maximum amount of children to request space for "
 
272
                                                        "consecutively in the given orientation."),
 
273
                                                      0,
 
274
                                                      65535,
 
275
                                                      0,
 
276
                                                      GTK_PARAM_READWRITE));
 
277
 
 
278
  /**
 
279
   * EggWrapBox:vertical-spacing:
 
280
   *
 
281
   * The amount of vertical space between two children.
 
282
   *
 
283
   */
 
284
  g_object_class_install_property (gobject_class,
 
285
                                   PROP_VERTICAL_SPACING,
 
286
                                   g_param_spec_uint ("vertical-spacing",
 
287
                                                     P_("Vertical spacing"),
 
288
                                                     P_("The amount of vertical space between two children"),
 
289
                                                     0,
 
290
                                                     65535,
 
291
                                                     0,
 
292
                                                     GTK_PARAM_READWRITE));
 
293
 
 
294
  /**
 
295
   * EggWrapBox:horizontal-spacing:
 
296
   *
 
297
   * The amount of horizontal space between two children.
 
298
   *
 
299
   */
 
300
  g_object_class_install_property (gobject_class,
 
301
                                   PROP_HORIZONTAL_SPACING,
 
302
                                   g_param_spec_uint ("horizontal-spacing",
 
303
                                                     P_("Horizontal spacing"),
 
304
                                                     P_("The amount of horizontal space between two children"),
 
305
                                                     0,
 
306
                                                     65535,
 
307
                                                     0,
 
308
                                                     GTK_PARAM_READWRITE));
 
309
 
 
310
  /* GtkContainerClass child properties */
 
311
 
 
312
  /**
 
313
   * EggWrapBox:packing:
 
314
   *
 
315
   * The #EggWrapBoxPacking options to specify how to pack a child into the box.
 
316
   */
 
317
  gtk_container_class_install_child_property (container_class,
 
318
                                              CHILD_PROP_PACKING,
 
319
                                              g_param_spec_flags
 
320
                                              ("packing",
 
321
                                               P_("Packing"),
 
322
                                               P_("The packing options to use for this child"),
 
323
                                               EGG_TYPE_WRAP_BOX_PACKING, 0,
 
324
                                               GTK_PARAM_READWRITE));
 
325
 
 
326
  g_type_class_add_private (class, sizeof (EggWrapBoxPrivate));
 
327
}
 
328
 
 
329
static void
 
330
egg_wrap_box_init (EggWrapBox *box)
 
331
{
 
332
  EggWrapBoxPrivate *priv;
 
333
 
 
334
  box->priv = priv =
 
335
    G_TYPE_INSTANCE_GET_PRIVATE (box, EGG_TYPE_WRAP_BOX, EggWrapBoxPrivate);
 
336
 
 
337
  priv->orientation          = GTK_ORIENTATION_HORIZONTAL;
 
338
  priv->mode                 = EGG_WRAP_ALLOCATE_FREE;
 
339
  priv->horizontal_spreading = EGG_WRAP_BOX_SPREAD_START;
 
340
  priv->vertical_spreading   = EGG_WRAP_BOX_SPREAD_START;
 
341
  priv->horizontal_spacing   = 0;
 
342
  priv->vertical_spacing     = 0;
 
343
  priv->children             = NULL;
 
344
 
 
345
  gtk_widget_set_has_window (GTK_WIDGET (box), FALSE);
 
346
}
 
347
 
 
348
/*****************************************************
 
349
 *                  GObectClass                      *
 
350
 *****************************************************/
 
351
static void
 
352
egg_wrap_box_get_property (GObject      *object,
 
353
                           guint         prop_id,
 
354
                           GValue       *value,
 
355
                           GParamSpec   *pspec)
 
356
{
 
357
  EggWrapBox        *box  = EGG_WRAP_BOX (object);
 
358
  EggWrapBoxPrivate *priv = box->priv;
 
359
 
 
360
  switch (prop_id)
 
361
    {
 
362
    case PROP_ORIENTATION:
 
363
      g_value_set_boolean (value, priv->orientation);
 
364
      break;
 
365
    case PROP_ALLOCATION_MODE:
 
366
      g_value_set_uint (value, priv->mode);
 
367
      break;
 
368
    case PROP_HORIZONTAL_SPREADING:
 
369
      g_value_set_uint (value, priv->horizontal_spreading);
 
370
      break;
 
371
    case PROP_VERTICAL_SPREADING:
 
372
      g_value_set_uint (value, priv->vertical_spreading);
 
373
      break;
 
374
    case PROP_HORIZONTAL_SPACING:
 
375
      g_value_set_uint (value, priv->horizontal_spacing);
 
376
      break;
 
377
    case PROP_VERTICAL_SPACING:
 
378
      g_value_set_uint (value, priv->vertical_spacing);
 
379
      break;
 
380
    case PROP_MINIMUM_LINE_CHILDREN:
 
381
      g_value_set_uint (value, priv->minimum_line_children);
 
382
      break;
 
383
    case PROP_NATURAL_LINE_CHILDREN:
 
384
      g_value_set_uint (value, priv->natural_line_children);
 
385
      break;
 
386
    default:
 
387
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 
388
      break;
 
389
    }
 
390
}
 
391
 
 
392
static void
 
393
egg_wrap_box_set_property (GObject      *object,
 
394
                           guint         prop_id,
 
395
                           const GValue *value,
 
396
                           GParamSpec   *pspec)
 
397
{
 
398
  EggWrapBox        *box = EGG_WRAP_BOX (object);
 
399
  EggWrapBoxPrivate *priv   = box->priv;
 
400
 
 
401
  switch (prop_id)
 
402
    {
 
403
    case PROP_ORIENTATION:
 
404
      priv->orientation = g_value_get_enum (value);
 
405
 
 
406
      /* Re-box the children in the new orientation */
 
407
      gtk_widget_queue_resize (GTK_WIDGET (box));
 
408
      break;
 
409
    case PROP_ALLOCATION_MODE:
 
410
      egg_wrap_box_set_allocation_mode (box, g_value_get_uint (value));
 
411
      break;
 
412
    case PROP_HORIZONTAL_SPREADING:
 
413
      egg_wrap_box_set_horizontal_spreading (box, g_value_get_uint (value));
 
414
      break;
 
415
    case PROP_VERTICAL_SPREADING:
 
416
      egg_wrap_box_set_vertical_spreading (box, g_value_get_uint (value));
 
417
      break;
 
418
    case PROP_HORIZONTAL_SPACING:
 
419
      egg_wrap_box_set_horizontal_spacing (box, g_value_get_uint (value));
 
420
      break;
 
421
    case PROP_VERTICAL_SPACING:
 
422
      egg_wrap_box_set_vertical_spacing (box, g_value_get_uint (value));
 
423
      break;
 
424
    case PROP_MINIMUM_LINE_CHILDREN:
 
425
      egg_wrap_box_set_minimum_line_children (box, g_value_get_uint (value));
 
426
      break;
 
427
    case PROP_NATURAL_LINE_CHILDREN:
 
428
      egg_wrap_box_set_natural_line_children (box, g_value_get_uint (value));
 
429
      break;
 
430
    default:
 
431
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
 
432
      break;
 
433
    }
 
434
}
 
435
 
 
436
/*****************************************************
 
437
 *                 GtkWidgetClass                    *
 
438
 *****************************************************/
 
439
 
 
440
static gint
 
441
get_visible_children (EggWrapBox  *box)
 
442
{
 
443
  EggWrapBoxPrivate *priv = box->priv;
 
444
  GList             *list;
 
445
  gint               i = 0;
 
446
 
 
447
  for (list = priv->children; list; list = list->next)
 
448
    {
 
449
      EggWrapBoxChild *child = list->data;
 
450
 
 
451
      if (!gtk_widget_get_visible (child->widget))
 
452
        continue;
 
453
 
 
454
      i++;
 
455
    }
 
456
 
 
457
  return i;
 
458
}
 
459
 
 
460
static gint
 
461
get_visible_expand_children (EggWrapBox     *box,
 
462
                             GtkOrientation  orientation,
 
463
                             GList          *cursor,
 
464
                             gint            n_visible)
 
465
{
 
466
  GList *list;
 
467
  gint   i, expand_children = 0;
 
468
 
 
469
  for (i = 0, list = cursor; (n_visible > 0 ? i < n_visible : TRUE) && list; list = list->next)
 
470
    {
 
471
      EggWrapBoxChild *child = list->data;
 
472
 
 
473
      if (!gtk_widget_get_visible (child->widget))
 
474
        continue;
 
475
 
 
476
      if ((orientation == GTK_ORIENTATION_HORIZONTAL && (child->packing & EGG_WRAP_BOX_H_EXPAND) != 0) ||
 
477
          (orientation == GTK_ORIENTATION_VERTICAL   && (child->packing & EGG_WRAP_BOX_V_EXPAND) != 0))
 
478
        expand_children++;
 
479
 
 
480
      i++;
 
481
    }
 
482
 
 
483
  return expand_children;
 
484
}
 
485
 
 
486
/* Used in columned modes where all items share at least their
 
487
 * equal widths or heights
 
488
 */
 
489
static void
 
490
get_average_item_size (EggWrapBox      *box,
 
491
                       GtkOrientation   orientation,
 
492
                       gint            *min_size,
 
493
                       gint            *nat_size)
 
494
{
 
495
  EggWrapBoxPrivate *priv = box->priv;
 
496
  GList             *list;
 
497
  gint               max_min_size = 0;
 
498
  gint               max_nat_size = 0;
 
499
 
 
500
  for (list = priv->children; list; list = list->next)
 
501
    {
 
502
      EggWrapBoxChild *child = list->data;
 
503
      gint             child_min, child_nat;
 
504
 
 
505
      if (!gtk_widget_get_visible (child->widget))
 
506
        continue;
 
507
 
 
508
      if (orientation == GTK_ORIENTATION_HORIZONTAL)
 
509
        gtk_widget_get_preferred_width (child->widget, &child_min, &child_nat);
 
510
      else
 
511
        gtk_widget_get_preferred_height (child->widget, &child_min, &child_nat);
 
512
 
 
513
      max_min_size = MAX (max_min_size, child_min);
 
514
      max_nat_size = MAX (max_nat_size, child_nat);
 
515
    }
 
516
 
 
517
  if (min_size)
 
518
    *min_size = max_min_size;
 
519
 
 
520
  if (nat_size)
 
521
    *nat_size = max_nat_size;
 
522
}
 
523
 
 
524
 
 
525
/* Gets the largest minimum/natural size for a given size
 
526
 * (used to get the largest item heights for a fixed item width and the opposite) */
 
527
static void
 
528
get_largest_size_for_opposing_orientation (EggWrapBox         *box,
 
529
                                           GtkOrientation      orientation,
 
530
                                           gint                item_size,
 
531
                                           gint               *min_item_size,
 
532
                                           gint               *nat_item_size)
 
533
{
 
534
  EggWrapBoxPrivate *priv = box->priv;
 
535
  GList             *list;
 
536
  gint               max_min_size = 0;
 
537
  gint               max_nat_size = 0;
 
538
 
 
539
  for (list = priv->children; list; list = list->next)
 
540
    {
 
541
      EggWrapBoxChild *child = list->data;
 
542
      gint             child_min, child_nat;
 
543
 
 
544
      if (!gtk_widget_get_visible (child->widget))
 
545
        continue;
 
546
 
 
547
      if (orientation == GTK_ORIENTATION_HORIZONTAL)
 
548
        gtk_widget_get_preferred_height_for_width (child->widget,
 
549
                                                         item_size,
 
550
                                                         &child_min, &child_nat);
 
551
      else
 
552
        gtk_widget_get_preferred_width_for_height (child->widget,
 
553
                                                   item_size,
 
554
                                                   &child_min, &child_nat);
 
555
 
 
556
      max_min_size = MAX (max_min_size, child_min);
 
557
      max_nat_size = MAX (max_nat_size, child_nat);
 
558
    }
 
559
 
 
560
  if (min_item_size)
 
561
    *min_item_size = max_min_size;
 
562
 
 
563
  if (nat_item_size)
 
564
    *nat_item_size = max_nat_size;
 
565
}
 
566
 
 
567
 
 
568
/* Gets the largest minimum/natural size on a single line for a given size
 
569
 * (used to get the largest line heights for a fixed item width and the opposite
 
570
 * while itterating over a list of children, note the new index is returned) */
 
571
static GList *
 
572
get_largest_size_for_line_in_opposing_orientation (EggWrapBox       *box,
 
573
                                                   GtkOrientation    orientation,
 
574
                                                   GList            *cursor,
 
575
                                                   gint              line_length,
 
576
                                                   GtkRequestedSize *item_sizes,
 
577
                                                   gint              extra_pixels,
 
578
                                                   gint             *min_item_size,
 
579
                                                   gint             *nat_item_size)
 
580
{
 
581
  GList  *list;
 
582
  gint    max_min_size = 0;
 
583
  gint    max_nat_size = 0;
 
584
  gint    i;
 
585
 
 
586
  for (list = cursor, i = 0; list && i < line_length; list = list->next)
 
587
    {
 
588
      EggWrapBoxChild *child = list->data;
 
589
      gint             child_min, child_nat, this_item_size;
 
590
 
 
591
      if (!gtk_widget_get_visible (child->widget))
 
592
        continue;
 
593
 
 
594
      /* Distribute the extra pixels to the first children in the line
 
595
       * (could be fancier and spread them out more evenly) */
 
596
      this_item_size = item_sizes[i].minimum_size;
 
597
      if (extra_pixels > 0 && ORIENTATION_SPREADING (box) == EGG_WRAP_BOX_SPREAD_EXPAND)
 
598
        {
 
599
          this_item_size++;
 
600
          extra_pixels--;
 
601
        }
 
602
 
 
603
      if (orientation == GTK_ORIENTATION_HORIZONTAL)
 
604
        gtk_widget_get_preferred_height_for_width (child->widget,
 
605
                                                   this_item_size,
 
606
                                                   &child_min, &child_nat);
 
607
      else
 
608
        gtk_widget_get_preferred_width_for_height (child->widget,
 
609
                                                   this_item_size,
 
610
                                                   &child_min, &child_nat);
 
611
 
 
612
      max_min_size = MAX (max_min_size, child_min);
 
613
      max_nat_size = MAX (max_nat_size, child_nat);
 
614
 
 
615
      i++;
 
616
    }
 
617
 
 
618
  if (min_item_size)
 
619
    *min_item_size = max_min_size;
 
620
 
 
621
  if (nat_item_size)
 
622
    *nat_item_size = max_nat_size;
 
623
 
 
624
  /* Return next item in the list */
 
625
  return list;
 
626
}
 
627
 
 
628
 
 
629
/* Gets the largest minimum/natural size on a single line for a given allocated line size
 
630
 * (used to get the largest line heights for a width in pixels and the opposite
 
631
 * while itterating over a list of children, note the new index is returned) */
 
632
static GList *
 
633
get_largest_size_for_free_line_in_opposing_orientation (EggWrapBox      *box,
 
634
                                                        GtkOrientation   orientation,
 
635
                                                        GList           *cursor,
 
636
                                                        gint             min_items,
 
637
                                                        gint             avail_size,
 
638
                                                        gint            *min_item_size,
 
639
                                                        gint            *nat_item_size,
 
640
                                                        gint            *extra_pixels,
 
641
                                                        GArray         **ret_array)
 
642
{
 
643
  EggWrapBoxPrivate *priv = box->priv;
 
644
  GtkRequestedSize  *sizes;
 
645
  GList             *list;
 
646
  GArray            *array;
 
647
  gint               max_min_size = 0;
 
648
  gint               max_nat_size = 0;
 
649
  gint               i, size = avail_size;
 
650
  gint               line_length, spacing;
 
651
  gint               expand_children = 0;
 
652
  gint               expand_per_child;
 
653
  gint               expand_remainder;
 
654
 
 
655
  if (orientation == GTK_ORIENTATION_HORIZONTAL)
 
656
    spacing = priv->horizontal_spacing;
 
657
  else
 
658
    spacing = priv->vertical_spacing;
 
659
 
 
660
  /* First determine the length of this line in items (how many items fit) */
 
661
  for (i = 0, list = cursor; size > 0 && list; list = list->next)
 
662
    {
 
663
      EggWrapBoxChild *child = list->data;
 
664
      gint             child_size;
 
665
 
 
666
      if (!gtk_widget_get_visible (child->widget))
 
667
        continue;
 
668
 
 
669
      if (orientation == GTK_ORIENTATION_HORIZONTAL)
 
670
        gtk_widget_get_preferred_width (child->widget, NULL, &child_size);
 
671
      else
 
672
        gtk_widget_get_preferred_height (child->widget, NULL, &child_size);
 
673
 
 
674
      if (i > 0)
 
675
        child_size += spacing;
 
676
 
 
677
      if (size - child_size >= 0)
 
678
        size -= child_size;
 
679
      else
 
680
        break;
 
681
 
 
682
      i++;
 
683
    }
 
684
 
 
685
  line_length = MAX (min_items, i);
 
686
  size        = avail_size;
 
687
 
 
688
  /* Collect the sizes of the items on this line */
 
689
  array = g_array_new (0, TRUE, sizeof (GtkRequestedSize));
 
690
 
 
691
  for (i = 0, list = cursor; i < line_length && list; list = list->next)
 
692
    {
 
693
      EggWrapBoxChild  *child = list->data;
 
694
      GtkRequestedSize  requested;
 
695
 
 
696
      if (!gtk_widget_get_visible (child->widget))
 
697
        continue;
 
698
 
 
699
      requested.data = child;
 
700
      if (orientation == GTK_ORIENTATION_HORIZONTAL)
 
701
        gtk_widget_get_preferred_width (child->widget,
 
702
                                        &requested.minimum_size,
 
703
                                        &requested.natural_size);
 
704
      else
 
705
        gtk_widget_get_preferred_height (child->widget,
 
706
                                         &requested.minimum_size,
 
707
                                         &requested.natural_size);
 
708
 
 
709
      if (i > 0)
 
710
        size -= spacing;
 
711
 
 
712
      size -= requested.minimum_size;
 
713
 
 
714
      g_array_append_val (array, requested);
 
715
 
 
716
      i++;
 
717
    }
 
718
 
 
719
  sizes = (GtkRequestedSize *)array->data;
 
720
  size  = gtk_distribute_natural_allocation (size, array->len, sizes);
 
721
 
 
722
  if (extra_pixels)
 
723
    *extra_pixels = size;
 
724
 
 
725
  /* Cut out any expand space if we're not distributing any */
 
726
  if (ORIENTATION_SPREADING (box) != EGG_WRAP_BOX_SPREAD_EXPAND)
 
727
    size = 0;
 
728
 
 
729
  /* Count how many children are going to expand... */
 
730
  expand_children = get_visible_expand_children (box, orientation,
 
731
                                                 cursor, line_length);
 
732
 
 
733
  /* If no child prefers to expand, they all get some expand space */
 
734
  if (expand_children == 0)
 
735
    {
 
736
      expand_per_child = size / line_length;
 
737
      expand_remainder = size % line_length;
 
738
    }
 
739
  else
 
740
    {
 
741
      expand_per_child = size / expand_children;
 
742
      expand_remainder = size % expand_children;
 
743
    }
 
744
 
 
745
  /* Now add the remaining expand space and get the collective size of this line
 
746
   * in the opposing orientation */
 
747
  for (i = 0, list = cursor; i < line_length && list; list = list->next)
 
748
    {
 
749
      EggWrapBoxChild *child = list->data;
 
750
      gint child_min, child_nat;
 
751
 
 
752
      if (!gtk_widget_get_visible (child->widget))
 
753
        continue;
 
754
 
 
755
      g_assert (child == sizes[i].data);
 
756
 
 
757
      if ((orientation == GTK_ORIENTATION_HORIZONTAL && (child->packing & EGG_WRAP_BOX_H_EXPAND) != 0) ||
 
758
          (orientation == GTK_ORIENTATION_VERTICAL   && (child->packing & EGG_WRAP_BOX_V_EXPAND) != 0) ||
 
759
          expand_children == 0)
 
760
        {
 
761
          sizes[i].minimum_size += expand_per_child;
 
762
          if (expand_remainder)
 
763
            {
 
764
              sizes[i].minimum_size++;
 
765
              expand_remainder--;
 
766
            }
 
767
        }
 
768
 
 
769
      if (orientation == GTK_ORIENTATION_HORIZONTAL)
 
770
        gtk_widget_get_preferred_height_for_width (child->widget,
 
771
                                                   sizes[i].minimum_size,
 
772
                                                   &child_min, &child_nat);
 
773
      else
 
774
        gtk_widget_get_preferred_width_for_height (child->widget,
 
775
                                                   sizes[i].minimum_size,
 
776
                                                   &child_min, &child_nat);
 
777
 
 
778
      max_min_size = MAX (max_min_size, child_min);
 
779
      max_nat_size = MAX (max_nat_size, child_nat);
 
780
 
 
781
      i++;
 
782
    }
 
783
 
 
784
  if (ret_array)
 
785
    *ret_array = array;
 
786
  else
 
787
    g_array_free (array, TRUE);
 
788
 
 
789
  if (min_item_size)
 
790
    *min_item_size = max_min_size;
 
791
 
 
792
  if (nat_item_size)
 
793
    *nat_item_size = max_nat_size;
 
794
 
 
795
  /* Return the next item */
 
796
  return list;
 
797
}
 
798
 
 
799
static void
 
800
allocate_child (EggWrapBox      *box,
 
801
                EggWrapBoxChild *child,
 
802
                gint             item_offset,
 
803
                gint             line_offset,
 
804
                gint             item_size,
 
805
                gint             line_size)
 
806
{
 
807
  EggWrapBoxPrivate  *priv   = box->priv;
 
808
  GtkAllocation       widget_allocation;
 
809
  GtkAllocation       child_allocation;
 
810
 
 
811
  gtk_widget_get_allocation (GTK_WIDGET (box), &widget_allocation);
 
812
 
 
813
  if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
 
814
    {
 
815
      child_allocation.x      = widget_allocation.x + item_offset;
 
816
      child_allocation.y      = widget_allocation.y + line_offset;
 
817
      child_allocation.width  = item_size;
 
818
      child_allocation.height = line_size;
 
819
    }
 
820
  else /* GTK_ORIENTATION_VERTICAL */
 
821
    {
 
822
      child_allocation.x      = widget_allocation.x + line_offset;
 
823
      child_allocation.y      = widget_allocation.y + item_offset;
 
824
      child_allocation.width  = line_size;
 
825
      child_allocation.height = item_size;
 
826
    }
 
827
 
 
828
  gtk_widget_size_allocate (child->widget, &child_allocation);
 
829
}
 
830
 
 
831
/* fit_aligned_item_requests() helper */
 
832
static gint
 
833
gather_aligned_item_requests (EggWrapBox       *box,
 
834
                              GtkOrientation    orientation,
 
835
                              gint              line_length,
 
836
                              gint              item_spacing,
 
837
                              gint              n_children,
 
838
                              GtkRequestedSize *item_sizes)
 
839
{
 
840
  EggWrapBoxPrivate *priv   = box->priv;
 
841
  GList             *list;
 
842
  gint               i;
 
843
  gint               extra_items, natural_line_size = 0;
 
844
 
 
845
  extra_items = n_children % line_length;
 
846
 
 
847
  for (list = priv->children, i = 0; list; list = list->next, i++)
 
848
    {
 
849
      EggWrapBoxChild *child = list->data;
 
850
      gint             child_min, child_nat;
 
851
      gint             position;
 
852
 
 
853
      if (!gtk_widget_get_visible (child->widget))
 
854
        continue;
 
855
 
 
856
      if (orientation == GTK_ORIENTATION_HORIZONTAL)
 
857
        gtk_widget_get_preferred_width (child->widget,
 
858
                                        &child_min, &child_nat);
 
859
      else
 
860
        gtk_widget_get_preferred_height (child->widget,
 
861
                                         &child_min, &child_nat);
 
862
 
 
863
      /* Get the index and push it over for the last line when spreading to the end */
 
864
      position = i % line_length;
 
865
 
 
866
      if (ORIENTATION_SPREADING (box) == EGG_WRAP_BOX_SPREAD_END && i >= n_children - extra_items)
 
867
        position += line_length - extra_items;
 
868
 
 
869
      /* Round up the size of every column/row */
 
870
      item_sizes[position].minimum_size = MAX (item_sizes[position].minimum_size, child_min);
 
871
      item_sizes[position].natural_size = MAX (item_sizes[position].natural_size, child_nat);
 
872
    }
 
873
 
 
874
  for (i = 0; i < line_length; i++)
 
875
    natural_line_size += item_sizes[i].natural_size;
 
876
 
 
877
  natural_line_size += (line_length - 1) * item_spacing;
 
878
 
 
879
  return natural_line_size;
 
880
}
 
881
 
 
882
static GtkRequestedSize *
 
883
fit_aligned_item_requests (EggWrapBox       *box, 
 
884
                           GtkOrientation    orientation, 
 
885
                           gint              avail_size,
 
886
                           gint              item_spacing,
 
887
                           gint             *line_length, /* in-out */
 
888
                           gint              n_children)
 
889
{
 
890
  GtkRequestedSize  *sizes, *try_sizes;
 
891
  gint               try_line_size, try_length;
 
892
 
 
893
  sizes = g_new0 (GtkRequestedSize, *line_length);
 
894
 
 
895
  /* get the sizes for the initial guess */
 
896
  try_line_size = 
 
897
    gather_aligned_item_requests (box, orientation, *line_length, item_spacing, n_children, sizes);
 
898
 
 
899
  /* Try columnizing the whole thing and adding an item to the end of the line;
 
900
   * try to fit as many columns into the available size as possible */
 
901
  for (try_length = *line_length + 1; try_line_size < avail_size; try_length++)
 
902
    {
 
903
      try_sizes     = g_new0 (GtkRequestedSize, try_length);
 
904
      try_line_size = gather_aligned_item_requests (box, orientation, try_length, item_spacing, 
 
905
                                                    n_children, try_sizes);
 
906
 
 
907
      if (try_line_size <= avail_size)
 
908
        {
 
909
          *line_length = try_length;
 
910
 
 
911
          g_free (sizes);
 
912
          sizes = try_sizes;
 
913
        }
 
914
      else
 
915
        {
 
916
          /* oops, this one failed; stick to the last size that fit and then return */
 
917
          g_free (try_sizes);
 
918
          break;
 
919
        }
 
920
    }
 
921
 
 
922
  return sizes;
 
923
}
 
924
 
 
925
 
 
926
typedef struct {
 
927
  GArray *requested;
 
928
  gint    extra_pixels;
 
929
} AllocatedLine;
 
930
 
 
931
static void
 
932
egg_wrap_box_size_allocate (GtkWidget     *widget,
 
933
                            GtkAllocation *allocation)
 
934
{
 
935
  EggWrapBox         *box  = EGG_WRAP_BOX (widget);
 
936
  EggWrapBoxPrivate  *priv = box->priv;
 
937
  gint                avail_size, avail_other_size, min_items, item_spacing, line_spacing;
 
938
  EggWrapBoxSpreading item_spreading;
 
939
  EggWrapBoxSpreading line_spreading;
 
940
 
 
941
  gtk_widget_set_allocation (widget, allocation);
 
942
 
 
943
  min_items = MAX (1, priv->minimum_line_children);
 
944
 
 
945
  if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
 
946
    {
 
947
      avail_size       = allocation->width;
 
948
      avail_other_size = allocation->height;
 
949
      item_spacing     = priv->horizontal_spacing;
 
950
      line_spacing     = priv->vertical_spacing;
 
951
    }
 
952
  else /* GTK_ORIENTATION_VERTICAL */
 
953
    {
 
954
      avail_size       = allocation->height;
 
955
      avail_other_size = allocation->width;
 
956
      item_spacing     = priv->vertical_spacing;
 
957
      line_spacing     = priv->horizontal_spacing;
 
958
    }
 
959
 
 
960
  item_spreading = ORIENTATION_SPREADING (box);
 
961
  line_spreading    = OPPOSING_ORIENTATION_SPREADING (box);
 
962
 
 
963
 
 
964
  /*********************************************************
 
965
   * Deal with ALIGNED/HOMOGENEOUS modes first, start with * 
 
966
   * initial guesses at item/line sizes                    *
 
967
   *********************************************************/
 
968
  if (priv->mode == EGG_WRAP_ALLOCATE_ALIGNED ||
 
969
      priv->mode == EGG_WRAP_ALLOCATE_HOMOGENEOUS)
 
970
    {
 
971
      GtkRequestedSize *line_sizes = NULL;
 
972
      GtkRequestedSize *item_sizes = NULL;
 
973
      GList *list;
 
974
      gint   min_item_size, nat_item_size;
 
975
      gint   line_length;
 
976
      gint   item_size = 0;
 
977
      gint   line_size = 0, min_fixed_line_size = 0, nat_fixed_line_size = 0;
 
978
      gint   line_offset, item_offset, n_children, n_lines, line_count;
 
979
      gint   extra_pixels, extra_per_item = 0, extra_extra = 0;
 
980
      gint   extra_line_pixels, extra_per_line = 0, extra_line_extra = 0;
 
981
      gint   i, this_line_size;
 
982
 
 
983
      get_average_item_size (box, priv->orientation, &min_item_size, &nat_item_size);
 
984
 
 
985
      /* By default wrap at the natural item width */
 
986
      line_length = avail_size / (nat_item_size + item_spacing);
 
987
 
 
988
      /* After the above aproximation, check if we cant fit one more on the line */
 
989
      if (line_length * item_spacing + (line_length + 1) * nat_item_size <= avail_size)
 
990
        line_length++;
 
991
 
 
992
      /* Its possible we were allocated just less than the natural width of the
 
993
       * minimum item wrap length */
 
994
      line_length = MAX (min_items, line_length);
 
995
 
 
996
      /* Get how many lines we'll be needing to wrap */
 
997
      n_children = get_visible_children (box);
 
998
 
 
999
      /* Here we just use the largest height-for-width and use that for the height
 
1000
       * of all lines */
 
1001
      if (priv->mode == EGG_WRAP_ALLOCATE_HOMOGENEOUS)
 
1002
        {
 
1003
          n_lines    = n_children / line_length;
 
1004
          if ((n_children % line_length) > 0)
 
1005
            n_lines++;
 
1006
          
 
1007
          n_lines = MAX (n_lines, 1);
 
1008
 
 
1009
          /* Now we need the real item allocation size */
 
1010
          item_size = (avail_size - (line_length - 1) * item_spacing) / line_length;
 
1011
          
 
1012
          /* Cut out the expand space if we're not distributing any */
 
1013
          if (item_spreading != EGG_WRAP_BOX_SPREAD_EXPAND)
 
1014
            item_size = MIN (item_size, nat_item_size);
 
1015
          
 
1016
          get_largest_size_for_opposing_orientation (box, priv->orientation, item_size,
 
1017
                                                     &min_fixed_line_size,
 
1018
                                                     &nat_fixed_line_size);
 
1019
 
 
1020
          /* resolve a fixed 'line_size' */
 
1021
          line_size = (avail_other_size - (n_lines - 1) * line_spacing) / n_lines;
 
1022
 
 
1023
          if (line_spreading != EGG_WRAP_BOX_SPREAD_EXPAND)
 
1024
            line_size = MIN (line_size, nat_fixed_line_size);
 
1025
 
 
1026
          /* Get the real extra pixels incase of EGG_WRAP_BOX_SPREAD_START lines */
 
1027
          extra_pixels      = avail_size       - (line_length - 1) * item_spacing - item_size * line_length;
 
1028
          extra_line_pixels = avail_other_size - (n_lines - 1)     * line_spacing - line_size * n_lines;
 
1029
        }
 
1030
      else /* EGG_WRAP_ALLOCATE_ALIGNED */
 
1031
        {
 
1032
          GList            *list;
 
1033
          gboolean          first_line = TRUE;
 
1034
 
 
1035
          /* Find the amount of columns that can fit aligned into the available space
 
1036
           * and collect their requests.
 
1037
           */
 
1038
          item_sizes = fit_aligned_item_requests (box, priv->orientation, avail_size,
 
1039
                                                  item_spacing, &line_length, n_children);
 
1040
 
 
1041
          /* Calculate the number of lines after determining the final line_length */
 
1042
          n_lines    = n_children / line_length;
 
1043
          if ((n_children % line_length) > 0)
 
1044
            n_lines++;
 
1045
          
 
1046
          n_lines = MAX (n_lines, 1);
 
1047
          line_sizes = g_new0 (GtkRequestedSize, n_lines);
 
1048
 
 
1049
          /* Get the available remaining size */
 
1050
          avail_size -= (line_length - 1) * item_spacing;
 
1051
          for (i = 0; i < line_length; i++)
 
1052
            avail_size -= item_sizes[i].minimum_size;
 
1053
 
 
1054
          /* Perform a natural allocation on the columnized items and get the remaining pixels */
 
1055
          extra_pixels = gtk_distribute_natural_allocation (avail_size, line_length, item_sizes);
 
1056
 
 
1057
          /* Now that we have the size of each column of items find the size of each individual 
 
1058
           * line based on the aligned item sizes.
 
1059
           */
 
1060
          for (i = 0, list = priv->children; list != NULL; i++)
 
1061
            {
 
1062
 
 
1063
              list =
 
1064
                get_largest_size_for_line_in_opposing_orientation (box, priv->orientation,
 
1065
                                                                   list, line_length,
 
1066
                                                                   item_sizes, extra_pixels,
 
1067
                                                                   &line_sizes[i].minimum_size,
 
1068
                                                                   &line_sizes[i].natural_size);
 
1069
 
 
1070
 
 
1071
              /* Its possible a line is made of completely invisible children */
 
1072
              if (line_sizes[i].natural_size > 0)
 
1073
                {
 
1074
                  if (first_line)
 
1075
                    first_line = FALSE;
 
1076
                  else
 
1077
                    avail_other_size -= line_spacing;
 
1078
 
 
1079
                  avail_other_size -= line_sizes[i].minimum_size;
 
1080
 
 
1081
                  line_sizes[i].data = GINT_TO_POINTER (i);
 
1082
                }
 
1083
            }
 
1084
 
 
1085
          /* Distribute space among lines naturally */
 
1086
          extra_line_pixels = gtk_distribute_natural_allocation (avail_other_size, n_lines, line_sizes);
 
1087
        }
 
1088
 
 
1089
      /*********************************************************
 
1090
       * Initial sizes of items/lines guessed at this point,   * 
 
1091
       * go on to distribute expand space if needed.           *
 
1092
       *********************************************************/
 
1093
 
 
1094
      /* FIXME: This portion needs to consider which columns
 
1095
       * and rows asked for expand space and distribute those
 
1096
       * accordingly for the case of ALIGNED allocation.
 
1097
       *
 
1098
       * If at least one child in a column/row asked for expand;
 
1099
       * we should make that row/column expand entirely.
 
1100
       */
 
1101
 
 
1102
      /* Calculate expand space per item */
 
1103
      if (item_spreading == EGG_WRAP_BOX_SPREAD_EVEN)
 
1104
        {
 
1105
          extra_per_item = extra_pixels / MAX (line_length -1, 1);
 
1106
          extra_extra    = extra_pixels % MAX (line_length -1, 1);
 
1107
        }
 
1108
      else if (item_spreading == EGG_WRAP_BOX_SPREAD_EXPAND)
 
1109
        {
 
1110
          extra_per_item = extra_pixels / line_length;
 
1111
          extra_extra    = extra_pixels % line_length;
 
1112
        }
 
1113
 
 
1114
      /* Calculate expand space per line */
 
1115
      if (line_spreading == EGG_WRAP_BOX_SPREAD_EVEN)
 
1116
        {
 
1117
          extra_per_line   = extra_line_pixels / MAX (n_lines -1, 1);
 
1118
          extra_line_extra = extra_line_pixels % MAX (n_lines -1, 1);
 
1119
        }
 
1120
      else if (line_spreading == EGG_WRAP_BOX_SPREAD_EXPAND)
 
1121
        {
 
1122
          extra_per_line   = extra_line_pixels / n_lines;
 
1123
          extra_line_extra = extra_line_pixels % n_lines;
 
1124
        }
 
1125
 
 
1126
      /*********************************************************
 
1127
       * Prepare item/line initial offsets and jump into the   *
 
1128
       * real allocation loop.                                 *
 
1129
       *********************************************************/
 
1130
      line_offset = item_offset = 0;
 
1131
 
 
1132
      /* prepend extra space to item_offset/line_offset for SPREAD_END */
 
1133
      if (item_spreading == EGG_WRAP_BOX_SPREAD_END)
 
1134
        item_offset += extra_pixels;
 
1135
 
 
1136
      if (line_spreading == EGG_WRAP_BOX_SPREAD_END)
 
1137
        line_offset += extra_line_pixels;
 
1138
 
 
1139
      /* Get the allocation size for the first line */
 
1140
      if (priv->mode == EGG_WRAP_ALLOCATE_HOMOGENEOUS)
 
1141
        this_line_size = line_size;
 
1142
      else 
 
1143
        {
 
1144
          this_line_size  = line_sizes[0].minimum_size;
 
1145
 
 
1146
          if (line_spreading == EGG_WRAP_BOX_SPREAD_EXPAND)
 
1147
            {
 
1148
              this_line_size += extra_per_line;
 
1149
 
 
1150
              if (extra_line_extra > 0)
 
1151
                this_line_size++;
 
1152
            }
 
1153
        }
 
1154
 
 
1155
      for (i = 0, line_count = 0, list = priv->children; list; list = list->next)
 
1156
        {
 
1157
          EggWrapBoxChild *child = list->data;
 
1158
          gint             position;
 
1159
          gint             this_item_size;
 
1160
 
 
1161
          if (!gtk_widget_get_visible (child->widget))
 
1162
            continue;
 
1163
 
 
1164
          /* Get item position */
 
1165
          position = i % line_length;
 
1166
 
 
1167
          /* adjust the line_offset/count at the beginning of each new line */
 
1168
          if (i > 0 && position == 0)
 
1169
            {
 
1170
              /* Push the line_offset */
 
1171
              line_offset += this_line_size + line_spacing;
 
1172
 
 
1173
              if (line_spreading == EGG_WRAP_BOX_SPREAD_EVEN)
 
1174
                {
 
1175
                  line_offset += extra_per_line;
 
1176
                      
 
1177
                  if (line_count < extra_line_extra)
 
1178
                    line_offset++;
 
1179
                }
 
1180
 
 
1181
              line_count++;
 
1182
 
 
1183
              /* Get the new line size */
 
1184
              if (priv->mode == EGG_WRAP_ALLOCATE_HOMOGENEOUS)
 
1185
                this_line_size = line_size;
 
1186
              else
 
1187
                {
 
1188
                  this_line_size = line_sizes[line_count].minimum_size;
 
1189
 
 
1190
                  if (line_spreading == EGG_WRAP_BOX_SPREAD_EXPAND)
 
1191
                    {
 
1192
                      this_line_size += extra_per_line;
 
1193
                      
 
1194
                      if (line_count < extra_line_extra)
 
1195
                        this_line_size++;
 
1196
                    }
 
1197
                }
 
1198
 
 
1199
              item_offset = 0;
 
1200
 
 
1201
              if (item_spreading == EGG_WRAP_BOX_SPREAD_END)
 
1202
                {
 
1203
                  item_offset += extra_pixels;
 
1204
 
 
1205
                  /* If we're on the last line, prepend the space for
 
1206
                   * any leading items */
 
1207
                  if (line_count == n_lines -1)
 
1208
                    {
 
1209
                      gint extra_items = n_children % line_length;
 
1210
 
 
1211
                      if (priv->mode == EGG_WRAP_ALLOCATE_HOMOGENEOUS)
 
1212
                        {
 
1213
                          item_offset += item_size * (line_length - extra_items);
 
1214
                          item_offset += item_spacing * (line_length - extra_items);
 
1215
                        }
 
1216
                      else
 
1217
                        {
 
1218
                          gint j;
 
1219
 
 
1220
                          for (j = 0; j < (line_length - extra_items); j++)
 
1221
                            {
 
1222
                              item_offset += item_sizes[j].minimum_size;
 
1223
                              item_offset += item_spacing;
 
1224
                            }
 
1225
                        }
 
1226
                    }
 
1227
                }
 
1228
            }
 
1229
 
 
1230
          /* Push the index along for the last line when spreading to the end */
 
1231
          if (item_spreading == EGG_WRAP_BOX_SPREAD_END &&
 
1232
              line_count == n_lines -1)
 
1233
            {
 
1234
              gint extra_items = n_children % line_length;
 
1235
 
 
1236
              position += line_length - extra_items;
 
1237
            }
 
1238
 
 
1239
          if (priv->mode == EGG_WRAP_ALLOCATE_HOMOGENEOUS)
 
1240
            this_item_size = item_size;
 
1241
          else /* aligned mode */
 
1242
            this_item_size = item_sizes[position].minimum_size;
 
1243
 
 
1244
          if (item_spreading == EGG_WRAP_BOX_SPREAD_EXPAND)
 
1245
            {
 
1246
              this_item_size += extra_per_item;
 
1247
 
 
1248
              if (position < extra_extra)
 
1249
                this_item_size++;
 
1250
            }
 
1251
 
 
1252
          /* Do the actual allocation */
 
1253
          allocate_child (box, child, item_offset, line_offset, this_item_size, this_line_size);
 
1254
 
 
1255
          item_offset += this_item_size;
 
1256
          item_offset += item_spacing;
 
1257
 
 
1258
          /* deal with extra spacing here */
 
1259
          if (item_spreading == EGG_WRAP_BOX_SPREAD_EVEN)
 
1260
            {
 
1261
              item_offset += extra_per_item;
 
1262
 
 
1263
              if (position < extra_extra)
 
1264
                item_offset++;
 
1265
            }
 
1266
 
 
1267
          i++;
 
1268
        }
 
1269
 
 
1270
      g_free (item_sizes);
 
1271
      g_free (line_sizes);
 
1272
    }
 
1273
  else /* EGG_WRAP_ALLOCATE_FREE */
 
1274
    {
 
1275
      /* Here we just fit as many children as we can allocate their natural size to
 
1276
       * on each line and add the heights for each of them on each line */
 
1277
      GtkRequestedSize  requested; 
 
1278
      GtkRequestedSize *sizes = NULL;
 
1279
      GList            *list = priv->children;
 
1280
      gboolean          first_line = TRUE;
 
1281
      gint              i, line_count = 0;
 
1282
      gint              line_offset, item_offset;
 
1283
      gint              extra_per_line = 0, extra_line_extra = 0;
 
1284
      gint              extra_pixels;
 
1285
      GArray           *array;
 
1286
  
 
1287
      array = g_array_new (0, TRUE, sizeof (GtkRequestedSize));
 
1288
 
 
1289
      while (list != NULL)
 
1290
        {
 
1291
          GArray         *line_array;
 
1292
          AllocatedLine  *line;
 
1293
 
 
1294
          list =
 
1295
            get_largest_size_for_free_line_in_opposing_orientation (box, priv->orientation,
 
1296
                                                                    list, min_items, avail_size,
 
1297
                                                                    &requested.minimum_size,
 
1298
                                                                    &requested.natural_size,
 
1299
                                                                    &extra_pixels,
 
1300
                                                                    &line_array);
 
1301
 
 
1302
          /* Its possible a line is made of completely invisible children */
 
1303
          if (requested.natural_size > 0)
 
1304
            {
 
1305
              if (first_line)
 
1306
                first_line = FALSE;
 
1307
              else
 
1308
                avail_other_size -= line_spacing;
 
1309
 
 
1310
              avail_other_size -= requested.minimum_size;
 
1311
 
 
1312
              line = g_slice_new0 (AllocatedLine);
 
1313
              line->requested    = line_array;
 
1314
              line->extra_pixels = extra_pixels;
 
1315
 
 
1316
              requested.data  = line;
 
1317
 
 
1318
              g_array_append_val (array, requested);
 
1319
            }
 
1320
        }
 
1321
 
 
1322
      /* Distribute space among lines naturally */
 
1323
      sizes            = (GtkRequestedSize *)array->data;
 
1324
      avail_other_size = gtk_distribute_natural_allocation (avail_other_size, array->len, sizes);
 
1325
 
 
1326
      /* Calculate expand space per line */
 
1327
      if (line_spreading == EGG_WRAP_BOX_SPREAD_EVEN)
 
1328
        {
 
1329
          extra_per_line   = avail_other_size / MAX (array->len -1, 1);
 
1330
          extra_line_extra = avail_other_size % MAX (array->len -1, 1);
 
1331
        }
 
1332
      else if (line_spreading == EGG_WRAP_BOX_SPREAD_EXPAND)
 
1333
        {
 
1334
          extra_per_line   = avail_other_size / array->len;
 
1335
          extra_line_extra = avail_other_size % array->len;
 
1336
        }
 
1337
 
 
1338
      if (line_spreading == EGG_WRAP_BOX_SPREAD_END)
 
1339
        line_offset = avail_other_size;
 
1340
      else
 
1341
        line_offset = 0;
 
1342
 
 
1343
      for (line_count = 0; line_count < array->len; line_count++)
 
1344
        {
 
1345
          AllocatedLine    *line       = (AllocatedLine *)sizes[line_count].data;
 
1346
          GArray           *line_array = line->requested;
 
1347
          GtkRequestedSize *line_sizes = (GtkRequestedSize *)line_array->data;
 
1348
          gint              line_size  = sizes[line_count].minimum_size;
 
1349
          gint              extra_per_item = 0;
 
1350
          gint              extra_extra = 0;
 
1351
 
 
1352
          /* Set line start offset */
 
1353
          item_offset = 0;
 
1354
 
 
1355
          if (line_spreading == EGG_WRAP_BOX_SPREAD_EXPAND)
 
1356
            {
 
1357
              line_size += extra_per_line;
 
1358
 
 
1359
              if (line_count < extra_line_extra)
 
1360
                line_size++;
 
1361
            }
 
1362
 
 
1363
          if (item_spreading == EGG_WRAP_BOX_SPREAD_END)
 
1364
            item_offset += line->extra_pixels;
 
1365
          else if (item_spreading == EGG_WRAP_BOX_SPREAD_EVEN)
 
1366
            {
 
1367
              extra_per_item = line->extra_pixels / MAX (line_array->len -1, 1);
 
1368
              extra_extra    = line->extra_pixels % MAX (line_array->len -1, 1);
 
1369
            }
 
1370
 
 
1371
          for (i = 0; i < line_array->len; i++)
 
1372
            {
 
1373
              EggWrapBoxChild *child     = line_sizes[i].data;
 
1374
              gint             item_size = line_sizes[i].minimum_size;
 
1375
 
 
1376
              /* Do the actual allocation */
 
1377
              allocate_child (box, child, item_offset, line_offset, item_size, line_size);
 
1378
 
 
1379
              /* Add extra space evenly between children */
 
1380
              if (item_spreading == EGG_WRAP_BOX_SPREAD_EVEN)
 
1381
                {
 
1382
                  item_offset += extra_per_item;
 
1383
                  if (i < extra_extra)
 
1384
                    item_offset++;
 
1385
                }
 
1386
 
 
1387
              /* Move item cursor along for the next allocation */
 
1388
              item_offset += item_spacing;
 
1389
              item_offset += item_size;
 
1390
            }
 
1391
 
 
1392
          /* New line, increment offset and reset item cursor */
 
1393
          line_offset += line_spacing;
 
1394
          line_offset += line_size;
 
1395
 
 
1396
          if (line_spreading == EGG_WRAP_BOX_SPREAD_EVEN)
 
1397
            {
 
1398
              line_offset += extra_per_line;
 
1399
 
 
1400
              if (line_count < extra_line_extra)
 
1401
                line_offset++;
 
1402
            }
 
1403
 
 
1404
          /* Free the array for this line now its not needed anymore */
 
1405
          g_array_free (line_array, TRUE);
 
1406
          g_slice_free (AllocatedLine, line);
 
1407
        }
 
1408
 
 
1409
      g_array_free (array, TRUE);
 
1410
    }
 
1411
}
 
1412
 
 
1413
/*****************************************************
 
1414
 *                GtkContainerClass                  *
 
1415
 *****************************************************/
 
1416
static void
 
1417
egg_wrap_box_add (GtkContainer *container,
 
1418
                  GtkWidget    *widget)
 
1419
{
 
1420
  egg_wrap_box_insert_child (EGG_WRAP_BOX (container), widget, -1, 0);
 
1421
}
 
1422
 
 
1423
static gint
 
1424
find_child_in_list (EggWrapBoxChild *child_in_list,
 
1425
                    GtkWidget       *search)
 
1426
{
 
1427
  return (child_in_list->widget == search) ? 0 : -1;
 
1428
}
 
1429
 
 
1430
static void
 
1431
egg_wrap_box_remove (GtkContainer *container,
 
1432
                     GtkWidget    *widget)
 
1433
{
 
1434
  EggWrapBox        *box = EGG_WRAP_BOX (container);
 
1435
  EggWrapBoxPrivate *priv   = box->priv;
 
1436
  GList             *list;
 
1437
 
 
1438
  list = g_list_find_custom (priv->children, widget,
 
1439
                             (GCompareFunc)find_child_in_list);
 
1440
 
 
1441
  if (list)
 
1442
    {
 
1443
      EggWrapBoxChild *child = list->data;
 
1444
      gboolean was_visible = gtk_widget_get_visible (widget);
 
1445
 
 
1446
      gtk_widget_unparent (widget);
 
1447
 
 
1448
      g_slice_free (EggWrapBoxChild, child);
 
1449
      priv->children = g_list_delete_link (priv->children, list);
 
1450
 
 
1451
      if (was_visible && gtk_widget_get_visible (GTK_WIDGET (container)))
 
1452
        gtk_widget_queue_resize (GTK_WIDGET (container));
 
1453
    }
 
1454
}
 
1455
 
 
1456
static void
 
1457
egg_wrap_box_forall (GtkContainer *container,
 
1458
                     gboolean      include_internals,
 
1459
                     GtkCallback   callback,
 
1460
                     gpointer      callback_data)
 
1461
{
 
1462
  EggWrapBox        *box = EGG_WRAP_BOX (container);
 
1463
  EggWrapBoxPrivate *priv   = box->priv;
 
1464
  EggWrapBoxChild   *child;
 
1465
  GList             *list;
 
1466
 
 
1467
  list = priv->children;
 
1468
 
 
1469
  while (list)
 
1470
    {
 
1471
      child = list->data;
 
1472
      list  = list->next;
 
1473
 
 
1474
      (* callback) (child->widget, callback_data);
 
1475
    }
 
1476
}
 
1477
 
 
1478
static GType
 
1479
egg_wrap_box_child_type (GtkContainer   *container)
 
1480
{
 
1481
  return GTK_TYPE_WIDGET;
 
1482
}
 
1483
 
 
1484
static void
 
1485
egg_wrap_box_set_child_property (GtkContainer    *container,
 
1486
                                 GtkWidget       *widget,
 
1487
                                 guint            property_id,
 
1488
                                 const GValue    *value,
 
1489
                                 GParamSpec      *pspec)
 
1490
{
 
1491
  EggWrapBox        *box  = EGG_WRAP_BOX (container);
 
1492
  EggWrapBoxPrivate *priv = box->priv;
 
1493
  EggWrapBoxChild   *child;
 
1494
  GList             *list;
 
1495
 
 
1496
  list = g_list_find_custom (priv->children, widget,
 
1497
                             (GCompareFunc)find_child_in_list);
 
1498
  g_return_if_fail (list != NULL);
 
1499
 
 
1500
  child = list->data;
 
1501
 
 
1502
  switch (property_id)
 
1503
    {
 
1504
    case CHILD_PROP_PACKING:
 
1505
      child->packing = g_value_get_flags (value);
 
1506
      break;
 
1507
    default:
 
1508
      GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
 
1509
      break;
 
1510
    }
 
1511
 
 
1512
  if (gtk_widget_get_visible (widget) &&
 
1513
      gtk_widget_get_visible (GTK_WIDGET (box)))
 
1514
    gtk_widget_queue_resize (widget);
 
1515
}
 
1516
 
 
1517
static void
 
1518
egg_wrap_box_get_child_property (GtkContainer    *container,
 
1519
                                 GtkWidget       *widget,
 
1520
                                 guint            property_id,
 
1521
                                 GValue          *value,
 
1522
                                 GParamSpec      *pspec)
 
1523
{
 
1524
  EggWrapBox        *box = EGG_WRAP_BOX (container);
 
1525
  EggWrapBoxPrivate *priv   = box->priv;
 
1526
  EggWrapBoxChild   *child;
 
1527
  GList             *list;
 
1528
 
 
1529
  list = g_list_find_custom (priv->children, widget,
 
1530
                             (GCompareFunc)find_child_in_list);
 
1531
  g_return_if_fail (list != NULL);
 
1532
 
 
1533
  child = list->data;
 
1534
 
 
1535
  switch (property_id)
 
1536
    {
 
1537
    case CHILD_PROP_PACKING:
 
1538
      g_value_set_flags (value, child->packing);
 
1539
      break;
 
1540
    default:
 
1541
      GTK_CONTAINER_WARN_INVALID_CHILD_PROPERTY_ID (container, property_id, pspec);
 
1542
      break;
 
1543
    }
 
1544
}
 
1545
 
 
1546
/*****************************************************
 
1547
 *                 size requests                     *
 
1548
 *****************************************************/
 
1549
 
 
1550
 
 
1551
static GtkSizeRequestMode
 
1552
egg_wrap_box_get_request_mode (GtkWidget      *widget)
 
1553
{
 
1554
  EggWrapBox        *box = EGG_WRAP_BOX (widget);
 
1555
  EggWrapBoxPrivate *priv   = box->priv;
 
1556
 
 
1557
  return (priv->orientation == GTK_ORIENTATION_HORIZONTAL) ?
 
1558
    GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH : GTK_SIZE_REQUEST_WIDTH_FOR_HEIGHT;
 
1559
}
 
1560
 
 
1561
/* Gets the largest minimum and natural length of
 
1562
 * 'line_length' consecutive items */
 
1563
static void
 
1564
get_largest_line_length (EggWrapBox      *box,
 
1565
                         GtkOrientation   orientation,
 
1566
                         gint             line_length,
 
1567
                         gint            *min_size,
 
1568
                         gint            *nat_size)
 
1569
{
 
1570
  EggWrapBoxPrivate *priv = box->priv;
 
1571
  GList             *list, *l;
 
1572
  gint               max_min_size = 0;
 
1573
  gint               max_nat_size = 0;
 
1574
  gint               spacing;
 
1575
 
 
1576
  if (orientation == GTK_ORIENTATION_HORIZONTAL)
 
1577
    spacing = priv->horizontal_spacing;
 
1578
  else
 
1579
    spacing = priv->vertical_spacing;
 
1580
 
 
1581
  /* Get the largest size of 'line_length' consecutive items in the list.
 
1582
   */
 
1583
  for (list = priv->children; list; list = list->next)
 
1584
    {
 
1585
      gint   line_min = 0;
 
1586
      gint   line_nat = 0;
 
1587
      gint   i;
 
1588
 
 
1589
      for (l = list, i = 0; l && i < line_length; l = l->next)
 
1590
        {
 
1591
          EggWrapBoxChild *child = l->data;
 
1592
          gint             child_min, child_nat;
 
1593
 
 
1594
          if (!gtk_widget_get_visible (child->widget))
 
1595
            continue;
 
1596
 
 
1597
          if (orientation == GTK_ORIENTATION_HORIZONTAL)
 
1598
            gtk_widget_get_preferred_width (child->widget,
 
1599
                                            &child_min, &child_nat);
 
1600
          else /* GTK_ORIENTATION_VERTICAL */
 
1601
            gtk_widget_get_preferred_height (child->widget,
 
1602
                                             &child_min, &child_nat);
 
1603
 
 
1604
          line_min += child_min;
 
1605
          line_nat += child_nat;
 
1606
 
 
1607
          i++;
 
1608
        }
 
1609
 
 
1610
      max_min_size = MAX (max_min_size, line_min);
 
1611
      max_nat_size = MAX (max_nat_size, line_nat);
 
1612
    }
 
1613
 
 
1614
  max_min_size += (line_length - 1) * spacing;
 
1615
  max_nat_size += (line_length - 1) * spacing;
 
1616
 
 
1617
  if (min_size)
 
1618
    *min_size = max_min_size;
 
1619
 
 
1620
  if (nat_size)
 
1621
    *nat_size = max_nat_size;
 
1622
}
 
1623
 
 
1624
/* Gets the largest minimum and natural length of
 
1625
 * 'line_length' consecutive items when aligned into rows/columns */
 
1626
static void
 
1627
get_largest_aligned_line_length (EggWrapBox      *box,
 
1628
                                 GtkOrientation   orientation,
 
1629
                                 gint             line_length,
 
1630
                                 gint            *min_size,
 
1631
                                 gint            *nat_size)
 
1632
{
 
1633
  EggWrapBoxPrivate *priv = box->priv;
 
1634
  GList             *list;
 
1635
  gint               max_min_size = 0;
 
1636
  gint               max_nat_size = 0;
 
1637
  gint               spacing, i;
 
1638
  GtkRequestedSize  *aligned_item_sizes;
 
1639
 
 
1640
  if (orientation == GTK_ORIENTATION_HORIZONTAL)
 
1641
    spacing = priv->horizontal_spacing;
 
1642
  else
 
1643
    spacing = priv->vertical_spacing;
 
1644
 
 
1645
  aligned_item_sizes = g_new0 (GtkRequestedSize, line_length);
 
1646
 
 
1647
  /* Get the largest sizes of each index in the line.
 
1648
   */
 
1649
  for (list = priv->children, i = 0; list; list = list->next)
 
1650
    {
 
1651
      EggWrapBoxChild *child = list->data;
 
1652
      gint             child_min, child_nat;
 
1653
      
 
1654
      if (!gtk_widget_get_visible (child->widget))
 
1655
        continue;
 
1656
 
 
1657
      if (orientation == GTK_ORIENTATION_HORIZONTAL)
 
1658
        gtk_widget_get_preferred_width (child->widget,
 
1659
                                        &child_min, &child_nat);
 
1660
      else /* GTK_ORIENTATION_VERTICAL */
 
1661
        gtk_widget_get_preferred_height (child->widget,
 
1662
                                         &child_min, &child_nat);
 
1663
 
 
1664
      aligned_item_sizes[i % line_length].minimum_size = 
 
1665
        MAX (aligned_item_sizes[i % line_length].minimum_size, child_min);
 
1666
 
 
1667
      aligned_item_sizes[i % line_length].natural_size = 
 
1668
        MAX (aligned_item_sizes[i % line_length].natural_size, child_nat);
 
1669
 
 
1670
      i++;
 
1671
    }
 
1672
 
 
1673
  /* Add up the largest indexes */
 
1674
  for (i = 0; i < line_length; i++)
 
1675
    {
 
1676
      max_min_size += aligned_item_sizes[i].minimum_size;
 
1677
      max_nat_size += aligned_item_sizes[i].natural_size;
 
1678
    }
 
1679
 
 
1680
  g_free (aligned_item_sizes);
 
1681
 
 
1682
  max_min_size += (line_length - 1) * spacing;
 
1683
  max_nat_size += (line_length - 1) * spacing;
 
1684
 
 
1685
  if (min_size)
 
1686
    *min_size = max_min_size;
 
1687
 
 
1688
  if (nat_size)
 
1689
    *nat_size = max_nat_size;
 
1690
}
 
1691
 
 
1692
 
 
1693
static void
 
1694
egg_wrap_box_get_preferred_width (GtkWidget           *widget,
 
1695
                                  gint                *minimum_size,
 
1696
                                  gint                *natural_size)
 
1697
{
 
1698
  EggWrapBox        *box  = EGG_WRAP_BOX (widget);
 
1699
  EggWrapBoxPrivate *priv = box->priv;
 
1700
  gint               min_item_width, nat_item_width;
 
1701
  gint               min_items, nat_items;
 
1702
  gint               min_width, nat_width;
 
1703
 
 
1704
  min_items = MAX (1, priv->minimum_line_children);
 
1705
  nat_items = MAX (min_items, priv->natural_line_children);
 
1706
 
 
1707
  if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
 
1708
    {
 
1709
      min_width    = nat_width = 0;
 
1710
 
 
1711
      if (priv->mode == EGG_WRAP_ALLOCATE_FREE ||
 
1712
          priv->mode == EGG_WRAP_ALLOCATE_ALIGNED)
 
1713
        {
 
1714
          /* In FREE and ALIGNED modes; horizontally oriented boxes
 
1715
           * need enough width for the widest row */
 
1716
          if (min_items == 1)
 
1717
            {
 
1718
              get_average_item_size (box, GTK_ORIENTATION_HORIZONTAL,
 
1719
                                     &min_item_width, &nat_item_width);
 
1720
 
 
1721
              min_width += min_item_width;
 
1722
              nat_width += nat_item_width;
 
1723
            }
 
1724
          else if (priv->mode == EGG_WRAP_ALLOCATE_FREE)
 
1725
            {
 
1726
              gint min_line_length, nat_line_length;
 
1727
 
 
1728
              get_largest_line_length (box, GTK_ORIENTATION_HORIZONTAL, min_items,
 
1729
                                       &min_line_length, &nat_line_length);
 
1730
 
 
1731
              if (nat_items > min_items)
 
1732
                get_largest_line_length (box, GTK_ORIENTATION_HORIZONTAL, nat_items,
 
1733
                                         NULL, &nat_line_length);
 
1734
 
 
1735
              min_width += min_line_length;
 
1736
              nat_width += nat_line_length;
 
1737
            }
 
1738
          else /* EGG_WRAP_MODE_ALIGNED */
 
1739
            {
 
1740
              gint min_line_length, nat_line_length;
 
1741
 
 
1742
              get_largest_aligned_line_length (box, GTK_ORIENTATION_HORIZONTAL, min_items,
 
1743
                                               &min_line_length, &nat_line_length);
 
1744
 
 
1745
              if (nat_items > min_items)
 
1746
                get_largest_aligned_line_length (box, GTK_ORIENTATION_HORIZONTAL, nat_items,
 
1747
                                                 NULL, &nat_line_length);
 
1748
 
 
1749
              min_width += min_line_length;
 
1750
              nat_width += nat_line_length;
 
1751
            }
 
1752
        }
 
1753
      else /* In HOMOGENEOUS mode; horizontally oriented boxs
 
1754
            * give the same width to all children */
 
1755
        {
 
1756
          get_average_item_size (box, GTK_ORIENTATION_HORIZONTAL,
 
1757
                                 &min_item_width, &nat_item_width);
 
1758
 
 
1759
          min_width += min_item_width * min_items;
 
1760
          min_width += (min_items -1) * priv->horizontal_spacing;
 
1761
 
 
1762
          nat_width += nat_item_width * nat_items;
 
1763
          nat_width += (nat_items -1) * priv->horizontal_spacing;
 
1764
        }
 
1765
    }
 
1766
  else /* GTK_ORIENTATION_VERTICAL */
 
1767
    {
 
1768
      /* Return the width for the minimum height */
 
1769
      gint min_height;
 
1770
 
 
1771
      GTK_WIDGET_GET_CLASS (widget)->get_preferred_height (widget, &min_height, NULL);
 
1772
      GTK_WIDGET_GET_CLASS (widget)->get_preferred_width_for_height (widget, min_height,
 
1773
                                                                           &min_width, &nat_width);
 
1774
 
 
1775
    }
 
1776
 
 
1777
  if (minimum_size)
 
1778
    *minimum_size = min_width;
 
1779
 
 
1780
  if (natural_size)
 
1781
    *natural_size = nat_width;
 
1782
}
 
1783
 
 
1784
static void
 
1785
egg_wrap_box_get_preferred_height (GtkWidget           *widget,
 
1786
                                   gint                *minimum_size,
 
1787
                                   gint                *natural_size)
 
1788
{
 
1789
  EggWrapBox        *box  = EGG_WRAP_BOX (widget);
 
1790
  EggWrapBoxPrivate *priv = box->priv;
 
1791
  gint               min_item_height, nat_item_height;
 
1792
  gint               min_items, nat_items;
 
1793
  gint               min_height, nat_height;
 
1794
 
 
1795
  min_items = MAX (1, priv->minimum_line_children);
 
1796
  nat_items = MAX (min_items, priv->natural_line_children);
 
1797
 
 
1798
  if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
 
1799
    {
 
1800
      /* Return the height for the minimum width */
 
1801
      gint min_width;
 
1802
 
 
1803
      GTK_WIDGET_GET_CLASS (widget)->get_preferred_width (widget, &min_width, NULL);
 
1804
      GTK_WIDGET_GET_CLASS (widget)->get_preferred_height_for_width (widget, min_width,
 
1805
                                                                           &min_height, &nat_height);
 
1806
    }
 
1807
  else /* GTK_ORIENTATION_VERTICAL */
 
1808
    {
 
1809
      min_height   = nat_height = 0;
 
1810
 
 
1811
      if (priv->mode == EGG_WRAP_ALLOCATE_FREE ||
 
1812
          priv->mode == EGG_WRAP_ALLOCATE_ALIGNED)
 
1813
        {
 
1814
          /* In FREE and ALIGNED modes; vertically oriented boxes
 
1815
           * need enough height for the tallest column */
 
1816
          if (min_items == 1)
 
1817
            {
 
1818
              get_average_item_size (box, GTK_ORIENTATION_VERTICAL,
 
1819
                                     &min_item_height, &nat_item_height);
 
1820
 
 
1821
              min_height += min_item_height;
 
1822
              nat_height += nat_item_height;
 
1823
            }
 
1824
          else if (priv->mode == EGG_WRAP_ALLOCATE_FREE)
 
1825
            {
 
1826
              gint min_line_length, nat_line_length;
 
1827
 
 
1828
              get_largest_line_length (box, GTK_ORIENTATION_VERTICAL, min_items,
 
1829
                                       &min_line_length, &nat_line_length);
 
1830
 
 
1831
              if (nat_items > min_items)
 
1832
                get_largest_line_length (box, GTK_ORIENTATION_VERTICAL, nat_items,
 
1833
                                         NULL, &nat_line_length);
 
1834
 
 
1835
              min_height += min_line_length;
 
1836
              nat_height += nat_line_length;
 
1837
            }
 
1838
          else /* EGG_WRAP_ALLOCATE_ALIGNED */
 
1839
            {
 
1840
              gint min_line_length, nat_line_length;
 
1841
 
 
1842
              get_largest_aligned_line_length (box, GTK_ORIENTATION_VERTICAL, min_items,
 
1843
                                               &min_line_length, &nat_line_length);
 
1844
 
 
1845
              if (nat_items > min_items)
 
1846
                get_largest_aligned_line_length (box, GTK_ORIENTATION_VERTICAL, nat_items,
 
1847
                                                 NULL, &nat_line_length);
 
1848
 
 
1849
              min_height += min_line_length;
 
1850
              nat_height += nat_line_length;
 
1851
            }
 
1852
 
 
1853
        }
 
1854
      else /* In HOMOGENEOUS mode; vertically oriented boxs
 
1855
            * give the same height to all children */
 
1856
        {
 
1857
          get_average_item_size (box, GTK_ORIENTATION_VERTICAL,
 
1858
                                 &min_item_height, &nat_item_height);
 
1859
 
 
1860
          min_height += min_item_height * min_items;
 
1861
          min_height += (min_items -1) * priv->vertical_spacing;
 
1862
 
 
1863
          nat_height += nat_item_height * nat_items;
 
1864
          nat_height += (nat_items -1) * priv->vertical_spacing;
 
1865
        }
 
1866
    }
 
1867
 
 
1868
  if (minimum_size)
 
1869
    *minimum_size = min_height;
 
1870
 
 
1871
  if (natural_size)
 
1872
    *natural_size = nat_height;
 
1873
}
 
1874
 
 
1875
static void
 
1876
egg_wrap_box_get_preferred_height_for_width (GtkWidget           *widget,
 
1877
                                             gint                 width,
 
1878
                                             gint                *minimum_height,
 
1879
                                             gint                *natural_height)
 
1880
{
 
1881
  EggWrapBox        *box = EGG_WRAP_BOX (widget);
 
1882
  EggWrapBoxPrivate *priv   = box->priv;
 
1883
  gint               min_item_width, nat_item_width;
 
1884
  gint               min_items;
 
1885
  gint               min_height, nat_height;
 
1886
  gint               avail_size, n_children;
 
1887
 
 
1888
  min_items = MAX (1, priv->minimum_line_children);
 
1889
 
 
1890
  min_height = 0;
 
1891
  nat_height = 0;
 
1892
 
 
1893
  if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
 
1894
    {
 
1895
      gint min_width;
 
1896
 
 
1897
      n_children = get_visible_children (box);
 
1898
 
 
1899
      /* Make sure its no smaller than the minimum */
 
1900
      GTK_WIDGET_GET_CLASS (widget)->get_preferred_width (widget, &min_width, NULL);
 
1901
 
 
1902
      avail_size  = MAX (width, min_width);
 
1903
 
 
1904
      if (priv->mode == EGG_WRAP_ALLOCATE_ALIGNED ||
 
1905
          priv->mode == EGG_WRAP_ALLOCATE_HOMOGENEOUS)
 
1906
        {
 
1907
          gint line_length;
 
1908
          gint item_size, extra_pixels;
 
1909
 
 
1910
          get_average_item_size (box, GTK_ORIENTATION_HORIZONTAL, &min_item_width, &nat_item_width);
 
1911
 
 
1912
          /* By default wrap at the natural item width */
 
1913
          line_length = avail_size / (nat_item_width + priv->horizontal_spacing);
 
1914
 
 
1915
          /* After the above aproximation, check if we cant fit one more on the line */
 
1916
          if (line_length * priv->horizontal_spacing + (line_length + 1) * nat_item_width <= avail_size)
 
1917
            line_length++;
 
1918
 
 
1919
          /* Its possible we were allocated just less than the natural width of the
 
1920
           * minimum item wrap length */
 
1921
          line_length = MAX (min_items, line_length);
 
1922
 
 
1923
          /* Now we need the real item allocation size */
 
1924
          item_size = (avail_size - (line_length - 1) * priv->horizontal_spacing) / line_length;
 
1925
 
 
1926
          /* Cut out the expand space if we're not distributing any */
 
1927
          if (priv->horizontal_spreading != EGG_WRAP_BOX_SPREAD_EXPAND)
 
1928
            {
 
1929
              item_size    = MIN (item_size, nat_item_width);
 
1930
              extra_pixels = 0;
 
1931
            }
 
1932
          else
 
1933
            /* Collect the extra pixels for expand children */
 
1934
            extra_pixels = (avail_size - (line_length - 1) * priv->horizontal_spacing) % line_length;
 
1935
 
 
1936
          if (priv->mode == EGG_WRAP_ALLOCATE_HOMOGENEOUS)
 
1937
            {
 
1938
              gint min_item_height, nat_item_height;
 
1939
              gint lines;
 
1940
 
 
1941
              /* Here we just use the largest height-for-width and
 
1942
               * add up the size accordingly */
 
1943
              get_largest_size_for_opposing_orientation (box, GTK_ORIENTATION_HORIZONTAL, item_size,
 
1944
                                                         &min_item_height, &nat_item_height);
 
1945
 
 
1946
              /* Round up how many lines we need to allocate for */
 
1947
              lines      = n_children / line_length;
 
1948
              if ((n_children % line_length) > 0)
 
1949
                lines++;
 
1950
 
 
1951
              min_height = min_item_height * lines;
 
1952
              nat_height = nat_item_height * lines;
 
1953
 
 
1954
              min_height += (lines - 1) * priv->vertical_spacing;
 
1955
              nat_height += (lines - 1) * priv->vertical_spacing;
 
1956
            }
 
1957
          else /* EGG_WRAP_ALLOCATE_ALIGNED */
 
1958
            {
 
1959
              GList *list = priv->children;
 
1960
              gint min_line_height, nat_line_height, i;
 
1961
              gboolean first_line = TRUE;
 
1962
              GtkRequestedSize *item_sizes;
 
1963
 
 
1964
              /* First get the size each set of items take to span the line
 
1965
               * when aligning the items above and below after wrapping.
 
1966
               */
 
1967
              item_sizes = fit_aligned_item_requests (box, priv->orientation, avail_size,
 
1968
                                                      priv->horizontal_spacing, &line_length, n_children);
 
1969
 
 
1970
 
 
1971
              /* Get the available remaining size */
 
1972
              avail_size -= (line_length - 1) * priv->horizontal_spacing;
 
1973
              for (i = 0; i < line_length; i++)
 
1974
                avail_size -= item_sizes[i].minimum_size;
 
1975
 
 
1976
              extra_pixels = gtk_distribute_natural_allocation (avail_size, line_length, item_sizes);
 
1977
 
 
1978
              while (list != NULL)
 
1979
                {
 
1980
                  list =
 
1981
                    get_largest_size_for_line_in_opposing_orientation (box, GTK_ORIENTATION_HORIZONTAL,
 
1982
                                                                       list, line_length,
 
1983
                                                                       item_sizes, extra_pixels,
 
1984
                                                                       &min_line_height, &nat_line_height);
 
1985
 
 
1986
                  /* Its possible the line only had invisible widgets */
 
1987
                  if (nat_line_height > 0)
 
1988
                    {
 
1989
                      if (first_line)
 
1990
                        first_line = FALSE;
 
1991
                      else
 
1992
                        {
 
1993
                          min_height += priv->vertical_spacing;
 
1994
                          nat_height += priv->vertical_spacing;
 
1995
                        }
 
1996
 
 
1997
                      min_height += min_line_height;
 
1998
                      nat_height += nat_line_height;
 
1999
                    }
 
2000
                }
 
2001
 
 
2002
              g_free (item_sizes);
 
2003
            }
 
2004
        }
 
2005
      else /* EGG_WRAP_ALLOCATE_FREE */
 
2006
        {
 
2007
          /* Here we just fit as many children as we can allocate their natural size to
 
2008
           * on each line and add the heights for each of them on each line */
 
2009
          GList *list = priv->children;
 
2010
          gint min_line_height = 0, nat_line_height = 0;
 
2011
          gboolean first_line = TRUE;
 
2012
 
 
2013
          while (list != NULL)
 
2014
            {
 
2015
              list =
 
2016
                get_largest_size_for_free_line_in_opposing_orientation (box, GTK_ORIENTATION_HORIZONTAL,
 
2017
                                                                        list, min_items, avail_size,
 
2018
                                                                        &min_line_height, &nat_line_height,
 
2019
                                                                        NULL, NULL);
 
2020
 
 
2021
              /* Its possible the last line only had invisible widgets */
 
2022
              if (nat_line_height > 0)
 
2023
                {
 
2024
                  if (first_line)
 
2025
                    first_line = FALSE;
 
2026
                  else
 
2027
                    {
 
2028
                      min_height += priv->vertical_spacing;
 
2029
                      nat_height += priv->vertical_spacing;
 
2030
                    }
 
2031
 
 
2032
                  min_height += min_line_height;
 
2033
                  nat_height += nat_line_height;
 
2034
                }
 
2035
            }
 
2036
        }
 
2037
    }
 
2038
  else /* GTK_ORIENTATION_VERTICAL */
 
2039
    {
 
2040
      /* Return the minimum height */
 
2041
      GTK_WIDGET_GET_CLASS (widget)->get_preferred_height (widget, &min_height, &nat_height);
 
2042
    }
 
2043
 
 
2044
  if (minimum_height)
 
2045
    *minimum_height = min_height;
 
2046
 
 
2047
  if (natural_height)
 
2048
    *natural_height = nat_height;
 
2049
}
 
2050
 
 
2051
static void
 
2052
egg_wrap_box_get_preferred_width_for_height (GtkWidget           *widget,
 
2053
                                             gint                 height,
 
2054
                                             gint                *minimum_width,
 
2055
                                             gint                *natural_width)
 
2056
{
 
2057
  EggWrapBox        *box = EGG_WRAP_BOX (widget);
 
2058
  EggWrapBoxPrivate *priv   = box->priv;
 
2059
  gint               min_item_height, nat_item_height;
 
2060
  gint               min_items;
 
2061
  gint               min_width, nat_width;
 
2062
  gint               avail_size, n_children;
 
2063
 
 
2064
  min_items = MAX (1, priv->minimum_line_children);
 
2065
 
 
2066
  min_width = 0;
 
2067
  nat_width = 0;
 
2068
 
 
2069
  if (priv->orientation == GTK_ORIENTATION_HORIZONTAL)
 
2070
    {
 
2071
      /* Return the minimum width */
 
2072
      GTK_WIDGET_GET_CLASS (widget)->get_preferred_width (widget, &min_width, &nat_width);
 
2073
    }
 
2074
  else /* GTK_ORIENTATION_VERTICAL */
 
2075
    {
 
2076
      gint min_height;
 
2077
 
 
2078
      n_children = get_visible_children (box);
 
2079
 
 
2080
      /* Make sure its no smaller than the minimum */
 
2081
      GTK_WIDGET_GET_CLASS (widget)->get_preferred_height (widget, &min_height, NULL);
 
2082
 
 
2083
      avail_size  = MAX (height, min_height);
 
2084
 
 
2085
      if (priv->mode == EGG_WRAP_ALLOCATE_ALIGNED ||
 
2086
          priv->mode == EGG_WRAP_ALLOCATE_HOMOGENEOUS)
 
2087
        {
 
2088
          gint line_length;
 
2089
          gint item_size, extra_pixels;
 
2090
 
 
2091
          get_average_item_size (box, GTK_ORIENTATION_VERTICAL, &min_item_height, &nat_item_height);
 
2092
 
 
2093
          /* By default wrap at the natural item width */
 
2094
          line_length = avail_size / (nat_item_height + priv->vertical_spacing);
 
2095
 
 
2096
          /* After the above aproximation, check if we cant fit one more on the line */
 
2097
          if (line_length * priv->vertical_spacing + (line_length + 1) * nat_item_height <= avail_size)
 
2098
            line_length++;
 
2099
 
 
2100
          /* Its possible we were allocated just less than the natural width of the
 
2101
           * minimum item wrap length */
 
2102
          line_length = MAX (min_items, line_length);
 
2103
 
 
2104
          /* Now we need the real item allocation size */
 
2105
          item_size = (avail_size - (line_length - 1) * priv->vertical_spacing) / line_length;
 
2106
 
 
2107
          /* Cut out the expand space if we're not distributing any */
 
2108
          if (priv->vertical_spreading != EGG_WRAP_BOX_SPREAD_EXPAND)
 
2109
            {
 
2110
              item_size    = MIN (item_size, nat_item_height);
 
2111
              extra_pixels = 0;
 
2112
            }
 
2113
          else
 
2114
            /* Collect the extra pixels for expand children */
 
2115
            extra_pixels = (avail_size - (line_length - 1) * priv->vertical_spacing) % line_length;
 
2116
 
 
2117
          if (priv->mode == EGG_WRAP_ALLOCATE_HOMOGENEOUS)
 
2118
            {
 
2119
              gint min_item_width, nat_item_width;
 
2120
              gint lines;
 
2121
 
 
2122
              /* Here we just use the largest height-for-width and
 
2123
               * add up the size accordingly */
 
2124
              get_largest_size_for_opposing_orientation (box, GTK_ORIENTATION_VERTICAL, item_size,
 
2125
                                                         &min_item_width, &nat_item_width);
 
2126
 
 
2127
              /* Round up how many lines we need to allocate for */
 
2128
              n_children = get_visible_children (box);
 
2129
              lines      = n_children / line_length;
 
2130
              if ((n_children % line_length) > 0)
 
2131
                lines++;
 
2132
 
 
2133
              min_width = min_item_width * lines;
 
2134
              nat_width = nat_item_width * lines;
 
2135
 
 
2136
              min_width += (lines - 1) * priv->horizontal_spacing;
 
2137
              nat_width += (lines - 1) * priv->horizontal_spacing;
 
2138
            }
 
2139
          else /* EGG_WRAP_ALLOCATE_ALIGNED */
 
2140
            {
 
2141
              GList *list = priv->children;
 
2142
              gint min_line_width, nat_line_width, i;
 
2143
              gboolean first_line = TRUE;
 
2144
              GtkRequestedSize *item_sizes;
 
2145
 
 
2146
              /* First get the size each set of items take to span the line
 
2147
               * when aligning the items above and below after wrapping.
 
2148
               */
 
2149
              item_sizes = fit_aligned_item_requests (box, priv->orientation, avail_size,
 
2150
                                                      priv->vertical_spacing, &line_length, n_children);
 
2151
 
 
2152
              /* Get the available remaining size */
 
2153
              avail_size -= (line_length - 1) * priv->horizontal_spacing;
 
2154
              for (i = 0; i < line_length; i++)
 
2155
                avail_size -= item_sizes[i].minimum_size;
 
2156
 
 
2157
              extra_pixels = gtk_distribute_natural_allocation (avail_size, line_length, item_sizes);
 
2158
 
 
2159
              while (list != NULL)
 
2160
                {
 
2161
                  list =
 
2162
                    get_largest_size_for_line_in_opposing_orientation (box, GTK_ORIENTATION_VERTICAL,
 
2163
                                                                       list, line_length,
 
2164
                                                                       item_sizes, extra_pixels,
 
2165
                                                                       &min_line_width, &nat_line_width);
 
2166
 
 
2167
                  /* Its possible the last line only had invisible widgets */
 
2168
                  if (nat_line_width > 0)
 
2169
                    {
 
2170
                      if (first_line)
 
2171
                        first_line = FALSE;
 
2172
                      else
 
2173
                        {
 
2174
                          min_width += priv->horizontal_spacing;
 
2175
                          nat_width += priv->horizontal_spacing;
 
2176
                        }
 
2177
 
 
2178
                      min_width += min_line_width;
 
2179
                      nat_width += nat_line_width;
 
2180
                    }
 
2181
                }
 
2182
              g_free (item_sizes);
 
2183
            }
 
2184
        }
 
2185
      else /* EGG_WRAP_ALLOCATE_FREE */
 
2186
        {
 
2187
          /* Here we just fit as many children as we can allocate their natural size to
 
2188
           * on each line and add the heights for each of them on each line */
 
2189
          GList *list = priv->children;
 
2190
          gint min_line_width = 0, nat_line_width = 0;
 
2191
          gboolean first_line = TRUE;
 
2192
 
 
2193
          while (list != NULL)
 
2194
            {
 
2195
              list =
 
2196
                get_largest_size_for_free_line_in_opposing_orientation (box, GTK_ORIENTATION_VERTICAL,
 
2197
                                                                        list, min_items, avail_size,
 
2198
                                                                        &min_line_width, &nat_line_width,
 
2199
                                                                        NULL, NULL);
 
2200
 
 
2201
              /* Its possible the last line only had invisible widgets */
 
2202
              if (nat_line_width > 0)
 
2203
                {
 
2204
                  if (first_line)
 
2205
                    first_line = FALSE;
 
2206
                  else
 
2207
                    {
 
2208
                      min_width += priv->horizontal_spacing;
 
2209
                      nat_width += priv->horizontal_spacing;
 
2210
                    }
 
2211
 
 
2212
                  min_width += min_line_width;
 
2213
                  nat_width += nat_line_width;
 
2214
                }
 
2215
            }
 
2216
        }
 
2217
    }
 
2218
 
 
2219
  if (minimum_width)
 
2220
    *minimum_width = min_width;
 
2221
 
 
2222
  if (natural_width)
 
2223
    *natural_width = nat_width;
 
2224
}
 
2225
 
 
2226
/*****************************************************
 
2227
 *                       API                         *
 
2228
 *****************************************************/
 
2229
 
 
2230
/**
 
2231
 * egg_wrap_box_new:
 
2232
 * @mode: The #EggWrapAllocationMode to use
 
2233
 * @horizontal_spreading: The horizontal #EggWrapBoxSpreading policy to use
 
2234
 * @vertical_spreading: The vertical #EggWrapBoxSpreading policy to use
 
2235
 * @horizontal_spacing: The horizontal spacing to add between children
 
2236
 * @vertical_spacing: The vertical spacing to add between children
 
2237
 *
 
2238
 * Creates an #EggWrapBox.
 
2239
 *
 
2240
 * Returns: A new #EggWrapBox container
 
2241
 */
 
2242
GtkWidget *
 
2243
egg_wrap_box_new (EggWrapAllocationMode mode,
 
2244
                  EggWrapBoxSpreading   horizontal_spreading,
 
2245
                  EggWrapBoxSpreading   vertical_spreading,
 
2246
                  guint                 horizontal_spacing,
 
2247
                  guint                 vertical_spacing)
 
2248
{
 
2249
  return (GtkWidget *)g_object_new (EGG_TYPE_WRAP_BOX,
 
2250
                                    "allocation-mode", mode,
 
2251
                                    "horizontal-spreading", horizontal_spreading,
 
2252
                                    "vertical-spreading", vertical_spreading,
 
2253
                                    "vertical-spacing", vertical_spacing,
 
2254
                                    "horizontal-spacing", horizontal_spacing,
 
2255
                                    NULL);
 
2256
}
 
2257
 
 
2258
/**
 
2259
 * egg_wrap_box_set_allocation_mode:
 
2260
 * @box: An #EggWrapBox
 
2261
 * @mode: The #EggWrapAllocationMode to use.
 
2262
 *
 
2263
 * Sets the allocation mode for @box's children.
 
2264
 */
 
2265
void
 
2266
egg_wrap_box_set_allocation_mode (EggWrapBox           *box,
 
2267
                                  EggWrapAllocationMode mode)
 
2268
{
 
2269
  EggWrapBoxPrivate *priv;
 
2270
 
 
2271
  g_return_if_fail (EGG_IS_WRAP_BOX (box));
 
2272
 
 
2273
  priv = box->priv;
 
2274
 
 
2275
  if (priv->mode != mode)
 
2276
    {
 
2277
      priv->mode = mode;
 
2278
 
 
2279
      gtk_widget_queue_resize (GTK_WIDGET (box));
 
2280
 
 
2281
      g_object_notify (G_OBJECT (box), "allocation-mode");
 
2282
    }
 
2283
}
 
2284
 
 
2285
/**
 
2286
 * egg_wrap_box_get_allocation_mode:
 
2287
 * @box: An #EggWrapBox
 
2288
 *
 
2289
 * Gets the allocation mode.
 
2290
 *
 
2291
 * Returns: The #EggWrapAllocationMode for @box.
 
2292
 */
 
2293
EggWrapAllocationMode
 
2294
egg_wrap_box_get_allocation_mode (EggWrapBox *box)
 
2295
{
 
2296
  g_return_val_if_fail (EGG_IS_WRAP_BOX (box), FALSE);
 
2297
 
 
2298
  return box->priv->mode;
 
2299
}
 
2300
 
 
2301
 
 
2302
/**
 
2303
 * egg_wrap_box_set_horizontal_spreading:
 
2304
 * @box: An #EggWrapBox
 
2305
 * @spreading: The #EggWrapBoxSpreading to use.
 
2306
 *
 
2307
 * Sets the horizontal spreading mode for @box's children.
 
2308
 */
 
2309
void
 
2310
egg_wrap_box_set_horizontal_spreading (EggWrapBox          *box,
 
2311
                                       EggWrapBoxSpreading  spreading)
 
2312
{
 
2313
  EggWrapBoxPrivate *priv;
 
2314
 
 
2315
  g_return_if_fail (EGG_IS_WRAP_BOX (box));
 
2316
 
 
2317
  priv = box->priv;
 
2318
 
 
2319
  if (priv->horizontal_spreading != spreading)
 
2320
    {
 
2321
      priv->horizontal_spreading = spreading;
 
2322
 
 
2323
      gtk_widget_queue_resize (GTK_WIDGET (box));
 
2324
 
 
2325
      g_object_notify (G_OBJECT (box), "horizontal-spreading");
 
2326
    }
 
2327
}
 
2328
 
 
2329
/**
 
2330
 * egg_wrap_box_get_horizontal_spreading:
 
2331
 * @box: An #EggWrapBox
 
2332
 *
 
2333
 * Gets the horizontal spreading mode.
 
2334
 *
 
2335
 * Returns: The horizontal #EggWrapBoxSpreading for @box.
 
2336
 */
 
2337
EggWrapBoxSpreading
 
2338
egg_wrap_box_get_horizontal_spreading (EggWrapBox *box)
 
2339
{
 
2340
  g_return_val_if_fail (EGG_IS_WRAP_BOX (box), FALSE);
 
2341
 
 
2342
  return box->priv->horizontal_spreading;
 
2343
}
 
2344
 
 
2345
 
 
2346
/**
 
2347
 * egg_wrap_box_set_vertical_spreading:
 
2348
 * @box: An #EggWrapBox
 
2349
 * @spreading: The #EggWrapBoxSpreading to use.
 
2350
 *
 
2351
 * Sets the vertical spreading mode for @box's children.
 
2352
 */
 
2353
void
 
2354
egg_wrap_box_set_vertical_spreading (EggWrapBox          *box,
 
2355
                                     EggWrapBoxSpreading  spreading)
 
2356
{
 
2357
  EggWrapBoxPrivate *priv;
 
2358
 
 
2359
  g_return_if_fail (EGG_IS_WRAP_BOX (box));
 
2360
 
 
2361
  priv = box->priv;
 
2362
 
 
2363
  if (priv->vertical_spreading != spreading)
 
2364
    {
 
2365
      priv->vertical_spreading = spreading;
 
2366
 
 
2367
      gtk_widget_queue_resize (GTK_WIDGET (box));
 
2368
 
 
2369
      g_object_notify (G_OBJECT (box), "vertical-spreading");
 
2370
    }
 
2371
}
 
2372
 
 
2373
/**
 
2374
 * egg_wrap_box_get_vertical_spreading:
 
2375
 * @box: An #EggWrapBox
 
2376
 *
 
2377
 * Gets the vertical spreading mode.
 
2378
 *
 
2379
 * Returns: The vertical #EggWrapBoxSpreading for @box.
 
2380
 */
 
2381
EggWrapBoxSpreading
 
2382
egg_wrap_box_get_vertical_spreading (EggWrapBox *box)
 
2383
{
 
2384
  g_return_val_if_fail (EGG_IS_WRAP_BOX (box), FALSE);
 
2385
 
 
2386
  return box->priv->vertical_spreading;
 
2387
}
 
2388
 
 
2389
 
 
2390
/**
 
2391
 * egg_wrap_box_set_vertical_spacing:
 
2392
 * @box: An #EggWrapBox
 
2393
 * @spacing: The spacing to use.
 
2394
 *
 
2395
 * Sets the vertical space to add between children.
 
2396
 */
 
2397
void
 
2398
egg_wrap_box_set_vertical_spacing  (EggWrapBox    *box,
 
2399
                                    guint          spacing)
 
2400
{
 
2401
  EggWrapBoxPrivate *priv;
 
2402
 
 
2403
  g_return_if_fail (EGG_IS_WRAP_BOX (box));
 
2404
 
 
2405
  priv = box->priv;
 
2406
 
 
2407
  if (priv->vertical_spacing != spacing)
 
2408
    {
 
2409
      priv->vertical_spacing = spacing;
 
2410
 
 
2411
      gtk_widget_queue_resize (GTK_WIDGET (box));
 
2412
 
 
2413
      g_object_notify (G_OBJECT (box), "vertical-spacing");
 
2414
    }
 
2415
}
 
2416
 
 
2417
/**
 
2418
 * egg_wrap_box_get_vertical_spacing:
 
2419
 * @box: An #EggWrapBox
 
2420
 *
 
2421
 * Gets the vertical spacing.
 
2422
 *
 
2423
 * Returns: The vertical spacing.
 
2424
 */
 
2425
guint
 
2426
egg_wrap_box_get_vertical_spacing  (EggWrapBox *box)
 
2427
{
 
2428
  g_return_val_if_fail (EGG_IS_WRAP_BOX (box), FALSE);
 
2429
 
 
2430
  return box->priv->vertical_spacing;
 
2431
}
 
2432
 
 
2433
/**
 
2434
 * egg_wrap_box_set_horizontal_spacing:
 
2435
 * @box: An #EggWrapBox
 
2436
 * @spacing: The spacing to use.
 
2437
 *
 
2438
 * Sets the horizontal space to add between children.
 
2439
 */
 
2440
void
 
2441
egg_wrap_box_set_horizontal_spacing (EggWrapBox    *box,
 
2442
                                     guint          spacing)
 
2443
{
 
2444
  EggWrapBoxPrivate *priv;
 
2445
 
 
2446
  g_return_if_fail (EGG_IS_WRAP_BOX (box));
 
2447
 
 
2448
  priv = box->priv;
 
2449
 
 
2450
  if (priv->horizontal_spacing != spacing)
 
2451
    {
 
2452
      priv->horizontal_spacing = spacing;
 
2453
 
 
2454
      gtk_widget_queue_resize (GTK_WIDGET (box));
 
2455
 
 
2456
      g_object_notify (G_OBJECT (box), "horizontal-spacing");
 
2457
    }
 
2458
}
 
2459
 
 
2460
/**
 
2461
 * egg_wrap_box_get_horizontal_spacing:
 
2462
 * @box: An #EggWrapBox
 
2463
 *
 
2464
 * Gets the horizontal spacing.
 
2465
 *
 
2466
 * Returns: The horizontal spacing.
 
2467
 */
 
2468
guint
 
2469
egg_wrap_box_get_horizontal_spacing (EggWrapBox *box)
 
2470
{
 
2471
  g_return_val_if_fail (EGG_IS_WRAP_BOX (box), FALSE);
 
2472
 
 
2473
  return box->priv->horizontal_spacing;
 
2474
}
 
2475
 
 
2476
/**
 
2477
 * egg_wrap_box_set_minimum_line_children:
 
2478
 * @box: An #EggWrapBox
 
2479
 * @n_children: The minimum amount of children per line.
 
2480
 *
 
2481
 * Sets the minimum amount of children to line up
 
2482
 * in @box's orientation before wrapping.
 
2483
 */
 
2484
void
 
2485
egg_wrap_box_set_minimum_line_children (EggWrapBox *box,
 
2486
                                        guint       n_children)
 
2487
{
 
2488
  EggWrapBoxPrivate *priv;
 
2489
 
 
2490
  g_return_if_fail (EGG_IS_WRAP_BOX (box));
 
2491
 
 
2492
  priv = box->priv;
 
2493
 
 
2494
  if (priv->minimum_line_children != n_children)
 
2495
    {
 
2496
      priv->minimum_line_children = n_children;
 
2497
 
 
2498
      gtk_widget_queue_resize (GTK_WIDGET (box));
 
2499
 
 
2500
      g_object_notify (G_OBJECT (box), "minimum-line-children");
 
2501
    }
 
2502
}
 
2503
 
 
2504
/**
 
2505
 * egg_wrap_box_get_minimum_line_children:
 
2506
 * @box: An #EggWrapBox
 
2507
 *
 
2508
 * Gets the minimum amount of children per line.
 
2509
 *
 
2510
 * Returns: The minimum amount of children per line.
 
2511
 */
 
2512
guint
 
2513
egg_wrap_box_get_minimum_line_children (EggWrapBox *box)
 
2514
{
 
2515
  g_return_val_if_fail (EGG_IS_WRAP_BOX (box), FALSE);
 
2516
 
 
2517
  return box->priv->minimum_line_children;
 
2518
}
 
2519
 
 
2520
/**
 
2521
 * egg_wrap_box_set_natural_line_children:
 
2522
 * @box: An #EggWrapBox
 
2523
 * @n_children: The natural amount of children per line.
 
2524
 *
 
2525
 * Sets the natural length of items to request and
 
2526
 * allocate space for in @box's orientation.
 
2527
 *
 
2528
 * Setting the natural amount of children per line
 
2529
 * limits the overall natural size request to be no more
 
2530
 * than @n_children items long in the given orientation.
 
2531
 */
 
2532
void
 
2533
egg_wrap_box_set_natural_line_children (EggWrapBox *box,
 
2534
                                        guint       n_children)
 
2535
{
 
2536
  EggWrapBoxPrivate *priv;
 
2537
 
 
2538
  g_return_if_fail (EGG_IS_WRAP_BOX (box));
 
2539
 
 
2540
  priv = box->priv;
 
2541
 
 
2542
  if (priv->natural_line_children != n_children)
 
2543
    {
 
2544
      priv->natural_line_children = n_children;
 
2545
 
 
2546
      gtk_widget_queue_resize (GTK_WIDGET (box));
 
2547
 
 
2548
      g_object_notify (G_OBJECT (box), "natural-line-children");
 
2549
    }
 
2550
}
 
2551
 
 
2552
/**
 
2553
 * egg_wrap_box_get_natural_line_children:
 
2554
 * @box: An #EggWrapBox
 
2555
 *
 
2556
 * Gets the natural amount of children per line.
 
2557
 *
 
2558
 * Returns: The natural amount of children per line.
 
2559
 */
 
2560
guint
 
2561
egg_wrap_box_get_natural_line_children (EggWrapBox *box)
 
2562
{
 
2563
  g_return_val_if_fail (EGG_IS_WRAP_BOX (box), FALSE);
 
2564
 
 
2565
  return box->priv->natural_line_children;
 
2566
}
 
2567
 
 
2568
 
 
2569
/**
 
2570
 * egg_wrap_box_insert_child:
 
2571
 * @box: And #EggWrapBox
 
2572
 * @widget: the child #GtkWidget to add
 
2573
 * @index: the position in the child list to insert, specify -1 to append to the list.
 
2574
 * @packing: The #EggWrapBoxPacking options to use.
 
2575
 *
 
2576
 * Adds a child to an #EggWrapBox with its packing options set
 
2577
 *
 
2578
 */
 
2579
void
 
2580
egg_wrap_box_insert_child (EggWrapBox        *box,
 
2581
                           GtkWidget         *widget,
 
2582
                           gint               index,
 
2583
                           EggWrapBoxPacking  packing)
 
2584
{
 
2585
  EggWrapBoxPrivate *priv;
 
2586
  EggWrapBoxChild   *child;
 
2587
  GList             *list;
 
2588
 
 
2589
  g_return_if_fail (EGG_IS_WRAP_BOX (box));
 
2590
  g_return_if_fail (GTK_IS_WIDGET (widget));
 
2591
 
 
2592
  priv = box->priv;
 
2593
 
 
2594
  list = g_list_find_custom (priv->children, widget,
 
2595
                             (GCompareFunc)find_child_in_list);
 
2596
  g_return_if_fail (list == NULL);
 
2597
 
 
2598
  child           = g_slice_new0 (EggWrapBoxChild);
 
2599
  child->widget   = widget;
 
2600
  child->packing  = packing;
 
2601
 
 
2602
  priv->children = g_list_insert (priv->children, child, index);
 
2603
 
 
2604
  gtk_widget_set_parent (widget, GTK_WIDGET (box));
 
2605
}
 
2606
 
 
2607
/**
 
2608
 * egg_wrap_box_reorder_child:
 
2609
 * @box: An #EggWrapBox
 
2610
 * @widget: The child to reorder
 
2611
 * @index: The new child position
 
2612
 *
 
2613
 * Reorders the child @widget in @box's list of children.
 
2614
 */
 
2615
void
 
2616
egg_wrap_box_reorder_child (EggWrapBox *box,
 
2617
                            GtkWidget  *widget,
 
2618
                            guint       index)
 
2619
{
 
2620
  EggWrapBoxPrivate *priv;
 
2621
  EggWrapBoxChild   *child;
 
2622
  GList             *list;
 
2623
 
 
2624
  g_return_if_fail (EGG_IS_WRAP_BOX (box));
 
2625
  g_return_if_fail (GTK_IS_WIDGET (widget));
 
2626
 
 
2627
  priv = box->priv;
 
2628
 
 
2629
  list = g_list_find_custom (priv->children, widget,
 
2630
                             (GCompareFunc)find_child_in_list);
 
2631
  g_return_if_fail (list != NULL);
 
2632
 
 
2633
  if (g_list_position (priv->children, list) != index)
 
2634
    {
 
2635
      child = list->data;
 
2636
      priv->children = g_list_delete_link (priv->children, list);
 
2637
      priv->children = g_list_insert (priv->children, child, index);
 
2638
 
 
2639
      gtk_widget_queue_resize (GTK_WIDGET (box));
 
2640
    }
 
2641
}