gnome-shell-3.6.3.1/src/st/st-box-layout.c

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;
Dereference of null pointer
(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 }