gnome-shell-3.6.3.1/src/st/st-scroll-view.c

Location Tool Test ID Function Issue
st/st-scroll-view.c:875:7 clang-analyzer Access to field 'g_class' results in a dereference of a null pointer (loaded from variable 'actor')
   1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
   2 /*
   3  * st-scroll-view.h: Container with scroll-bars
   4  *
   5  * Copyright 2008 OpenedHand
   6  * Copyright 2009 Intel Corporation.
   7  * Copyright 2009, 2010 Red Hat, Inc.
   8  * Copyright 2010 Maxim Ermilov
   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 /**
  24  * SECTION:st-scroll-view
  25  * @short_description: a container for scrollable children
  26  *
  27  * #StScrollView is a single child container for actors that implement
  28  * #StScrollable. It provides scrollbars around the edge of the child to
  29  * allow the user to move around the scrollable area.
  30  */
  31 
  32 /* TODO: The code here currently only deals with height-for-width
  33  * allocation; width-for-height allocation would need a second set of
  34  * code paths through get_preferred_height()/get_preferred_width()/allocate()
  35  * that reverse the roles of the horizontal and vertical scrollbars.
  36  *
  37  * TODO: The multiple layout passes with and without scrollbars when
  38  * using the automatic policy causes considerable inefficiency because
  39  * it breaks request caching; we should saved the last size passed
  40  * into allocate() and if it's the same as previous size not repeat
  41  * the determination of scrollbar visibility. This requires overriding
  42  * queue_relayout() so we know when to discard the saved value.
  43  *
  44  * The size negotiation between the #StScrollView and the child is
  45  * described in the documentation for #StScrollable; the significant
  46  * part to note there is that reported minimum sizes for a scrolled
  47  * child are the minimum sizes when no scrollbar is needed. This allows
  48  * us to determine what scrollbars are visible without a need to look
  49  * inside the #StAdjustment.
  50  *
  51  * The second simplification that we make that allows us to implement
  52  * a straighforward height-for-width negotiation without multiple
  53  * allocate passes is that when the vertical scrollbar policy is
  54  * AUTO, we always reserve space for the vertical scrollbar in the
  55  * reported minimum and natural size.
  56  *
  57  * See https://bugzilla.gnome.org/show_bug.cgi?id=611740 for a more
  58  * detailed description of the considerations involved.
  59  */
  60 
  61 #include "st-scroll-view.h"
  62 #include "st-scroll-bar.h"
  63 #include "st-scrollable.h"
  64 #include "st-scroll-view-fade.h"
  65 #include <clutter/clutter.h>
  66 #include <math.h>
  67 
  68 static void clutter_container_iface_init (ClutterContainerIface *iface);
  69 
  70 static ClutterContainerIface *st_scroll_view_parent_iface = NULL;
  71 
  72 G_DEFINE_TYPE_WITH_CODE (StScrollView, st_scroll_view, ST_TYPE_BIN,
  73                          G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_CONTAINER,
  74                                                 clutter_container_iface_init))
  75 
  76 #define SCROLL_VIEW_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), \
  77                                                              ST_TYPE_SCROLL_VIEW, \
  78                                                              StScrollViewPrivate))
  79 
  80 struct _StScrollViewPrivate
  81 {
  82   /* a pointer to the child; this is actually stored
  83    * inside StBin:child, but we keep it to avoid
  84    * calling st_bin_get_child() every time we need it
  85    */
  86   ClutterActor *child;
  87 
  88   StAdjustment *hadjustment;
  89   ClutterActor *hscroll;
  90   StAdjustment *vadjustment;
  91   ClutterActor *vscroll;
  92 
  93   GtkPolicyType hscrollbar_policy;
  94   GtkPolicyType vscrollbar_policy;
  95 
  96   gfloat        row_size;
  97   gfloat        column_size;
  98 
  99   StScrollViewFade *fade_effect;
 100 
 101   gboolean      row_size_set : 1;
 102   gboolean      column_size_set : 1;
 103   guint         mouse_scroll : 1;
 104   guint         hscrollbar_visible : 1;
 105   guint         vscrollbar_visible : 1;
 106 };
 107 
 108 enum {
 109   PROP_0,
 110 
 111   PROP_HSCROLL,
 112   PROP_VSCROLL,
 113   PROP_HSCROLLBAR_POLICY,
 114   PROP_VSCROLLBAR_POLICY,
 115   PROP_HSCROLLBAR_VISIBLE,
 116   PROP_VSCROLLBAR_VISIBLE,
 117   PROP_MOUSE_SCROLL,
 118 };
 119 
 120 static void
 121 st_scroll_view_get_property (GObject    *object,
 122                              guint       property_id,
 123                              GValue     *value,
 124                              GParamSpec *pspec)
 125 {
 126   StScrollViewPrivate *priv = ((StScrollView *) object)->priv;
 127 
 128   switch (property_id)
 129     {
 130     case PROP_HSCROLL:
 131       g_value_set_object (value, priv->hscroll);
 132       break;
 133     case PROP_VSCROLL:
 134       g_value_set_object (value, priv->vscroll);
 135       break;
 136     case PROP_HSCROLLBAR_POLICY:
 137       g_value_set_enum (value, priv->hscrollbar_policy);
 138       break;
 139     case PROP_VSCROLLBAR_POLICY:
 140       g_value_set_enum (value, priv->vscrollbar_policy);
 141       break;
 142     case PROP_HSCROLLBAR_VISIBLE:
 143       g_value_set_boolean (value, priv->hscrollbar_visible);
 144       break;
 145     case PROP_VSCROLLBAR_VISIBLE:
 146       g_value_set_boolean (value, priv->vscrollbar_visible);
 147       break;
 148     case PROP_MOUSE_SCROLL:
 149       g_value_set_boolean (value, priv->mouse_scroll);
 150       break;
 151     default:
 152       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
 153     }
 154 }
 155 
 156 /**
 157  * st_scroll_view_update_fade_effect:
 158  * @self: a #StScrollView
 159  * @vfade_offset: The length of the veritcal fade effect, in pixels.
 160  * @hfade_offset: The length of the horizontal fade effect, in pixels.
 161  *
 162  * Sets the height of the fade area area in pixels. A value of 0
 163  * disables the effect.
 164  */
 165 static void
 166 st_scroll_view_update_fade_effect (StScrollView *self,
 167                                    float vfade_offset,
 168                                    float hfade_offset)
 169 {
 170   StScrollViewPrivate *priv = ST_SCROLL_VIEW (self)->priv;
 171 
 172   /* A fade amount of more than 0 enables the effect. */
 173   if (vfade_offset > 0. || hfade_offset > 0.)
 174     {
 175       if (priv->fade_effect == NULL) {
 176         priv->fade_effect = g_object_new (ST_TYPE_SCROLL_VIEW_FADE, NULL);
 177 
 178         clutter_actor_add_effect_with_name (CLUTTER_ACTOR (self), "fade",
 179                                             CLUTTER_EFFECT (priv->fade_effect));
 180       }
 181 
 182       g_object_set (priv->fade_effect,
 183                     "vfade-offset", vfade_offset,
 184                     NULL);
 185       g_object_set (priv->fade_effect,
 186                     "hfade-offset", hfade_offset,
 187                     NULL);
 188     }
 189    else
 190     {
 191       if (priv->fade_effect != NULL) {
 192         clutter_actor_remove_effect (CLUTTER_ACTOR (self), CLUTTER_EFFECT (priv->fade_effect));
 193         priv->fade_effect = NULL;
 194       }
 195     }
 196 
 197   clutter_actor_queue_redraw (CLUTTER_ACTOR (self));
 198 }
 199 
 200 static void
 201 st_scroll_view_set_property (GObject      *object,
 202                              guint         property_id,
 203                              const GValue *value,
 204                              GParamSpec   *pspec)
 205 {
 206   StScrollView *self = ST_SCROLL_VIEW (object);
 207   StScrollViewPrivate *priv = self->priv;
 208 
 209   switch (property_id)
 210     {
 211     case PROP_MOUSE_SCROLL:
 212       st_scroll_view_set_mouse_scrolling (self,
 213                                           g_value_get_boolean (value));
 214       break;
 215     case PROP_HSCROLLBAR_POLICY:
 216       st_scroll_view_set_policy (self,
 217                                  g_value_get_enum (value),
 218                                  priv->vscrollbar_policy);
 219       break;
 220     case PROP_VSCROLLBAR_POLICY:
 221       st_scroll_view_set_policy (self,
 222                                  priv->hscrollbar_policy,
 223                                  g_value_get_enum (value));
 224       break;
 225     default:
 226       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
 227     }
 228 }
 229 
 230 static void
 231 st_scroll_view_dispose (GObject *object)
 232 {
 233   StScrollViewPrivate *priv = ST_SCROLL_VIEW (object)->priv;
 234 
 235   if (priv->fade_effect)
 236     {
 237       clutter_actor_remove_effect (CLUTTER_ACTOR (object), CLUTTER_EFFECT (priv->fade_effect));
 238       priv->fade_effect = NULL;
 239     }
 240 
 241   if (priv->vscroll)
 242     clutter_actor_destroy (priv->vscroll);
 243 
 244   if (priv->hscroll)
 245     clutter_actor_destroy (priv->hscroll);
 246 
 247   /* For most reliable freeing of memory, an object with signals
 248    * like StAdjustment should be explicitly disposed. Since we own
 249    * the adjustments, we take care of that. This also disconnects
 250    * the signal handlers that we established on creation.
 251    */
 252   if (priv->hadjustment)
 253     {
 254       g_object_run_dispose (G_OBJECT (priv->hadjustment));
 255       g_object_unref (priv->hadjustment);
 256       priv->hadjustment = NULL;
 257     }
 258 
 259   if (priv->vadjustment)
 260     {
 261       g_object_run_dispose (G_OBJECT (priv->vadjustment));
 262       g_object_unref (priv->vadjustment);
 263       priv->vadjustment = NULL;
 264     }
 265 
 266   G_OBJECT_CLASS (st_scroll_view_parent_class)->dispose (object);
 267 }
 268 
 269 static void
 270 st_scroll_view_paint (ClutterActor *actor)
 271 {
 272   StScrollViewPrivate *priv = ST_SCROLL_VIEW (actor)->priv;
 273 
 274   st_widget_paint_background (ST_WIDGET (actor));
 275 
 276   if (priv->child)
 277     clutter_actor_paint (priv->child);
 278   if (priv->hscrollbar_visible)
 279     clutter_actor_paint (priv->hscroll);
 280   if (priv->vscrollbar_visible)
 281     clutter_actor_paint (priv->vscroll);
 282 }
 283 
 284 static void
 285 st_scroll_view_pick (ClutterActor       *actor,
 286                      const ClutterColor *color)
 287 {
 288   StScrollViewPrivate *priv = ST_SCROLL_VIEW (actor)->priv;
 289 
 290   /* Chain up so we get a bounding box pained (if we are reactive) */
 291   CLUTTER_ACTOR_CLASS (st_scroll_view_parent_class)->pick (actor, color);
 292 
 293   if (priv->child)
 294     clutter_actor_paint (priv->child);
 295   if (priv->hscrollbar_visible)
 296     clutter_actor_paint (priv->hscroll);
 297   if (priv->vscrollbar_visible)
 298     clutter_actor_paint (priv->vscroll);
 299 }
 300 
 301 static double
 302 get_scrollbar_width (StScrollView *scroll,
 303                      gfloat        for_height)
 304 {
 305   StScrollViewPrivate *priv = scroll->priv;
 306 
 307   if (CLUTTER_ACTOR_IS_VISIBLE (priv->vscroll))
 308     {
 309       gfloat min_size;
 310 
 311       clutter_actor_get_preferred_width (CLUTTER_ACTOR (priv->vscroll), for_height,
 312                                          &min_size, NULL);
 313       return min_size;
 314     }
 315   else
 316     return 0;
 317 }
 318 
 319 static double
 320 get_scrollbar_height (StScrollView *scroll,
 321                       gfloat        for_width)
 322 {
 323   StScrollViewPrivate *priv = scroll->priv;
 324 
 325   if (CLUTTER_ACTOR_IS_VISIBLE (priv->hscroll))
 326     {
 327       gfloat min_size;
 328 
 329       clutter_actor_get_preferred_height (CLUTTER_ACTOR (priv->hscroll), for_width,
 330                                           &min_size, NULL);
 331 
 332       return min_size;
 333     }
 334   else
 335     return 0;
 336 }
 337 
 338 static void
 339 st_scroll_view_get_preferred_width (ClutterActor *actor,
 340                                     gfloat        for_height,
 341                                     gfloat       *min_width_p,
 342                                     gfloat       *natural_width_p)
 343 {
 344   StScrollViewPrivate *priv = ST_SCROLL_VIEW (actor)->priv;
 345   StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor));
 346   gboolean account_for_vscrollbar = FALSE;
 347   gfloat min_width = 0, natural_width;
 348   gfloat child_min_width, child_natural_width;
 349 
 350   if (!priv->child)
 351     return;
 352 
 353   st_theme_node_adjust_for_height (theme_node, &for_height);
 354 
 355   clutter_actor_get_preferred_width (priv->child, -1,
 356                                      &child_min_width, &child_natural_width);
 357 
 358   natural_width = child_natural_width;
 359 
 360   switch (priv->hscrollbar_policy)
 361     {
 362     case GTK_POLICY_NEVER:
 363       min_width = child_min_width;
 364       break;
 365     case GTK_POLICY_ALWAYS:
 366     case GTK_POLICY_AUTOMATIC:
 367       /* Should theoretically use the min width of the hscrollbar,
 368        * but that's not cleanly defined at the moment */
 369       min_width = 0;
 370       break;
 371     }
 372 
 373   switch (priv->vscrollbar_policy)
 374     {
 375     case GTK_POLICY_NEVER:
 376       account_for_vscrollbar = FALSE;
 377       break;
 378     case GTK_POLICY_ALWAYS:
 379       account_for_vscrollbar = TRUE;
 380       break;
 381     case GTK_POLICY_AUTOMATIC:
 382       /* For automatic scrollbars, we always request space for the vertical
 383        * scrollbar; we won't know whether we actually need one until our
 384        * height is assigned in allocate().
 385        */
 386       account_for_vscrollbar = TRUE;
 387       break;
 388     }
 389 
 390   if (account_for_vscrollbar)
 391     {
 392       float sb_width = get_scrollbar_width (ST_SCROLL_VIEW (actor), for_height);
 393 
 394       min_width += sb_width;
 395       natural_width += sb_width;
 396     }
 397 
 398   if (min_width_p)
 399     *min_width_p = min_width;
 400 
 401   if (natural_width_p)
 402     *natural_width_p = natural_width;
 403 
 404   st_theme_node_adjust_preferred_width (theme_node, min_width_p, natural_width_p);
 405 }
 406 
 407 static void
 408 st_scroll_view_get_preferred_height (ClutterActor *actor,
 409                                      gfloat        for_width,
 410                                      gfloat       *min_height_p,
 411                                      gfloat       *natural_height_p)
 412 {
 413   StScrollViewPrivate *priv = ST_SCROLL_VIEW (actor)->priv;
 414   StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor));
 415   gboolean account_for_hscrollbar = FALSE;
 416   gfloat min_height = 0, natural_height;
 417   gfloat child_min_height, child_natural_height;
 418   gfloat child_min_width;
 419   gfloat sb_width;
 420 
 421   if (!priv->child)
 422     return;
 423 
 424   st_theme_node_adjust_for_width (theme_node, &for_width);
 425 
 426   clutter_actor_get_preferred_width (priv->child, -1,
 427                                      &child_min_width, NULL);
 428 
 429   if (min_height_p)
 430     *min_height_p = 0;
 431 
 432   sb_width = get_scrollbar_width (ST_SCROLL_VIEW (actor), -1);
 433 
 434   switch (priv->vscrollbar_policy)
 435     {
 436     case GTK_POLICY_NEVER:
 437       break;
 438     case GTK_POLICY_ALWAYS:
 439     case GTK_POLICY_AUTOMATIC:
 440       /* We've requested space for the scrollbar, subtract it back out */
 441       for_width -= sb_width;
 442       break;
 443     }
 444 
 445   switch (priv->hscrollbar_policy)
 446     {
 447     case GTK_POLICY_NEVER:
 448       account_for_hscrollbar = FALSE;
 449       break;
 450     case GTK_POLICY_ALWAYS:
 451       account_for_hscrollbar = TRUE;
 452       break;
 453     case GTK_POLICY_AUTOMATIC:
 454       account_for_hscrollbar = for_width < child_min_width;
 455       break;
 456     }
 457 
 458   clutter_actor_get_preferred_height (priv->child, for_width,
 459                                       &child_min_height, &child_natural_height);
 460 
 461   natural_height = child_natural_height;
 462 
 463   switch (priv->vscrollbar_policy)
 464     {
 465     case GTK_POLICY_NEVER:
 466       min_height = child_min_height;
 467       break;
 468     case GTK_POLICY_ALWAYS:
 469     case GTK_POLICY_AUTOMATIC:
 470       /* Should theoretically use the min height of the vscrollbar,
 471        * but that's not cleanly defined at the moment */
 472       min_height = 0;
 473       break;
 474     }
 475 
 476   if (account_for_hscrollbar)
 477     {
 478       float sb_height = get_scrollbar_height (ST_SCROLL_VIEW (actor), for_width);
 479 
 480       min_height += sb_height;
 481       natural_height += sb_height;
 482     }
 483 
 484   if (min_height_p)
 485     *min_height_p = min_height;
 486 
 487   if (natural_height_p)
 488     *natural_height_p = natural_height;
 489 
 490   st_theme_node_adjust_preferred_height (theme_node, min_height_p, natural_height_p);
 491 }
 492 
 493 static void
 494 st_scroll_view_allocate (ClutterActor          *actor,
 495                          const ClutterActorBox *box,
 496                          ClutterAllocationFlags flags)
 497 {
 498   ClutterActorBox content_box, child_box;
 499   gfloat avail_width, avail_height, sb_width, sb_height;
 500   gboolean hscrollbar_visible, vscrollbar_visible;
 501 
 502   StScrollViewPrivate *priv = ST_SCROLL_VIEW (actor)->priv;
 503   StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor));
 504 
 505   clutter_actor_set_allocation (actor, box, flags);
 506 
 507   st_theme_node_get_content_box (theme_node, box, &content_box);
 508 
 509   avail_width = content_box.x2 - content_box.x1;
 510   avail_height = content_box.y2 - content_box.y1;
 511 
 512   if (clutter_actor_get_request_mode (actor) == CLUTTER_REQUEST_HEIGHT_FOR_WIDTH)
 513     {
 514       sb_width = get_scrollbar_width (ST_SCROLL_VIEW (actor), -1);
 515       sb_height = get_scrollbar_height (ST_SCROLL_VIEW (actor), sb_width);
 516     }
 517   else
 518     {
 519       sb_height = get_scrollbar_height (ST_SCROLL_VIEW (actor), -1);
 520       sb_width = get_scrollbar_width (ST_SCROLL_VIEW (actor), sb_height);
 521     }
 522 
 523   /* Determine what scrollbars are visible. The basic idea of the
 524    * handling of an automatic scrollbars is that we start off with the
 525    * assumption that we don't need any scrollbars, see if that works,
 526    * and if not add horizontal and vertical scrollbars until we are no
 527    * longer overflowing.
 528    */
 529   if (priv->child)
 530     {
 531       gfloat child_min_width;
 532       gfloat child_min_height;
 533 
 534       clutter_actor_get_preferred_width (priv->child, -1,
 535                                          &child_min_width, NULL);
 536 
 537       if (priv->vscrollbar_policy == GTK_POLICY_AUTOMATIC)
 538         {
 539           if (priv->hscrollbar_policy == GTK_POLICY_AUTOMATIC)
 540             {
 541               /* Pass one, try without a vertical scrollbar */
 542               clutter_actor_get_preferred_height (priv->child, avail_width, &child_min_height, NULL);
 543               vscrollbar_visible = child_min_height > avail_height;
 544               hscrollbar_visible = child_min_width > avail_width - (vscrollbar_visible ? sb_width : 0);
 545               vscrollbar_visible = child_min_height > avail_height - (hscrollbar_visible ? sb_height : 0);
 546 
 547               /* Pass two - if we needed a vertical scrollbar, get a new preferred height */
 548               if (vscrollbar_visible)
 549                 {
 550                   clutter_actor_get_preferred_height (priv->child, MAX (avail_width - sb_width, 0),
 551                                                       &child_min_height, NULL);
 552                   hscrollbar_visible = child_min_width > avail_width - sb_width;
 553                 }
 554             }
 555           else
 556             {
 557               hscrollbar_visible = priv->hscrollbar_policy != GTK_POLICY_NEVER;
 558 
 559               /* try without a vertical scrollbar */
 560               clutter_actor_get_preferred_height (priv->child, avail_width, &child_min_height, NULL);
 561               vscrollbar_visible = child_min_height > avail_height - (hscrollbar_visible ? sb_height : 0);
 562             }
 563         }
 564       else
 565         {
 566           vscrollbar_visible = priv->vscrollbar_policy != GTK_POLICY_NEVER;
 567 
 568           if (priv->hscrollbar_policy == GTK_POLICY_AUTOMATIC)
 569             hscrollbar_visible = child_min_width > avail_height - (vscrollbar_visible ? 0 : sb_width);
 570           else
 571             hscrollbar_visible = priv->hscrollbar_policy != GTK_POLICY_NEVER;
 572         }
 573     }
 574   else
 575     {
 576       hscrollbar_visible = priv->hscrollbar_policy != GTK_POLICY_NEVER;
 577       vscrollbar_visible = priv->vscrollbar_policy != GTK_POLICY_NEVER;
 578     }
 579 
 580   /* Whether or not we show the scrollbars, if the scrollbars are visible
 581    * actors, we need to give them some allocation, so we unconditionally
 582    * give them the "right" allocation; that might overlap the child when
 583    * the scrollbars are not visible, but it doesn't matter because we
 584    * don't include them in pick or paint.
 585    */
 586 
 587   /* Vertical scrollbar */
 588   if (CLUTTER_ACTOR_IS_VISIBLE (priv->vscroll))
 589     {
 590       if (clutter_actor_get_text_direction (actor) == CLUTTER_TEXT_DIRECTION_RTL)
 591         {
 592           child_box.x1 = content_box.x1;
 593           child_box.x2 = content_box.x1 + sb_width;
 594         }
 595       else
 596         {
 597           child_box.x1 = content_box.x2 - sb_width;
 598           child_box.x2 = content_box.x2;
 599         }
 600       child_box.y1 = content_box.y1;
 601       child_box.y2 = content_box.y2 - (hscrollbar_visible ? sb_height : 0);
 602 
 603       clutter_actor_allocate (priv->vscroll, &child_box, flags);
 604     }
 605 
 606   /* Horizontal scrollbar */
 607   if (CLUTTER_ACTOR_IS_VISIBLE (priv->hscroll))
 608     {
 609       if (clutter_actor_get_text_direction (actor) == CLUTTER_TEXT_DIRECTION_RTL)
 610         {
 611           child_box.x1 = content_box.x1 + (vscrollbar_visible ? sb_width : 0);
 612           child_box.x2 = content_box.x2;
 613         }
 614       else
 615         {
 616           child_box.x1 = content_box.x1;
 617           child_box.x2 = content_box.x2 - (vscrollbar_visible ? sb_width : 0);
 618         }
 619       child_box.y1 = content_box.y2 - sb_height;
 620       child_box.y2 = content_box.y2;
 621 
 622       clutter_actor_allocate (priv->hscroll, &child_box, flags);
 623     }
 624 
 625   /* Now fold visibility into the scrollbar sizes to simplify the rest
 626    * of the computations.
 627    */
 628   if (!hscrollbar_visible)
 629     sb_height = 0;
 630   if (!vscrollbar_visible)
 631     sb_width = 0;
 632 
 633   /* Child */
 634   if (clutter_actor_get_text_direction (actor) == CLUTTER_TEXT_DIRECTION_RTL)
 635     {
 636       child_box.x1 = content_box.x1 + sb_width;
 637       child_box.x2 = content_box.x2;
 638     }
 639   else
 640     {
 641       child_box.x1 = content_box.x1;
 642       child_box.x2 = content_box.x2 - sb_width;
 643     }
 644   child_box.y1 = content_box.y1;
 645   child_box.y2 = content_box.y2 - sb_height;
 646 
 647   if (priv->child)
 648     clutter_actor_allocate (priv->child, &child_box, flags);
 649 
 650   if (priv->hscrollbar_visible != hscrollbar_visible)
 651     {
 652       g_object_freeze_notify (G_OBJECT (actor));
 653       priv->hscrollbar_visible = hscrollbar_visible;
 654       g_object_notify (G_OBJECT (actor), "hscrollbar-visible");
 655       g_object_thaw_notify (G_OBJECT (actor));
 656     }
 657 
 658   if (priv->vscrollbar_visible != vscrollbar_visible)
 659     {
 660       g_object_freeze_notify (G_OBJECT (actor));
 661       priv->vscrollbar_visible = vscrollbar_visible;
 662       g_object_notify (G_OBJECT (actor), "vscrollbar-visible");
 663       g_object_thaw_notify (G_OBJECT (actor));
 664     }
 665 
 666 }
 667 
 668 static void
 669 st_scroll_view_style_changed (StWidget *widget)
 670 {
 671   StScrollView *self = ST_SCROLL_VIEW (widget);
 672   StScrollViewPrivate *priv = self->priv;
 673 
 674   StThemeNode *theme_node = st_widget_get_theme_node (widget);
 675   gdouble vfade_offset = st_theme_node_get_length (theme_node, "-st-vfade-offset");
 676   gdouble hfade_offset = st_theme_node_get_length (theme_node, "-st-hfade-offset");
 677   st_scroll_view_update_fade_effect (self, vfade_offset, hfade_offset);
 678 
 679   st_widget_style_changed (ST_WIDGET (priv->hscroll));
 680   st_widget_style_changed (ST_WIDGET (priv->vscroll));
 681 
 682   ST_WIDGET_CLASS (st_scroll_view_parent_class)->style_changed (widget);
 683 }
 684 
 685 static gboolean
 686 st_scroll_view_scroll_event (ClutterActor       *self,
 687                              ClutterScrollEvent *event)
 688 {
 689   StScrollViewPrivate *priv = ST_SCROLL_VIEW (self)->priv;
 690   gdouble value, step, hvalue, vvalue, delta_x, delta_y;
 691 
 692   /* don't handle scroll events if requested not to */
 693   if (!priv->mouse_scroll)
 694     return FALSE;
 695 
 696   switch (event->direction)
 697     {
 698     case CLUTTER_SCROLL_SMOOTH:
 699       clutter_event_get_scroll_delta ((ClutterEvent *)event,
 700                                       &delta_x, &delta_y);
 701       g_object_get (priv->hadjustment,
 702                     "value", &hvalue,
 703                     NULL);
 704       g_object_get (priv->vadjustment,
 705                     "value", &vvalue,
 706                     NULL);
 707       break;
 708     case CLUTTER_SCROLL_UP:
 709     case CLUTTER_SCROLL_DOWN:
 710       g_object_get (priv->vadjustment,
 711                     "step-increment", &step,
 712                     "value", &value,
 713                     NULL);
 714       break;
 715     case CLUTTER_SCROLL_LEFT:
 716     case CLUTTER_SCROLL_RIGHT:
 717       g_object_get (priv->hadjustment,
 718                     "step-increment", &step,
 719                     "value", &value,
 720                     NULL);
 721       break;
 722     }
 723 
 724   switch (event->direction)
 725     {
 726     case CLUTTER_SCROLL_SMOOTH:
 727       st_adjustment_set_value (priv->hadjustment, hvalue + delta_x);
 728       st_adjustment_set_value (priv->vadjustment, vvalue + delta_y);
 729       break;
 730     case CLUTTER_SCROLL_UP:
 731       st_adjustment_set_value (priv->vadjustment, value - step);
 732       break;
 733     case CLUTTER_SCROLL_DOWN:
 734       st_adjustment_set_value (priv->vadjustment, value + step);
 735       break;
 736     case CLUTTER_SCROLL_LEFT:
 737       st_adjustment_set_value (priv->hadjustment, value - step);
 738       break;
 739     case CLUTTER_SCROLL_RIGHT:
 740       st_adjustment_set_value (priv->hadjustment, value + step);
 741       break;
 742     }
 743 
 744   return TRUE;
 745 }
 746 
 747 static void
 748 st_scroll_view_class_init (StScrollViewClass *klass)
 749 {
 750   GParamSpec *pspec;
 751   GObjectClass *object_class = G_OBJECT_CLASS (klass);
 752   ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
 753   StWidgetClass *widget_class = ST_WIDGET_CLASS (klass);
 754 
 755   g_type_class_add_private (klass, sizeof (StScrollViewPrivate));
 756 
 757   object_class->get_property = st_scroll_view_get_property;
 758   object_class->set_property = st_scroll_view_set_property;
 759   object_class->dispose = st_scroll_view_dispose;
 760 
 761   actor_class->paint = st_scroll_view_paint;
 762   actor_class->pick = st_scroll_view_pick;
 763   actor_class->get_preferred_width = st_scroll_view_get_preferred_width;
 764   actor_class->get_preferred_height = st_scroll_view_get_preferred_height;
 765   actor_class->allocate = st_scroll_view_allocate;
 766   actor_class->scroll_event = st_scroll_view_scroll_event;
 767 
 768   widget_class->style_changed = st_scroll_view_style_changed;
 769 
 770   g_object_class_install_property (object_class,
 771                                    PROP_HSCROLL,
 772                                    g_param_spec_object ("hscroll",
 773                                                         "StScrollBar",
 774                                                         "Horizontal scroll indicator",
 775                                                         ST_TYPE_SCROLL_BAR,
 776                                                         G_PARAM_READABLE));
 777 
 778   g_object_class_install_property (object_class,
 779                                    PROP_VSCROLL,
 780                                    g_param_spec_object ("vscroll",
 781                                                         "StScrollBar",
 782                                                         "Vertical scroll indicator",
 783                                                         ST_TYPE_SCROLL_BAR,
 784                                                         G_PARAM_READABLE));
 785 
 786 
 787   pspec = g_param_spec_enum ("vscrollbar-policy",
 788                              "Vertical Scrollbar Policy",
 789                              "When the vertical scrollbar is displayed",
 790                              GTK_TYPE_POLICY_TYPE,
 791                              GTK_POLICY_AUTOMATIC,
 792                              G_PARAM_READWRITE);
 793   g_object_class_install_property (object_class, PROP_VSCROLLBAR_POLICY, pspec);
 794 
 795   pspec = g_param_spec_enum ("hscrollbar-policy",
 796                              "Horizontal Scrollbar Policy",
 797                              "When the horizontal scrollbar is displayed",
 798                              GTK_TYPE_POLICY_TYPE,
 799                              GTK_POLICY_AUTOMATIC,
 800                              G_PARAM_READWRITE);
 801   g_object_class_install_property (object_class, PROP_HSCROLLBAR_POLICY, pspec);
 802 
 803   pspec = g_param_spec_boolean ("hscrollbar-visible",
 804                                 "Horizontal Scrollbar Visibility",
 805                                 "Whether the horizontal scrollbar is visible",
 806                                 TRUE,
 807                                 G_PARAM_READABLE);
 808   g_object_class_install_property (object_class, PROP_HSCROLLBAR_VISIBLE, pspec);
 809 
 810   pspec = g_param_spec_boolean ("vscrollbar-visible",
 811                                 "Vertical Scrollbar Visibility",
 812                                 "Whether the vertical scrollbar is visible",
 813                                 TRUE,
 814                                 G_PARAM_READABLE);
 815   g_object_class_install_property (object_class, PROP_VSCROLLBAR_VISIBLE, pspec);
 816 
 817   pspec = g_param_spec_boolean ("enable-mouse-scrolling",
 818                                 "Enable Mouse Scrolling",
 819                                 "Enable automatic mouse wheel scrolling",
 820                                 TRUE,
 821                                 G_PARAM_READWRITE);
 822   g_object_class_install_property (object_class,
 823                                    PROP_MOUSE_SCROLL,
 824                                    pspec);
 825 
 826 }
 827 
 828 static void
 829 st_scroll_view_init (StScrollView *self)
 830 {
 831   StScrollViewPrivate *priv = self->priv = SCROLL_VIEW_PRIVATE (self);
 832 
 833   priv->hscrollbar_policy = GTK_POLICY_AUTOMATIC;
 834   priv->vscrollbar_policy = GTK_POLICY_AUTOMATIC;
 835 
 836   priv->hadjustment = g_object_new (ST_TYPE_ADJUSTMENT, NULL);
 837   priv->hscroll = g_object_new (ST_TYPE_SCROLL_BAR,
 838                                 "adjustment", priv->hadjustment,
 839                                 "vertical", FALSE,
 840                                 NULL);
 841 
 842   priv->vadjustment = g_object_new (ST_TYPE_ADJUSTMENT, NULL);
 843   priv->vscroll = g_object_new (ST_TYPE_SCROLL_BAR,
 844                                 "adjustment", priv->vadjustment,
 845                                 "vertical", TRUE,
 846                                 NULL);
 847 
 848   clutter_actor_add_child (CLUTTER_ACTOR (self), priv->hscroll);
 849   clutter_actor_add_child (CLUTTER_ACTOR (self), priv->vscroll);
 850 
 851   /* mouse scroll is enabled by default, so we also need to be reactive */
 852   priv->mouse_scroll = TRUE;
 853   g_object_set (G_OBJECT (self), "reactive", TRUE, NULL);
 854 }
 855 
 856 static void
 857 st_scroll_view_add (ClutterContainer *container,
 858                     ClutterActor     *actor)
 859 {
 860   StScrollView *self = ST_SCROLL_VIEW (container);
 861   StScrollViewPrivate *priv = self->priv;
 862 
 863   if (ST_IS_SCROLLABLE (actor))
 864     {
 865       priv->child = actor;
 866 
 867       /* chain up to StBin::add() */
 868       st_scroll_view_parent_iface->add (container, actor);
 869 
 870       st_scrollable_set_adjustments (ST_SCROLLABLE (actor),
 871                                      priv->hadjustment, priv->vadjustment);
 872     }
 873   else
 874     {
 875       g_warning ("Attempting to add an actor of type %s to "
Access to field 'g_class' results in a dereference of a null pointer (loaded from variable 'actor')
(emitted by clang-analyzer)

TODO: a detailed trace is available in the data model (not yet rendered in this report)

876 "a StScrollView, but the actor does " 877 "not implement StScrollable.", 878 g_type_name (G_OBJECT_TYPE (actor))); 879 } 880 } 881 882 static void 883 st_scroll_view_remove (ClutterContainer *container, 884 ClutterActor *actor) 885 { 886 StScrollView *self = ST_SCROLL_VIEW (container); 887 StScrollViewPrivate *priv = self->priv; 888 889 if (actor == priv->child) 890 { 891 g_object_ref (priv->child); 892 893 /* chain up to StBin::remove() */ 894 st_scroll_view_parent_iface->remove (container, actor); 895 896 st_scrollable_set_adjustments (ST_SCROLLABLE (priv->child), 897 NULL, NULL); 898 899 g_object_unref (priv->child); 900 priv->child = NULL; 901 } 902 else 903 { 904 if (actor == priv->vscroll) 905 priv->vscroll = NULL; 906 else if (actor == priv->hscroll) 907 priv->hscroll = NULL; 908 else 909 g_assert ("Unknown child removed from StScrollView"); 910 911 clutter_actor_remove_child (CLUTTER_ACTOR (container), actor); 912 } 913 } 914 915 static void 916 st_scroll_view_foreach_with_internals (ClutterContainer *container, 917 ClutterCallback callback, 918 gpointer user_data) 919 { 920 StScrollViewPrivate *priv = ST_SCROLL_VIEW (container)->priv; 921 922 if (priv->child != NULL) 923 callback (priv->child, user_data); 924 925 if (priv->hscroll != NULL) 926 callback (priv->hscroll, user_data); 927 928 if (priv->vscroll != NULL) 929 callback (priv->vscroll, user_data); 930 } 931 932 static void 933 clutter_container_iface_init (ClutterContainerIface *iface) 934 { 935 /* store a pointer to the StBin implementation of 936 * ClutterContainer so that we can chain up when 937 * overriding the methods 938 */ 939 st_scroll_view_parent_iface = g_type_interface_peek_parent (iface); 940 941 iface->add = st_scroll_view_add; 942 iface->remove = st_scroll_view_remove; 943 iface->foreach_with_internals = st_scroll_view_foreach_with_internals; 944 } 945 946 StWidget * 947 st_scroll_view_new (void) 948 { 949 return g_object_new (ST_TYPE_SCROLL_VIEW, NULL); 950 } 951 952 /** 953 * st_scroll_view_get_hscroll_bar: 954 * @scroll: a #StScrollView 955 * 956 * Gets the horizontal scrollbar of the scrollbiew 957 * 958 * Return value: (transfer none): the horizontal #StScrollBar 959 */ 960 ClutterActor * 961 st_scroll_view_get_hscroll_bar (StScrollView *scroll) 962 { 963 g_return_val_if_fail (ST_IS_SCROLL_VIEW (scroll), NULL); 964 965 return scroll->priv->hscroll; 966 } 967 968 /** 969 * st_scroll_view_get_vscroll_bar: 970 * @scroll: a #StScrollView 971 * 972 * Gets the vertical scrollbar of the scrollbiew 973 * 974 * Return value: (transfer none): the vertical #StScrollBar 975 */ 976 ClutterActor * 977 st_scroll_view_get_vscroll_bar (StScrollView *scroll) 978 { 979 g_return_val_if_fail (ST_IS_SCROLL_VIEW (scroll), NULL); 980 981 return scroll->priv->vscroll; 982 } 983 984 gfloat 985 st_scroll_view_get_column_size (StScrollView *scroll) 986 { 987 gdouble column_size; 988 989 g_return_val_if_fail (scroll, 0); 990 991 g_object_get (scroll->priv->hadjustment, 992 "step-increment", &column_size, 993 NULL); 994 995 return column_size; 996 } 997 998 void 999 st_scroll_view_set_column_size (StScrollView *scroll, 1000 gfloat column_size) 1001 { 1002 g_return_if_fail (scroll); 1003 1004 if (column_size < 0) 1005 { 1006 scroll->priv->column_size_set = FALSE; 1007 scroll->priv->column_size = -1; 1008 } 1009 else 1010 { 1011 scroll->priv->column_size_set = TRUE; 1012 scroll->priv->column_size = column_size; 1013 1014 g_object_set (scroll->priv->hadjustment, 1015 "step-increment", (gdouble) scroll->priv->column_size, 1016 NULL); 1017 } 1018 } 1019 1020 gfloat 1021 st_scroll_view_get_row_size (StScrollView *scroll) 1022 { 1023 gdouble row_size; 1024 1025 g_return_val_if_fail (scroll, 0); 1026 1027 g_object_get (scroll->priv->vadjustment, 1028 "step-increment", &row_size, 1029 NULL); 1030 1031 return row_size; 1032 } 1033 1034 void 1035 st_scroll_view_set_row_size (StScrollView *scroll, 1036 gfloat row_size) 1037 { 1038 g_return_if_fail (scroll); 1039 1040 if (row_size < 0) 1041 { 1042 scroll->priv->row_size_set = FALSE; 1043 scroll->priv->row_size = -1; 1044 } 1045 else 1046 { 1047 scroll->priv->row_size_set = TRUE; 1048 scroll->priv->row_size = row_size; 1049 1050 g_object_set (scroll->priv->vadjustment, 1051 "step-increment", (gdouble) scroll->priv->row_size, 1052 NULL); 1053 } 1054 } 1055 1056 void 1057 st_scroll_view_set_mouse_scrolling (StScrollView *scroll, 1058 gboolean enabled) 1059 { 1060 StScrollViewPrivate *priv; 1061 1062 g_return_if_fail (ST_IS_SCROLL_VIEW (scroll)); 1063 1064 priv = ST_SCROLL_VIEW (scroll)->priv; 1065 1066 if (priv->mouse_scroll != enabled) 1067 { 1068 priv->mouse_scroll = enabled; 1069 1070 /* make sure we can receive mouse wheel events */ 1071 if (enabled) 1072 clutter_actor_set_reactive ((ClutterActor *) scroll, TRUE); 1073 } 1074 } 1075 1076 gboolean 1077 st_scroll_view_get_mouse_scrolling (StScrollView *scroll) 1078 { 1079 StScrollViewPrivate *priv; 1080 1081 g_return_val_if_fail (ST_IS_SCROLL_VIEW (scroll), FALSE); 1082 1083 priv = ST_SCROLL_VIEW (scroll)->priv; 1084 1085 return priv->mouse_scroll; 1086 } 1087 1088 /** 1089 * st_scroll_view_set_policy: 1090 * @scroll: A #StScrollView 1091 * @hscroll: Whether to enable horizontal scrolling 1092 * @vscroll: Whether to enable vertical scrolling 1093 * 1094 * Set the scroll policy. 1095 */ 1096 void 1097 st_scroll_view_set_policy (StScrollView *scroll, 1098 GtkPolicyType hscroll, 1099 GtkPolicyType vscroll) 1100 { 1101 StScrollViewPrivate *priv; 1102 1103 g_return_if_fail (ST_IS_SCROLL_VIEW (scroll)); 1104 1105 priv = ST_SCROLL_VIEW (scroll)->priv; 1106 1107 if (priv->hscrollbar_policy == hscroll && priv->vscrollbar_policy == vscroll) 1108 return; 1109 1110 g_object_freeze_notify ((GObject *) scroll); 1111 1112 if (priv->hscrollbar_policy != hscroll) 1113 { 1114 priv->hscrollbar_policy = hscroll; 1115 g_object_notify ((GObject *) scroll, "hscrollbar-policy"); 1116 } 1117 1118 if (priv->vscrollbar_policy != vscroll) 1119 { 1120 priv->vscrollbar_policy = vscroll; 1121 g_object_notify ((GObject *) scroll, "vscrollbar-policy"); 1122 } 1123 1124 clutter_actor_queue_relayout (CLUTTER_ACTOR (scroll)); 1125 1126 g_object_thaw_notify ((GObject *) scroll); 1127 }