Location | Tool | Test ID | Function | Issue |
---|---|---|---|---|
st/st-box-layout.c:780:28 | clang-analyzer | Dereference of null pointer |
1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
2 /*
3 * st-box-layout.h: box layout actor
4 *
5 * Copyright 2009 Intel Corporation.
6 * Copyright 2009 Abderrahim Kitouni
7 * Copyright 2009, 2010 Red Hat, Inc.
8 * Copyright 2010 Florian Muellner
9 *
10 * This program is free software; you can redistribute it and/or modify it
11 * under the terms and conditions of the GNU Lesser General Public License,
12 * version 2.1, as published by the Free Software Foundation.
13 *
14 * This program is distributed in the hope it will be useful, but WITHOUT ANY
15 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
17 * more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public License
20 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21 */
22
23 /* Portions copied from Clutter:
24 * Clutter.
25 *
26 * An OpenGL based 'interactive canvas' library.
27 *
28 * Authored By Matthew Allum <mallum@openedhand.com>
29 *
30 * Copyright (C) 2006 OpenedHand
31 * This library is free software; you can redistribute it and/or
32 * modify it under the terms of the GNU Lesser General Public
33 * License as published by the Free Software Foundation; either
34 * version 2 of the License, or (at your option) any later version.
35 */
36
37 /**
38 * SECTION:st-box-layout
39 * @short_description: a layout container arranging children in a single line
40 *
41 * The #StBoxLayout arranges its children along a single line, where each
42 * child can be allocated either its preferred size or larger if the expand
43 * option is set. If the fill option is set, the actor will be allocated more
44 * than its requested size. If the fill option is not set, but the expand option
45 * is enabled, then the position of the actor within the available space can
46 * be determined by the alignment child property.
47 *
48 */
49
50 #include <stdlib.h>
51
52 #include "st-box-layout.h"
53
54 #include "st-private.h"
55 #include "st-scrollable.h"
56 #include "st-box-layout-child.h"
57
58
59 static void st_box_container_iface_init (ClutterContainerIface *iface);
60 static void st_box_scrollable_interface_init (StScrollableInterface *iface);
61
62 G_DEFINE_TYPE_WITH_CODE (StBoxLayout, st_box_layout, ST_TYPE_WIDGET,
63 G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_CONTAINER,
64 st_box_container_iface_init)
65 G_IMPLEMENT_INTERFACE (ST_TYPE_SCROLLABLE,
66 st_box_scrollable_interface_init));
67
68 #define BOX_LAYOUT_PRIVATE(o) \
69 (G_TYPE_INSTANCE_GET_PRIVATE ((o), ST_TYPE_BOX_LAYOUT, StBoxLayoutPrivate))
70
71 enum {
72 PROP_0,
73
74 PROP_VERTICAL,
75 PROP_PACK_START,
76
77 PROP_HADJUST,
78 PROP_VADJUST
79 };
80
81 struct _StBoxLayoutPrivate
82 {
83 guint spacing;
84
85 guint is_vertical : 1;
86 guint is_pack_start : 1;
87
88 StAdjustment *hadjustment;
89 StAdjustment *vadjustment;
90 };
91
92 /*
93 * StScrollable Interface Implementation
94 */
95 static void
96 adjustment_value_notify_cb (StAdjustment *adjustment,
97 GParamSpec *pspec,
98 StBoxLayout *box)
99 {
100 clutter_actor_queue_redraw (CLUTTER_ACTOR (box));
101 }
102
103 static void
104 scrollable_set_adjustments (StScrollable *scrollable,
105 StAdjustment *hadjustment,
106 StAdjustment *vadjustment)
107 {
108 StBoxLayoutPrivate *priv = ST_BOX_LAYOUT (scrollable)->priv;
109
110 g_object_freeze_notify (G_OBJECT (scrollable));
111
112 if (hadjustment != priv->hadjustment)
113 {
114 if (priv->hadjustment)
115 {
116 g_signal_handlers_disconnect_by_func (priv->hadjustment,
117 adjustment_value_notify_cb,
118 scrollable);
119 g_object_unref (priv->hadjustment);
120 }
121
122 if (hadjustment)
123 {
124 g_object_ref (hadjustment);
125 g_signal_connect (hadjustment, "notify::value",
126 G_CALLBACK (adjustment_value_notify_cb),
127 scrollable);
128 }
129
130 priv->hadjustment = hadjustment;
131 g_object_notify (G_OBJECT (scrollable), "hadjustment");
132 }
133
134 if (vadjustment != priv->vadjustment)
135 {
136 if (priv->vadjustment)
137 {
138 g_signal_handlers_disconnect_by_func (priv->vadjustment,
139 adjustment_value_notify_cb,
140 scrollable);
141 g_object_unref (priv->vadjustment);
142 }
143
144 if (vadjustment)
145 {
146 g_object_ref (vadjustment);
147 g_signal_connect (vadjustment, "notify::value",
148 G_CALLBACK (adjustment_value_notify_cb),
149 scrollable);
150 }
151
152 priv->vadjustment = vadjustment;
153 g_object_notify (G_OBJECT (scrollable), "vadjustment");
154 }
155
156 g_object_thaw_notify (G_OBJECT (scrollable));
157 }
158
159 static void
160 scrollable_get_adjustments (StScrollable *scrollable,
161 StAdjustment **hadjustment,
162 StAdjustment **vadjustment)
163 {
164 StBoxLayoutPrivate *priv;
165
166 priv = (ST_BOX_LAYOUT (scrollable))->priv;
167
168 if (hadjustment)
169 *hadjustment = priv->hadjustment;
170
171 if (vadjustment)
172 *vadjustment = priv->vadjustment;
173 }
174
175
176
177 static void
178 st_box_scrollable_interface_init (StScrollableInterface *iface)
179 {
180 iface->set_adjustments = scrollable_set_adjustments;
181 iface->get_adjustments = scrollable_get_adjustments;
182 }
183
184 static void
185 st_box_container_iface_init (ClutterContainerIface *iface)
186 {
187 iface->child_meta_type = ST_TYPE_BOX_LAYOUT_CHILD;
188 }
189
190
191 static void
192 st_box_layout_get_property (GObject *object,
193 guint property_id,
194 GValue *value,
195 GParamSpec *pspec)
196 {
197 StBoxLayoutPrivate *priv = ST_BOX_LAYOUT (object)->priv;
198 StAdjustment *adjustment;
199
200 switch (property_id)
201 {
202 case PROP_VERTICAL:
203 g_value_set_boolean (value, priv->is_vertical);
204 break;
205
206 case PROP_PACK_START:
207 g_value_set_boolean (value, priv->is_pack_start);
208 break;
209
210 case PROP_HADJUST:
211 scrollable_get_adjustments (ST_SCROLLABLE (object), &adjustment, NULL);
212 g_value_set_object (value, adjustment);
213 break;
214
215 case PROP_VADJUST:
216 scrollable_get_adjustments (ST_SCROLLABLE (object), NULL, &adjustment);
217 g_value_set_object (value, adjustment);
218 break;
219
220 default:
221 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
222 }
223 }
224
225 static void
226 st_box_layout_set_property (GObject *object,
227 guint property_id,
228 const GValue *value,
229 GParamSpec *pspec)
230 {
231 StBoxLayout *box = ST_BOX_LAYOUT (object);
232
233 switch (property_id)
234 {
235 case PROP_VERTICAL:
236 st_box_layout_set_vertical (box, g_value_get_boolean (value));
237 break;
238
239 case PROP_PACK_START:
240 st_box_layout_set_pack_start (box, g_value_get_boolean (value));
241 break;
242
243 case PROP_HADJUST:
244 scrollable_set_adjustments (ST_SCROLLABLE (object),
245 g_value_get_object (value),
246 box->priv->vadjustment);
247 break;
248
249 case PROP_VADJUST:
250 scrollable_set_adjustments (ST_SCROLLABLE (object),
251 box->priv->hadjustment,
252 g_value_get_object (value));
253 break;
254
255 default:
256 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
257 }
258 }
259
260 static void
261 st_box_layout_dispose (GObject *object)
262 {
263 StBoxLayoutPrivate *priv = ST_BOX_LAYOUT (object)->priv;
264
265 if (priv->hadjustment)
266 {
267 g_object_unref (priv->hadjustment);
268 priv->hadjustment = NULL;
269 }
270
271 if (priv->vadjustment)
272 {
273 g_object_unref (priv->vadjustment);
274 priv->vadjustment = NULL;
275 }
276
277 G_OBJECT_CLASS (st_box_layout_parent_class)->dispose (object);
278 }
279
280 static void
281 get_content_preferred_width (StBoxLayout *self,
282 gfloat for_height,
283 gfloat *min_width_p,
284 gfloat *natural_width_p)
285 {
286 StBoxLayoutPrivate *priv = self->priv;
287 gint n_children = 0;
288 gint n_fixed = 0;
289 gfloat min_width, natural_width;
290 ClutterActor *child;
291
292 min_width = 0;
293 natural_width = 0;
294
295 for (child = clutter_actor_get_first_child (CLUTTER_ACTOR (self));
296 child != NULL;
297 child = clutter_actor_get_next_sibling (child))
298 {
299 gfloat child_min = 0, child_nat = 0;
300 gboolean child_fill;
301
302 if (!CLUTTER_ACTOR_IS_VISIBLE (child))
303 continue;
304
305 n_children++;
306
307 if (clutter_actor_get_fixed_position_set (child))
308 {
309 n_fixed++;
310 continue;
311 }
312
313 if (priv->is_vertical)
314 {
315 _st_actor_get_preferred_width (child, -1, FALSE,
316 &child_min, &child_nat);
317 min_width = MAX (child_min, min_width);
318 natural_width = MAX (child_nat, natural_width);
319 }
320 else
321 {
322 clutter_container_child_get (CLUTTER_CONTAINER (self), child,
323 "y-fill", &child_fill,
324 NULL);
325 _st_actor_get_preferred_width (child, for_height, child_fill,
326 &child_min, &child_nat);
327 min_width += child_min;
328 natural_width += child_nat;
329 }
330 }
331
332 if (!priv->is_vertical && (n_children - n_fixed) > 1)
333 {
334 min_width += priv->spacing * (n_children - n_fixed - 1);
335 natural_width += priv->spacing * (n_children - n_fixed - 1);
336 }
337
338 if (min_width_p)
339 *min_width_p = min_width;
340
341 if (natural_width_p)
342 *natural_width_p = natural_width;
343 }
344
345 static void
346 st_box_layout_get_preferred_width (ClutterActor *actor,
347 gfloat for_height,
348 gfloat *min_width_p,
349 gfloat *natural_width_p)
350 {
351 StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor));
352
353 st_theme_node_adjust_for_height (theme_node, &for_height);
354
355 get_content_preferred_width (ST_BOX_LAYOUT (actor), for_height,
356 min_width_p, natural_width_p);
357
358 st_theme_node_adjust_preferred_width (theme_node,
359 min_width_p, natural_width_p);
360 }
361
362 static void
363 get_content_preferred_height (StBoxLayout *self,
364 gfloat for_width,
365 gfloat *min_height_p,
366 gfloat *natural_height_p)
367 {
368 StBoxLayoutPrivate *priv = self->priv;
369 gint n_children = 0;
370 gint n_fixed = 0;
371 gfloat min_height, natural_height;
372 ClutterActor *child;
373
374 min_height = 0;
375 natural_height = 0;
376
377 for (child = clutter_actor_get_first_child (CLUTTER_ACTOR (self));
378 child != NULL;
379 child = clutter_actor_get_next_sibling (child))
380 {
381 gfloat child_min = 0, child_nat = 0;
382 gboolean child_fill = FALSE;
383
384 if (!CLUTTER_ACTOR_IS_VISIBLE (child))
385 continue;
386
387 n_children++;
388
389 if (clutter_actor_get_fixed_position_set (child))
390 {
391 n_fixed++;
392 continue;
393 }
394
395 if (priv->is_vertical)
396 {
397 clutter_container_child_get ((ClutterContainer*) self, child,
398 "x-fill", &child_fill,
399 NULL);
400 }
401 _st_actor_get_preferred_height (child,
402 (priv->is_vertical) ? for_width : -1,
403 child_fill,
404 &child_min,
405 &child_nat);
406
407 if (!priv->is_vertical)
408 {
409 min_height = MAX (child_min, min_height);
410 natural_height = MAX (child_nat, natural_height);
411 }
412 else
413 {
414 min_height += child_min;
415 natural_height += child_nat;
416 }
417 }
418
419 if (priv->is_vertical && (n_children - n_fixed) > 1)
420 {
421 min_height += priv->spacing * (n_children - n_fixed - 1);
422 natural_height += priv->spacing * (n_children - n_fixed - 1);
423 }
424
425 if (min_height_p)
426 *min_height_p = min_height;
427
428 if (natural_height_p)
429 *natural_height_p = natural_height;
430 }
431
432 static void
433 st_box_layout_get_preferred_height (ClutterActor *actor,
434 gfloat for_width,
435 gfloat *min_height_p,
436 gfloat *natural_height_p)
437 {
438 StBoxLayout *self = ST_BOX_LAYOUT (actor);
439 StBoxLayoutPrivate *priv = self->priv;
440 StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor));
441
442 st_theme_node_adjust_for_width (theme_node, &for_width);
443
444 if (priv->hadjustment)
445 {
446 /* If we're scrolled, the parent calls us with the width that
447 * we'll actually get, which can be smaller than the minimum
448 * width that we give our contents.
449 */
450 gfloat min_width;
451
452 get_content_preferred_width (self, -1, &min_width, NULL);
453 for_width = MAX (for_width, min_width);
454 }
455
456 get_content_preferred_height (self, for_width,
457 min_height_p, natural_height_p);
458
459 st_theme_node_adjust_preferred_height (theme_node,
460 min_height_p, natural_height_p);
461 }
462
463 typedef struct {
464 int child_index;
465 gfloat shrink_amount;
466 } BoxChildShrink;
467
468 /* Sort with the greatest shrink amount first */
469 static int
470 compare_by_shrink_amount (const void *a,
471 const void *b)
472 {
473 float diff = ((const BoxChildShrink *)a)->shrink_amount - ((const BoxChildShrink *)b)->shrink_amount;
474 return diff < 0 ? 1 : (diff == 0 ? 0 : -1);
475 }
476
477 /* Sort in ascending order by child index */
478 static int
479 compare_by_child_index (const void *a,
480 const void *b)
481 {
482 return ((const BoxChildShrink *)a)->child_index - ((const BoxChildShrink *)b)->child_index;
483 }
484
485 static BoxChildShrink *
486 compute_shrinks (StBoxLayout *self,
487 gfloat for_length,
488 gfloat total_shrink)
489 {
490 StBoxLayoutPrivate *priv = self->priv;
491 int n_children = clutter_actor_get_n_children (CLUTTER_ACTOR (self));
492 BoxChildShrink *shrinks = g_new0 (BoxChildShrink, n_children);
493 gfloat shrink_so_far;
494 gfloat base_shrink = 0; /* the "= 0" is just to make gcc happy */
495 int n_shrink_children;
496 ClutterActor *child;
497 int i = 0;
498
499 /* The effect that we want is that all the children get an equal chance
500 * to expand from their minimum size up to the natural size. Or to put
501 * it a different way, we want to start by shrinking only the child that
502 * can shrink most, then shrink that and the next most shrinkable child,
503 * to the point where we are shrinking everything.
504 */
505
506 /* Find the amount of possible shrink for each child */
507 int n_possible_shrink_children = 0;
508 for (child = clutter_actor_get_first_child (CLUTTER_ACTOR (self));
509 child != NULL;
510 child = clutter_actor_get_next_sibling (child))
511 {
512 gfloat child_min, child_nat;
513 gboolean child_fill;
514 gboolean fixed;
515
516 fixed = clutter_actor_get_fixed_position_set (child);
517
518 shrinks[i].child_index = i;
519 if (CLUTTER_ACTOR_IS_VISIBLE (child) && !fixed)
520 {
521 if (priv->is_vertical)
522 {
523 clutter_container_child_get ((ClutterContainer*) self, child,
524 "x-fill", &child_fill,
525 NULL);
526 _st_actor_get_preferred_height (child,
527 for_length, child_fill,
528 &child_min, &child_nat);
529 }
530 else
531 {
532 clutter_container_child_get ((ClutterContainer*) self, child,
533 "y-fill", &child_fill,
534 NULL);
535 _st_actor_get_preferred_width (child,
536 for_length, child_fill,
537 &child_min, &child_nat);
538 }
539
540 shrinks[i].shrink_amount = MAX (0., child_nat - child_min);
541 n_possible_shrink_children++;
542 }
543 else
544 {
545 shrinks[i].shrink_amount = -1.;
546 }
547
548 i++;
549 }
550
551 /* We want to process children starting from the child with the maximum available
552 * shrink, so sort in this order; !visible children end up at the end */
553 qsort (shrinks, n_children, sizeof (BoxChildShrink), compare_by_shrink_amount);
554
555 /* +--+
556 * | |
557 * | | +--
558 * | | | |
559 * | | | | +-+
560 * --+--+-+-+-+-+----------
561 * | | | | | | +-+ +-+
562 * | | | | | | | | | |
563 * --+--+-+-+-+-+-+-+------
564 *
565 * We are trying to find the correct position for the upper line the "water mark"
566 * so that total of the portion of the bars above the line is equal to the total
567 * amount we want to shrink.
568 */
569
570 /* Start by moving the line downward, top-of-bar by top-of-bar */
571 shrink_so_far = 0;
572 for (n_shrink_children = 1; n_shrink_children <= n_possible_shrink_children; n_shrink_children++)
573 {
574 if (n_shrink_children < n_possible_shrink_children)
575 base_shrink = shrinks[n_shrink_children].shrink_amount;
576 else
577 base_shrink = 0;
578 shrink_so_far += n_shrink_children * (shrinks[n_shrink_children - 1].shrink_amount - base_shrink);
579
580 if (shrink_so_far >= total_shrink || n_shrink_children == n_possible_shrink_children)
581 break;
582 }
583
584 /* OK, we found enough shrinkage, move it back upwards to the right position */
585 base_shrink += (shrink_so_far - total_shrink) / n_shrink_children;
586 if (base_shrink < 0) /* can't shrink that much, probably round-off error */
587 base_shrink = 0;
588
589 /* Assign the portion above the base shrink line to the shrink_amount */
590 for (i = 0; i < n_shrink_children; i++)
591 shrinks[i].shrink_amount -= base_shrink;
592 for (; i < n_children; i++)
593 shrinks[i].shrink_amount = 0;
594
595 /* And sort back to their original order */
596 qsort (shrinks, n_children, sizeof (BoxChildShrink), compare_by_child_index);
597
598 return shrinks;
599 }
600
601 static void
602 st_box_layout_allocate (ClutterActor *actor,
603 const ClutterActorBox *box,
604 ClutterAllocationFlags flags)
605 {
606 StBoxLayoutPrivate *priv = ST_BOX_LAYOUT (actor)->priv;
607 StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor));
608 ClutterActorBox content_box;
609 gfloat avail_width, avail_height, min_width, natural_width, min_height, natural_height;
610 gfloat position, next_position;
611 gint n_expand_children = 0, i;
612 gfloat expand_amount, shrink_amount;
613 BoxChildShrink *shrinks = NULL;
614 gboolean flip = (clutter_actor_get_text_direction (actor) == CLUTTER_TEXT_DIRECTION_RTL)
615 && (!priv->is_vertical);
616 ClutterActor *child;
617
618 clutter_actor_set_allocation (actor, box, flags);
619
620 st_theme_node_get_content_box (theme_node, box, &content_box);
621
622 avail_width = content_box.x2 - content_box.x1;
623 avail_height = content_box.y2 - content_box.y1;
624
625 get_content_preferred_width (ST_BOX_LAYOUT (actor), avail_height,
626 &min_width, &natural_width);
627 get_content_preferred_height (ST_BOX_LAYOUT (actor), MAX (avail_width, min_width),
628 &min_height, &natural_height);
629
630
631 /* update adjustments for scrolling */
632 if (priv->vadjustment)
633 {
634 gdouble prev_value;
635
636 g_object_set (G_OBJECT (priv->vadjustment),
637 "lower", 0.0,
638 "upper", MAX (min_height, avail_height),
639 "page-size", avail_height,
640 "step-increment", avail_height / 6,
641 "page-increment", avail_height - avail_height / 6,
642 NULL);
643
644 prev_value = st_adjustment_get_value (priv->vadjustment);
645 st_adjustment_set_value (priv->vadjustment, prev_value);
646 }
647
648 if (priv->hadjustment)
649 {
650 gdouble prev_value;
651
652 g_object_set (G_OBJECT (priv->hadjustment),
653 "lower", 0.0,
654 "upper", MAX (min_width, avail_width),
655 "page-size", avail_width,
656 "step-increment", avail_width / 6,
657 "page-increment", avail_width - avail_width / 6,
658 NULL);
659
660 prev_value = st_adjustment_get_value (priv->hadjustment);
661 st_adjustment_set_value (priv->hadjustment, prev_value);
662 }
663
664 if (avail_height < min_height)
665 {
666 avail_height = min_height;
667 content_box.y2 = content_box.y1 + avail_height;
668 }
669
670 if (avail_width < min_width)
671 {
672 avail_width = min_width;
673 content_box.x2 = content_box.x1 + avail_width;
674 }
675
676 if (priv->is_vertical)
677 {
678 expand_amount = MAX (0, avail_height - natural_height);
679 shrink_amount = MAX (0, natural_height - avail_height);
680 }
681 else
682 {
683 expand_amount = MAX (0, avail_width - natural_width);
684 shrink_amount = MAX (0, natural_width - avail_width);
685 }
686
687 if (expand_amount > 0)
688 {
689 /* count the number of children with expand set to TRUE */
690 n_expand_children = 0;
691 for (child = clutter_actor_get_first_child (actor);
692 child != NULL;
693 child = clutter_actor_get_next_sibling (child))
694 {
695 gboolean expand;
696
697 if (!CLUTTER_ACTOR_IS_VISIBLE (child) ||
698 clutter_actor_get_fixed_position_set (child))
699 continue;
700
701 clutter_container_child_get ((ClutterContainer *) actor,
702 child,
703 "expand", &expand,
704 NULL);
705 if (expand)
706 n_expand_children++;
707 }
708
709 if (n_expand_children == 0)
710 expand_amount = 0;
711 }
712 else if (shrink_amount > 0)
713 {
714 shrinks = compute_shrinks (ST_BOX_LAYOUT (actor),
715 priv->is_vertical ? avail_width : avail_height,
716 shrink_amount);
717 }
718
719 if (priv->is_vertical)
720 position = content_box.y1;
721 else if (flip)
722 position = content_box.x2;
723 else
724 position = content_box.x1;
725
726 if (priv->is_pack_start)
727 {
728 child = clutter_actor_get_last_child (actor);
729 i = clutter_actor_get_n_children (actor);
730 }
731 else
732 {
733 child = clutter_actor_get_first_child (actor);
734 i = 0;
735 }
736
737 while (child != NULL)
738 {
739 ClutterActorBox child_box;
740 gfloat child_min, child_nat, child_allocated;
741 gboolean xfill, yfill, expand, fixed;
742 StAlign xalign, yalign;
743 gdouble xalign_f, yalign_f;
744
745 if (!CLUTTER_ACTOR_IS_VISIBLE (child))
746 goto next_child;
747
748 fixed = clutter_actor_get_fixed_position_set (child);
749 if (fixed)
750 {
751 clutter_actor_allocate_preferred_size (child, flags);
752 goto next_child;
753 }
754
755 clutter_container_child_get ((ClutterContainer*) actor, child,
756 "x-fill", &xfill,
757 "y-fill", &yfill,
758 "x-align", &xalign,
759 "y-align", &yalign,
760 "expand", &expand,
761 NULL);
762
763 _st_get_align_factors (xalign, yalign, &xalign_f, &yalign_f);
764
765 if (priv->is_vertical)
766 {
767 _st_actor_get_preferred_height (child, avail_width, xfill,
768 &child_min, &child_nat);
769 }
770 else
771 {
772 _st_actor_get_preferred_width (child, avail_height, yfill,
773 &child_min, &child_nat);
774 }
775
776 child_allocated = child_nat;
777 if (expand_amount > 0 && expand)
778 child_allocated += expand_amount / n_expand_children;
779 else if (shrink_amount > 0)
780 child_allocated -= shrinks[i].shrink_amount;
(emitted by clang-analyzer)TODO: a detailed trace is available in the data model (not yet rendered in this report)
781
782 if (flip)
783 next_position = position - child_allocated;
784 else
785 next_position = position + child_allocated;
786
787 if (priv->is_vertical)
788 {
789 child_box.y1 = (int)(0.5 + position);
790 child_box.y2 = (int)(0.5 + next_position);
791 child_box.x1 = content_box.x1;
792 child_box.x2 = content_box.x2;
793
794 clutter_actor_allocate_align_fill (child, &child_box,
795 xalign_f, yalign_f,
796 xfill, yfill, flags);
797 }
798 else
799 {
800 if (flip)
801 {
802 child_box.x1 = (int)(0.5 + next_position);
803 child_box.x2 = (int)(0.5 + position);
804 }
805 else
806 {
807 child_box.x1 = (int)(0.5 + position);
808 child_box.x2 = (int)(0.5 + next_position);
809 }
810
811 child_box.y1 = content_box.y1;
812 child_box.y2 = content_box.y2;
813
814 clutter_actor_allocate_align_fill (child, &child_box,
815 xalign_f, yalign_f,
816 xfill, yfill, flags);
817 }
818
819 if (flip)
820 position = next_position - priv->spacing;
821 else
822 position = next_position + priv->spacing;
823
824 next_child:
825 if (priv->is_pack_start)
826 {
827 child = clutter_actor_get_previous_sibling (child);
828 i--;
829 }
830 else
831 {
832 child = clutter_actor_get_next_sibling (child);
833 i++;
834 }
835 }
836
837 if (shrinks)
838 g_free (shrinks);
839 }
840
841 static void
842 st_box_layout_apply_transform (ClutterActor *a,
843 CoglMatrix *m)
844 {
845 StBoxLayoutPrivate *priv = ST_BOX_LAYOUT (a)->priv;
846 gdouble x, y;
847
848 CLUTTER_ACTOR_CLASS (st_box_layout_parent_class)->apply_transform (a, m);
849
850 if (priv->hadjustment)
851 x = st_adjustment_get_value (priv->hadjustment);
852 else
853 x = 0;
854
855 if (priv->vadjustment)
856 y = st_adjustment_get_value (priv->vadjustment);
857 else
858 y = 0;
859
860 cogl_matrix_translate (m, (int) -x, (int) -y, 0);
861 }
862
863 /* If we are translated, then we need to translate back before chaining
864 * up or the background and borders will be drawn in the wrong place */
865 static void
866 get_border_paint_offsets (StBoxLayout *self,
867 double *x,
868 double *y)
869 {
870 StBoxLayoutPrivate *priv = self->priv;
871
872 if (priv->hadjustment)
873 *x = st_adjustment_get_value (priv->hadjustment);
874 else
875 *x = 0;
876
877 if (priv->vadjustment)
878 *y = st_adjustment_get_value (priv->vadjustment);
879 else
880 *y = 0;
881 }
882
883
884 static void
885 st_box_layout_paint (ClutterActor *actor)
886 {
887 StBoxLayout *self = ST_BOX_LAYOUT (actor);
888 StBoxLayoutPrivate *priv = self->priv;
889 StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor));
890 gdouble x, y;
891 ClutterActorBox allocation_box;
892 ClutterActorBox content_box;
893 ClutterActor *child;
894
895 get_border_paint_offsets (self, &x, &y);
896 if (x != 0 || y != 0)
897 {
898 cogl_push_matrix ();
899 cogl_translate ((int)x, (int)y, 0);
900 }
901
902 st_widget_paint_background (ST_WIDGET (actor));
903
904 if (x != 0 || y != 0)
905 {
906 cogl_pop_matrix ();
907 }
908
909 if (clutter_actor_get_n_children (actor) == 0)
910 return;
911
912 clutter_actor_get_allocation_box (actor, &allocation_box);
913 st_theme_node_get_content_box (theme_node, &allocation_box, &content_box);
914
915 content_box.x1 += x;
916 content_box.y1 += y;
917 content_box.x2 += x;
918 content_box.y2 += y;
919
920 /* The content area forms the viewport into the scrolled contents, while
921 * the borders and background stay in place; after drawing the borders and
922 * background, we clip to the content area */
923 if (priv->hadjustment || priv->vadjustment)
924 cogl_clip_push_rectangle ((int)content_box.x1,
925 (int)content_box.y1,
926 (int)content_box.x2,
927 (int)content_box.y2);
928
929 for (child = clutter_actor_get_first_child (actor);
930 child != NULL;
931 child = clutter_actor_get_next_sibling (child))
932 clutter_actor_paint (child);
933
934 if (priv->hadjustment || priv->vadjustment)
935 cogl_clip_pop ();
936 }
937
938 static void
939 st_box_layout_pick (ClutterActor *actor,
940 const ClutterColor *color)
941 {
942 StBoxLayout *self = ST_BOX_LAYOUT (actor);
943 StBoxLayoutPrivate *priv = self->priv;
944 StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor));
945 gdouble x, y;
946 ClutterActorBox allocation_box;
947 ClutterActorBox content_box;
948 ClutterActor *child;
949
950 get_border_paint_offsets (self, &x, &y);
951 if (x != 0 || y != 0)
952 {
953 cogl_push_matrix ();
954 cogl_translate ((int)x, (int)y, 0);
955 }
956
957 CLUTTER_ACTOR_CLASS (st_box_layout_parent_class)->pick (actor, color);
958
959 if (x != 0 || y != 0)
960 {
961 cogl_pop_matrix ();
962 }
963
964 if (clutter_actor_get_n_children (actor) == 0)
965 return;
966
967 clutter_actor_get_allocation_box (actor, &allocation_box);
968 st_theme_node_get_content_box (theme_node, &allocation_box, &content_box);
969
970 content_box.x1 += x;
971 content_box.y1 += y;
972 content_box.x2 += x;
973 content_box.y2 += y;
974
975 if (priv->hadjustment || priv->vadjustment)
976 cogl_clip_push_rectangle ((int)content_box.x1,
977 (int)content_box.y1,
978 (int)content_box.x2,
979 (int)content_box.y2);
980
981 for (child = clutter_actor_get_first_child (actor);
982 child != NULL;
983 child = clutter_actor_get_next_sibling (child))
984 clutter_actor_paint (child);
985
986 if (priv->hadjustment || priv->vadjustment)
987 cogl_clip_pop ();
988 }
989
990 static gboolean
991 st_box_layout_get_paint_volume (ClutterActor *actor,
992 ClutterPaintVolume *volume)
993 {
994 StBoxLayout *self = ST_BOX_LAYOUT (actor);
995 gdouble x, y;
996 StBoxLayoutPrivate *priv = self->priv;
997 StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor));
998 ClutterActorBox allocation_box;
999 ClutterActorBox content_box;
1000 ClutterVertex origin;
1001
1002 /* When have an adjustment we are clipped to the content box, so base
1003 * our paint volume on that. */
1004 if (priv->hadjustment || priv->vadjustment)
1005 {
1006 clutter_actor_get_allocation_box (actor, &allocation_box);
1007 st_theme_node_get_content_box (theme_node, &allocation_box, &content_box);
1008 origin.x = content_box.x1 - allocation_box.x1;
1009 origin.y = content_box.y1 - allocation_box.y2;
1010 origin.z = 0.f;
1011 clutter_paint_volume_set_width (volume, content_box.x2 - content_box.x1);
1012 clutter_paint_volume_set_height (volume, content_box.y2 - content_box.y1);
1013 }
1014 else if (!CLUTTER_ACTOR_CLASS (st_box_layout_parent_class)->get_paint_volume (actor, volume))
1015 return FALSE;
1016
1017 /* When scrolled, st_box_layout_apply_transform() includes the scroll offset
1018 * and affects paint volumes. This is right for our children, but our paint volume
1019 * is determined by our allocation and borders and doesn't scroll, so we need
1020 * to reverse-compensate here, the same as we do when painting.
1021 */
1022 get_border_paint_offsets (self, &x, &y);
1023 if (x != 0 || y != 0)
1024 {
1025 clutter_paint_volume_get_origin (volume, &origin);
1026 origin.x += x;
1027 origin.y += y;
1028 clutter_paint_volume_set_origin (volume, &origin);
1029 }
1030
1031 return TRUE;
1032 }
1033
1034 static void
1035 st_box_layout_style_changed (StWidget *self)
1036 {
1037 StBoxLayoutPrivate *priv = ST_BOX_LAYOUT (self)->priv;
1038 StThemeNode *theme_node = st_widget_get_theme_node (self);
1039 int old_spacing = priv->spacing;
1040 double spacing;
1041
1042 spacing = st_theme_node_get_length (theme_node, "spacing");
1043 priv->spacing = (int)(spacing + 0.5);
1044 if (priv->spacing != old_spacing)
1045 clutter_actor_queue_relayout (CLUTTER_ACTOR (self));
1046
1047 ST_WIDGET_CLASS (st_box_layout_parent_class)->style_changed (self);
1048 }
1049
1050 static void
1051 st_box_layout_class_init (StBoxLayoutClass *klass)
1052 {
1053 GObjectClass *object_class = G_OBJECT_CLASS (klass);
1054 ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
1055 StWidgetClass *widget_class = ST_WIDGET_CLASS (klass);
1056 GParamSpec *pspec;
1057
1058 g_type_class_add_private (klass, sizeof (StBoxLayoutPrivate));
1059
1060 object_class->get_property = st_box_layout_get_property;
1061 object_class->set_property = st_box_layout_set_property;
1062 object_class->dispose = st_box_layout_dispose;
1063
1064 actor_class->allocate = st_box_layout_allocate;
1065 actor_class->get_preferred_width = st_box_layout_get_preferred_width;
1066 actor_class->get_preferred_height = st_box_layout_get_preferred_height;
1067 actor_class->apply_transform = st_box_layout_apply_transform;
1068
1069 actor_class->paint = st_box_layout_paint;
1070 actor_class->get_paint_volume = st_box_layout_get_paint_volume;
1071 actor_class->pick = st_box_layout_pick;
1072
1073 widget_class->style_changed = st_box_layout_style_changed;
1074
1075 pspec = g_param_spec_boolean ("vertical",
1076 "Vertical",
1077 "Whether the layout should be vertical, rather"
1078 "than horizontal",
1079 FALSE,
1080 ST_PARAM_READWRITE);
1081 g_object_class_install_property (object_class, PROP_VERTICAL, pspec);
1082
1083 pspec = g_param_spec_boolean ("pack-start",
1084 "Pack Start",
1085 "Whether to pack items at the start of the box",
1086 FALSE,
1087 ST_PARAM_READWRITE);
1088 g_object_class_install_property (object_class, PROP_PACK_START, pspec);
1089
1090 /* StScrollable properties */
1091 g_object_class_override_property (object_class,
1092 PROP_HADJUST,
1093 "hadjustment");
1094
1095 g_object_class_override_property (object_class,
1096 PROP_VADJUST,
1097 "vadjustment");
1098
1099 }
1100
1101 static void
1102 st_box_layout_init (StBoxLayout *self)
1103 {
1104 self->priv = BOX_LAYOUT_PRIVATE (self);
1105 }
1106
1107 /**
1108 * st_box_layout_new:
1109 *
1110 * Create a new #StBoxLayout.
1111 *
1112 * Returns: a newly allocated #StBoxLayout
1113 */
1114 StWidget *
1115 st_box_layout_new (void)
1116 {
1117 return g_object_new (ST_TYPE_BOX_LAYOUT, NULL);
1118 }
1119
1120 /**
1121 * st_box_layout_set_vertical:
1122 * @box: A #StBoxLayout
1123 * @vertical: %TRUE if the layout should be vertical
1124 *
1125 * Set the value of the #StBoxLayout::vertical property
1126 *
1127 */
1128 void
1129 st_box_layout_set_vertical (StBoxLayout *box,
1130 gboolean vertical)
1131 {
1132 g_return_if_fail (ST_IS_BOX_LAYOUT (box));
1133
1134 if (box->priv->is_vertical != vertical)
1135 {
1136 box->priv->is_vertical = vertical;
1137 clutter_actor_queue_relayout ((ClutterActor*) box);
1138
1139 g_object_notify (G_OBJECT (box), "vertical");
1140 }
1141 }
1142
1143 /**
1144 * st_box_layout_get_vertical:
1145 * @box: A #StBoxLayout
1146 *
1147 * Get the value of the #StBoxLayout::vertical property.
1148 *
1149 * Returns: %TRUE if the layout is vertical
1150 */
1151 gboolean
1152 st_box_layout_get_vertical (StBoxLayout *box)
1153 {
1154 g_return_val_if_fail (ST_IS_BOX_LAYOUT (box), FALSE);
1155
1156 return box->priv->is_vertical;
1157 }
1158
1159 /**
1160 * st_box_layout_set_pack_start:
1161 * @box: A #StBoxLayout
1162 * @pack_start: %TRUE if the layout should use pack-start
1163 *
1164 * Set the value of the #StBoxLayout::pack-start property.
1165 *
1166 */
1167 void
1168 st_box_layout_set_pack_start (StBoxLayout *box,
1169 gboolean pack_start)
1170 {
1171 g_return_if_fail (ST_IS_BOX_LAYOUT (box));
1172
1173 if (box->priv->is_pack_start != pack_start)
1174 {
1175 box->priv->is_pack_start = pack_start;
1176 clutter_actor_queue_relayout ((ClutterActor*) box);
1177
1178 g_object_notify (G_OBJECT (box), "pack-start");
1179 }
1180 }
1181
1182 /**
1183 * st_box_layout_get_pack_start:
1184 * @box: A #StBoxLayout
1185 *
1186 * Get the value of the #StBoxLayout::pack-start property.
1187 *
1188 * Returns: %TRUE if pack-start is enabled
1189 */
1190 gboolean
1191 st_box_layout_get_pack_start (StBoxLayout *box)
1192 {
1193 g_return_val_if_fail (ST_IS_BOX_LAYOUT (box), FALSE);
1194
1195 return box->priv->is_pack_start;
1196 }