Location | Tool | Test ID | Function | Issue |
---|---|---|---|---|
eggwrapbox.c:736:31 | clang-analyzer | Division by zero | ||
eggwrapbox.c:736:31 | clang-analyzer | Division by zero | ||
eggwrapbox.c:1930:15 | clang-analyzer | Value stored to 'extra_pixels' is never read | ||
eggwrapbox.c:1930:15 | clang-analyzer | Value stored to 'extra_pixels' is never read | ||
eggwrapbox.c:1934:13 | clang-analyzer | Value stored to 'extra_pixels' is never read | ||
eggwrapbox.c:1934:13 | clang-analyzer | Value stored to 'extra_pixels' is never read | ||
eggwrapbox.c:2111:15 | clang-analyzer | Value stored to 'extra_pixels' is never read | ||
eggwrapbox.c:2111:15 | clang-analyzer | Value stored to 'extra_pixels' is never read | ||
eggwrapbox.c:2115:13 | clang-analyzer | Value stored to 'extra_pixels' is never read | ||
eggwrapbox.c:2115:13 | clang-analyzer | Value stored to 'extra_pixels' is never read |
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;
(emitted by clang-analyzer)TODO: a detailed trace is available in the data model (not yet rendered in this report)
(emitted by clang-analyzer)TODO: a detailed trace is available in the data model (not yet rendered in this report)
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;
(emitted by clang-analyzer)TODO: a detailed trace is available in the data model (not yet rendered in this report)
(emitted by clang-analyzer)TODO: a detailed trace is available in the data model (not yet rendered in this report)
1931 }
1932 else
1933 /* Collect the extra pixels for expand children */
1934 extra_pixels = (avail_size - (line_length - 1) * priv->horizontal_spacing) % line_length;
(emitted by clang-analyzer)TODO: a detailed trace is available in the data model (not yet rendered in this report)
(emitted by clang-analyzer)TODO: a detailed trace is available in the data model (not yet rendered in this report)
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;
(emitted by clang-analyzer)TODO: a detailed trace is available in the data model (not yet rendered in this report)
(emitted by clang-analyzer)TODO: a detailed trace is available in the data model (not yet rendered in this report)
2112 }
2113 else
2114 /* Collect the extra pixels for expand children */
2115 extra_pixels = (avail_size - (line_length - 1) * priv->vertical_spacing) % line_length;
(emitted by clang-analyzer)TODO: a detailed trace is available in the data model (not yet rendered in this report)
(emitted by clang-analyzer)TODO: a detailed trace is available in the data model (not yet rendered in this report)
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 }