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 "
(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 }