~mterry/ubuntu/natty/gnome-shell/wip

« back to all changes in this revision

Viewing changes to src/st/st-scroll-bar.c

  • Committer: Bazaar Package Importer
  • Author(s): Sebastien Bacher
  • Date: 2009-10-12 22:44:00 UTC
  • mfrom: (1.1.5 upstream)
  • Revision ID: james.westby@ubuntu.com-20091012224400-k91p42yvou07i525
Tags: 2.28.0-0ubuntu1
* New upstream version
* debian/control:
  - updated build requirement
* debian/patches/80_git_change_fix_alt_tab_ressource_usage.patch:
  - git change to fix ressources not being freed on alt-tab

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
 
2
/*
 
3
 * st-scroll-bar.c: Scroll bar actor
 
4
 *
 
5
 * Copyright 2008 OpenedHand
 
6
 * Copyright 2009 Intel Corporation.
 
7
 *
 
8
 * This program is free software; you can redistribute it and/or modify it
 
9
 * under the terms and conditions of the GNU Lesser General Public License,
 
10
 * version 2.1, as published by the Free Software Foundation.
 
11
 *
 
12
 * This program is distributed in the hope it will be useful, but WITHOUT ANY
 
13
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 
14
 * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
 
15
 * more details.
 
16
 *
 
17
 * You should have received a copy of the GNU Lesser General Public License
 
18
 * along with this program; if not, write to the Free Software Foundation,
 
19
 * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
 
20
 *
 
21
 * Written by: Chris Lord <chris@openedhand.com>
 
22
 * Port to St by: Robert Staudinger <robsta@openedhand.com>
 
23
 *
 
24
 */
 
25
 
 
26
/**
 
27
 * SECTION:st-scroll-bar
 
28
 * @short_description: a user interface element to control scrollable areas.
 
29
 *
 
30
 * The #StScrollBar allows users to scroll scrollable actors, either by
 
31
 * the step or page amount, or by manually dragging the handle.
 
32
 */
 
33
 
 
34
#ifdef HAVE_CONFIG_H
 
35
#include "config.h"
 
36
#endif
 
37
 
 
38
#include <clutter/clutter.h>
 
39
 
 
40
#include "st-scroll-bar.h"
 
41
#include "st-bin.h"
 
42
#include "st-marshal.h"
 
43
#include "st-enum-types.h"
 
44
#include "st-private.h"
 
45
#include "st-button.h"
 
46
 
 
47
G_DEFINE_TYPE (StScrollBar, st_scroll_bar, ST_TYPE_BIN)
 
48
 
 
49
#define ST_SCROLL_BAR_GET_PRIVATE(o)  (G_TYPE_INSTANCE_GET_PRIVATE ((o), ST_TYPE_SCROLL_BAR, StScrollBarPrivate))
 
50
 
 
51
#define PAGING_INITIAL_REPEAT_TIMEOUT 500
 
52
#define PAGING_SUBSEQUENT_REPEAT_TIMEOUT 200
 
53
 
 
54
struct _StScrollBarPrivate
 
55
{
 
56
  StAdjustment *adjustment;
 
57
 
 
58
  gulong        capture_handler;
 
59
  gfloat        x_origin;
 
60
  gfloat        y_origin;
 
61
 
 
62
  ClutterActor *bw_stepper;
 
63
  ClutterActor *fw_stepper;
 
64
  ClutterActor *trough;
 
65
  ClutterActor *handle;
 
66
 
 
67
  gfloat        move_x;
 
68
  gfloat        move_y;
 
69
 
 
70
  /* Trough-click handling. */
 
71
  enum { NONE, UP, DOWN }  paging_direction;
 
72
  guint             paging_source_id;
 
73
  guint             paging_event_no;
 
74
 
 
75
  gboolean          stepper_forward;
 
76
  guint             stepper_source_id;
 
77
 
 
78
  ClutterAnimation *paging_animation;
 
79
 
 
80
  gboolean          vertical;
 
81
};
 
82
 
 
83
enum
 
84
{
 
85
  PROP_0,
 
86
 
 
87
  PROP_ADJUSTMENT,
 
88
  PROP_VERTICAL
 
89
};
 
90
 
 
91
enum
 
92
{
 
93
  SCROLL_START,
 
94
  SCROLL_STOP,
 
95
 
 
96
  LAST_SIGNAL
 
97
};
 
98
 
 
99
static guint signals[LAST_SIGNAL] = { 0, };
 
100
 
 
101
static gboolean
 
102
handle_button_press_event_cb (ClutterActor       *actor,
 
103
                              ClutterButtonEvent *event,
 
104
                              StScrollBar        *bar);
 
105
 
 
106
static void
 
107
st_scroll_bar_get_property (GObject    *gobject,
 
108
                            guint       prop_id,
 
109
                            GValue     *value,
 
110
                            GParamSpec *pspec)
 
111
{
 
112
  StScrollBarPrivate *priv = ST_SCROLL_BAR (gobject)->priv;
 
113
 
 
114
  switch (prop_id)
 
115
    {
 
116
    case PROP_ADJUSTMENT:
 
117
      g_value_set_object (value, priv->adjustment);
 
118
      break;
 
119
 
 
120
    case PROP_VERTICAL:
 
121
      g_value_set_boolean (value, priv->vertical);
 
122
      break;
 
123
 
 
124
    default:
 
125
      G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
 
126
      break;
 
127
    }
 
128
}
 
129
 
 
130
static void
 
131
st_scroll_bar_set_property (GObject      *gobject,
 
132
                            guint         prop_id,
 
133
                            const GValue *value,
 
134
                            GParamSpec   *pspec)
 
135
{
 
136
  StScrollBar *bar = ST_SCROLL_BAR (gobject);
 
137
 
 
138
  switch (prop_id)
 
139
    {
 
140
    case PROP_ADJUSTMENT:
 
141
      st_scroll_bar_set_adjustment (bar, g_value_get_object (value));
 
142
      break;
 
143
 
 
144
    case PROP_VERTICAL:
 
145
      bar->priv->vertical = g_value_get_boolean (value);
 
146
      if (bar->priv->vertical)
 
147
        {
 
148
          clutter_actor_set_name (CLUTTER_ACTOR (bar->priv->bw_stepper),
 
149
                                  "up-stepper");
 
150
          clutter_actor_set_name (CLUTTER_ACTOR (bar->priv->fw_stepper),
 
151
                                  "down-stepper");
 
152
          clutter_actor_set_name (CLUTTER_ACTOR (bar->priv->handle),
 
153
                                  "vhandle");
 
154
        }
 
155
      else
 
156
        {
 
157
          clutter_actor_set_name (CLUTTER_ACTOR (bar->priv->fw_stepper),
 
158
                                  "forward-stepper");
 
159
          clutter_actor_set_name (CLUTTER_ACTOR (bar->priv->bw_stepper),
 
160
                                  "backward-stepper");
 
161
          clutter_actor_set_name (CLUTTER_ACTOR (bar->priv->handle),
 
162
                                  "hhandle");
 
163
        }
 
164
      clutter_actor_queue_relayout ((ClutterActor*) gobject);
 
165
      break;
 
166
 
 
167
    default:
 
168
      G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
 
169
      break;
 
170
    }
 
171
}
 
172
 
 
173
static void
 
174
st_scroll_bar_dispose (GObject *gobject)
 
175
{
 
176
  StScrollBar *bar = ST_SCROLL_BAR (gobject);
 
177
  StScrollBarPrivate *priv = bar->priv;
 
178
 
 
179
  if (priv->adjustment)
 
180
    st_scroll_bar_set_adjustment (bar, NULL);
 
181
 
 
182
  if (priv->handle)
 
183
    {
 
184
      g_signal_handlers_disconnect_by_func (priv->handle,
 
185
                                            G_CALLBACK (handle_button_press_event_cb),
 
186
                                            bar);
 
187
      clutter_actor_unparent (priv->handle);
 
188
      priv->handle = NULL;
 
189
    }
 
190
 
 
191
  clutter_actor_unparent (priv->bw_stepper);
 
192
  priv->bw_stepper = NULL;
 
193
 
 
194
  clutter_actor_unparent (priv->fw_stepper);
 
195
  priv->fw_stepper = NULL;
 
196
 
 
197
  clutter_actor_unparent (priv->trough);
 
198
  priv->trough = NULL;
 
199
 
 
200
  G_OBJECT_CLASS (st_scroll_bar_parent_class)->dispose (gobject);
 
201
}
 
202
 
 
203
static void
 
204
st_scroll_bar_paint (ClutterActor *actor)
 
205
{
 
206
  StScrollBarPrivate *priv = ST_SCROLL_BAR (actor)->priv;
 
207
 
 
208
  CLUTTER_ACTOR_CLASS (st_scroll_bar_parent_class)->paint (actor);
 
209
 
 
210
  clutter_actor_paint (priv->bw_stepper);
 
211
 
 
212
  clutter_actor_paint (priv->fw_stepper);
 
213
 
 
214
  clutter_actor_paint (priv->trough);
 
215
 
 
216
  if (priv->handle && CLUTTER_ACTOR_IS_VISIBLE (priv->handle))
 
217
    clutter_actor_paint (priv->handle);
 
218
}
 
219
 
 
220
static void
 
221
st_scroll_bar_pick (ClutterActor       *actor,
 
222
                    const ClutterColor *pick_color)
 
223
{
 
224
  StScrollBarPrivate *priv = ST_SCROLL_BAR (actor)->priv;
 
225
 
 
226
  CLUTTER_ACTOR_CLASS (st_scroll_bar_parent_class)->pick (actor, pick_color);
 
227
 
 
228
  clutter_actor_paint (priv->bw_stepper);
 
229
  clutter_actor_paint (priv->fw_stepper);
 
230
  clutter_actor_paint (priv->trough);
 
231
 
 
232
  if (priv->handle && priv->adjustment)
 
233
    clutter_actor_paint (priv->handle);
 
234
}
 
235
 
 
236
static void
 
237
st_scroll_bar_map (ClutterActor *actor)
 
238
{
 
239
  StScrollBarPrivate *priv = ST_SCROLL_BAR (actor)->priv;
 
240
 
 
241
  CLUTTER_ACTOR_CLASS (st_scroll_bar_parent_class)->map (actor);
 
242
 
 
243
  clutter_actor_map (priv->bw_stepper);
 
244
  clutter_actor_map (priv->fw_stepper);
 
245
  clutter_actor_map (priv->trough);
 
246
 
 
247
  if (priv->handle)
 
248
    clutter_actor_map (priv->handle);
 
249
}
 
250
 
 
251
static void
 
252
st_scroll_bar_unmap (ClutterActor *actor)
 
253
{
 
254
  StScrollBarPrivate *priv = ST_SCROLL_BAR (actor)->priv;
 
255
 
 
256
  CLUTTER_ACTOR_CLASS (st_scroll_bar_parent_class)->unmap (actor);
 
257
 
 
258
  clutter_actor_unmap (priv->bw_stepper);
 
259
  clutter_actor_unmap (priv->fw_stepper);
 
260
  clutter_actor_unmap (priv->trough);
 
261
 
 
262
  if (priv->handle)
 
263
    clutter_actor_unmap (priv->handle);
 
264
}
 
265
 
 
266
static void
 
267
st_scroll_bar_allocate (ClutterActor          *actor,
 
268
                        const ClutterActorBox *box,
 
269
                        ClutterAllocationFlags flags)
 
270
{
 
271
  StScrollBarPrivate *priv = ST_SCROLL_BAR (actor)->priv;
 
272
  StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor));
 
273
  ClutterActorBox content_box, bw_box, fw_box, trough_box;
 
274
  gfloat stepper_size;
 
275
 
 
276
  /* Chain up */
 
277
  CLUTTER_ACTOR_CLASS (st_scroll_bar_parent_class)->
 
278
  allocate (actor, box, flags);
 
279
 
 
280
  st_theme_node_get_content_box (theme_node, box, &content_box);
 
281
 
 
282
  if (priv->vertical)
 
283
    {
 
284
      stepper_size = content_box.x2 - content_box.x1;
 
285
 
 
286
      /* Backward stepper */
 
287
      bw_box.x1 = content_box.x1;
 
288
      bw_box.y1 = content_box.y1;
 
289
      bw_box.x2 = content_box.x2;
 
290
      bw_box.y2 = bw_box.y1 + stepper_size;
 
291
      clutter_actor_allocate (priv->bw_stepper, &bw_box, flags);
 
292
 
 
293
      /* Forward stepper */
 
294
      fw_box.x1 = content_box.x1;
 
295
      fw_box.y1 = content_box.y2 - stepper_size;
 
296
      fw_box.x2 = content_box.x2;
 
297
      fw_box.y2 = content_box.y2;
 
298
      clutter_actor_allocate (priv->fw_stepper, &fw_box, flags);
 
299
 
 
300
      /* Trough */
 
301
      trough_box.x1 = content_box.x1;
 
302
      trough_box.y1 = content_box.y1 + stepper_size;
 
303
      trough_box.x2 = content_box.x2;
 
304
      trough_box.y2 = content_box.y2 - stepper_size;
 
305
      clutter_actor_allocate (priv->trough, &trough_box, flags);
 
306
 
 
307
    }
 
308
  else
 
309
    {
 
310
      stepper_size = content_box.y2 - content_box.y1;
 
311
 
 
312
      /* Backward stepper */
 
313
      bw_box.x1 = content_box.x1;
 
314
      bw_box.y1 = content_box.y1;
 
315
      bw_box.x2 = bw_box.x1 + stepper_size;
 
316
      bw_box.y2 = content_box.y2;
 
317
      clutter_actor_allocate (priv->bw_stepper, &bw_box, flags);
 
318
 
 
319
      /* Forward stepper */
 
320
      fw_box.x1 = content_box.x2 - stepper_size;
 
321
      fw_box.y1 = content_box.y1;
 
322
      fw_box.x2 = content_box.x2;
 
323
      fw_box.y2 = content_box.y2;
 
324
      clutter_actor_allocate (priv->fw_stepper, &fw_box, flags);
 
325
 
 
326
      /* Trough */
 
327
      trough_box.x1 = content_box.x1 + stepper_size;
 
328
      trough_box.y1 = content_box.y1;
 
329
      trough_box.x2 = content_box.x2 - stepper_size;
 
330
      trough_box.y2 = content_box.y2;
 
331
      clutter_actor_allocate (priv->trough, &trough_box, flags);
 
332
    }
 
333
 
 
334
 
 
335
  if (priv->adjustment)
 
336
    {
 
337
      StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (actor));
 
338
      float handle_size, position, avail_size;
 
339
      gdouble value, lower, upper, page_size, increment, min_size, max_size;
 
340
      ClutterActorBox handle_box = { 0, };
 
341
 
 
342
      st_adjustment_get_values (priv->adjustment,
 
343
                                &value,
 
344
                                &lower,
 
345
                                &upper,
 
346
                                NULL,
 
347
                                NULL,
 
348
                                &page_size);
 
349
 
 
350
      if ((upper == lower)
 
351
          || (page_size >= (upper - lower)))
 
352
        increment = 1.0;
 
353
      else
 
354
        increment = page_size / (upper - lower);
 
355
 
 
356
      min_size = 32.;
 
357
      st_theme_node_get_length (theme_node, "min-size", FALSE, &min_size);
 
358
      max_size = G_MAXINT16;
 
359
      st_theme_node_get_length (theme_node, "max-size", FALSE, &max_size);
 
360
 
 
361
      if (upper - lower - page_size <= 0)
 
362
        position = 0;
 
363
      else
 
364
        position = (value - lower) / (upper - lower - page_size);
 
365
 
 
366
      if (priv->vertical)
 
367
        {
 
368
          avail_size = content_box.y2 - content_box.y1 - stepper_size * 2;
 
369
          handle_size = increment * avail_size;
 
370
          handle_size = CLAMP (handle_size, min_size, max_size);
 
371
 
 
372
          handle_box.x1 = content_box.x1;
 
373
          handle_box.y1 = bw_box.y2 + position * (avail_size - handle_size);
 
374
 
 
375
          handle_box.x2 = content_box.x2;
 
376
          handle_box.y2 = handle_box.y1 + handle_size;
 
377
        }
 
378
      else
 
379
        {
 
380
          avail_size = content_box.x2 - content_box.x1 - stepper_size * 2;
 
381
          handle_size = increment * avail_size;
 
382
          handle_size = CLAMP (handle_size, min_size, max_size);
 
383
 
 
384
          handle_box.x1 = bw_box.x2 + position * (avail_size - handle_size);
 
385
          handle_box.y1 = content_box.y1;
 
386
 
 
387
          handle_box.x2 = handle_box.x1 + handle_size;
 
388
          handle_box.y2 = content_box.y2;
 
389
        }
 
390
 
 
391
      /* snap to pixel */
 
392
      handle_box.x1 = (int) handle_box.x1;
 
393
      handle_box.y1 = (int) handle_box.y1;
 
394
      handle_box.x2 = (int) handle_box.x2;
 
395
      handle_box.y2 = (int) handle_box.y2;
 
396
 
 
397
      clutter_actor_allocate (priv->handle,
 
398
                              &handle_box,
 
399
                              flags);
 
400
    }
 
401
}
 
402
 
 
403
static void
 
404
st_scroll_bar_style_changed (StWidget *widget)
 
405
{
 
406
  StScrollBarPrivate *priv = ST_SCROLL_BAR (widget)->priv;
 
407
 
 
408
  st_widget_style_changed (ST_WIDGET (priv->bw_stepper));
 
409
  st_widget_style_changed (ST_WIDGET (priv->fw_stepper));
 
410
  st_widget_style_changed (ST_WIDGET (priv->trough));
 
411
  st_widget_style_changed (ST_WIDGET (priv->handle));
 
412
 
 
413
  ST_WIDGET_CLASS (st_scroll_bar_parent_class)->style_changed (widget);
 
414
}
 
415
 
 
416
static void
 
417
bar_reactive_notify_cb (GObject    *gobject,
 
418
                        GParamSpec *arg1,
 
419
                        gpointer    user_data)
 
420
{
 
421
  StScrollBar *bar = ST_SCROLL_BAR (gobject);
 
422
 
 
423
  clutter_actor_set_reactive (bar->priv->handle,
 
424
                              clutter_actor_get_reactive (CLUTTER_ACTOR (bar)));
 
425
}
 
426
 
 
427
static GObject*
 
428
st_scroll_bar_constructor (GType                  type,
 
429
                           guint                  n_properties,
 
430
                           GObjectConstructParam *properties)
 
431
{
 
432
  GObjectClass *gobject_class;
 
433
  GObject *obj;
 
434
  StScrollBar *bar;
 
435
  StScrollBarPrivate *priv;
 
436
 
 
437
  gobject_class = G_OBJECT_CLASS (st_scroll_bar_parent_class);
 
438
  obj = gobject_class->constructor (type, n_properties, properties);
 
439
 
 
440
  bar  = ST_SCROLL_BAR (obj);
 
441
  priv = ST_SCROLL_BAR_GET_PRIVATE (bar);
 
442
 
 
443
  g_signal_connect (bar, "notify::reactive",
 
444
                    G_CALLBACK (bar_reactive_notify_cb), NULL);
 
445
 
 
446
  return obj;
 
447
}
 
448
 
 
449
static gboolean
 
450
st_scroll_bar_scroll_event (ClutterActor       *actor,
 
451
                            ClutterScrollEvent *event)
 
452
{
 
453
  StScrollBarPrivate *priv = ST_SCROLL_BAR (actor)->priv;
 
454
  gdouble lower, step, upper, value;
 
455
 
 
456
  if (priv->adjustment)
 
457
    {
 
458
      g_object_get (priv->adjustment,
 
459
                    "lower", &lower,
 
460
                    "step-increment", &step,
 
461
                    "upper", &upper,
 
462
                    "value", &value,
 
463
                    NULL);
 
464
    }
 
465
  else
 
466
    {
 
467
      return FALSE;
 
468
    }
 
469
 
 
470
  switch (event->direction)
 
471
    {
 
472
    case CLUTTER_SCROLL_UP:
 
473
    case CLUTTER_SCROLL_LEFT:
 
474
      if (value == lower)
 
475
        return FALSE;
 
476
      else
 
477
        st_adjustment_set_value (priv->adjustment, value - step);
 
478
      break;
 
479
    case CLUTTER_SCROLL_DOWN:
 
480
    case CLUTTER_SCROLL_RIGHT:
 
481
      if (value == upper)
 
482
        return FALSE;
 
483
      else
 
484
        st_adjustment_set_value (priv->adjustment, value + step);
 
485
      break;
 
486
    }
 
487
 
 
488
  return TRUE;
 
489
}
 
490
 
 
491
static void
 
492
st_scroll_bar_class_init (StScrollBarClass *klass)
 
493
{
 
494
  GObjectClass *object_class = G_OBJECT_CLASS (klass);
 
495
  ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
 
496
  StWidgetClass *widget_class = ST_WIDGET_CLASS (klass);
 
497
  GParamSpec *pspec;
 
498
 
 
499
  g_type_class_add_private (klass, sizeof (StScrollBarPrivate));
 
500
 
 
501
  object_class->get_property = st_scroll_bar_get_property;
 
502
  object_class->set_property = st_scroll_bar_set_property;
 
503
  object_class->dispose      = st_scroll_bar_dispose;
 
504
  object_class->constructor  = st_scroll_bar_constructor;
 
505
 
 
506
  actor_class->allocate       = st_scroll_bar_allocate;
 
507
  actor_class->paint          = st_scroll_bar_paint;
 
508
  actor_class->pick           = st_scroll_bar_pick;
 
509
  actor_class->scroll_event   = st_scroll_bar_scroll_event;
 
510
  actor_class->map            = st_scroll_bar_map;
 
511
  actor_class->unmap          = st_scroll_bar_unmap;
 
512
 
 
513
  widget_class->style_changed = st_scroll_bar_style_changed;
 
514
 
 
515
  g_object_class_install_property
 
516
                 (object_class,
 
517
                 PROP_ADJUSTMENT,
 
518
                 g_param_spec_object ("adjustment",
 
519
                                      "Adjustment",
 
520
                                      "The adjustment",
 
521
                                      ST_TYPE_ADJUSTMENT,
 
522
                                      ST_PARAM_READWRITE));
 
523
 
 
524
  pspec = g_param_spec_boolean ("vertical",
 
525
                                "Vertical Orientation",
 
526
                                "Vertical Orientation",
 
527
                                FALSE,
 
528
                                ST_PARAM_READWRITE);
 
529
  g_object_class_install_property (object_class, PROP_VERTICAL, pspec);
 
530
 
 
531
  signals[SCROLL_START] =
 
532
    g_signal_new ("scroll-start",
 
533
                  G_TYPE_FROM_CLASS (klass),
 
534
                  G_SIGNAL_RUN_LAST,
 
535
                  G_STRUCT_OFFSET (StScrollBarClass, scroll_start),
 
536
                  NULL, NULL,
 
537
                  g_cclosure_marshal_VOID__VOID,
 
538
                  G_TYPE_NONE, 0);
 
539
 
 
540
  signals[SCROLL_STOP] =
 
541
    g_signal_new ("scroll-stop",
 
542
                  G_TYPE_FROM_CLASS (klass),
 
543
                  G_SIGNAL_RUN_LAST,
 
544
                  G_STRUCT_OFFSET (StScrollBarClass, scroll_stop),
 
545
                  NULL, NULL,
 
546
                  g_cclosure_marshal_VOID__VOID,
 
547
                  G_TYPE_NONE, 0);
 
548
}
 
549
 
 
550
static void
 
551
move_slider (StScrollBar *bar,
 
552
             gfloat       x,
 
553
             gfloat       y)
 
554
{
 
555
  StScrollBarPrivate *priv = bar->priv;
 
556
  gdouble position, lower, upper, page_size;
 
557
  gfloat ux, uy, pos, size;
 
558
 
 
559
  if (!priv->adjustment)
 
560
    return;
 
561
 
 
562
  if (!clutter_actor_transform_stage_point (priv->trough, x, y, &ux, &uy))
 
563
    return;
 
564
 
 
565
  if (priv->vertical)
 
566
    size = clutter_actor_get_height (priv->trough)
 
567
           - clutter_actor_get_height (priv->handle);
 
568
  else
 
569
    size = clutter_actor_get_width (priv->trough)
 
570
           - clutter_actor_get_width (priv->handle);
 
571
 
 
572
  if (size == 0)
 
573
    return;
 
574
 
 
575
  if (priv->vertical)
 
576
    pos = uy - priv->y_origin;
 
577
  else
 
578
    pos = ux - priv->x_origin;
 
579
  pos = CLAMP (pos, 0, size);
 
580
 
 
581
  st_adjustment_get_values (priv->adjustment,
 
582
                            NULL,
 
583
                            &lower,
 
584
                            &upper,
 
585
                            NULL,
 
586
                            NULL,
 
587
                            &page_size);
 
588
 
 
589
  position = ((pos / size)
 
590
              * (upper - lower - page_size))
 
591
             + lower;
 
592
 
 
593
  st_adjustment_set_value (priv->adjustment, position);
 
594
}
 
595
 
 
596
static gboolean
 
597
handle_capture_event_cb (ClutterActor *trough,
 
598
                         ClutterEvent *event,
 
599
                         StScrollBar  *bar)
 
600
{
 
601
  if (clutter_event_type (event) == CLUTTER_MOTION)
 
602
    {
 
603
      move_slider (bar,
 
604
                   ((ClutterMotionEvent*) event)->x,
 
605
                   ((ClutterMotionEvent*) event)->y);
 
606
    }
 
607
  else if (clutter_event_type (event) == CLUTTER_BUTTON_RELEASE
 
608
           && ((ClutterButtonEvent*) event)->button == 1)
 
609
    {
 
610
      ClutterActor *stage, *target;
 
611
 
 
612
      stage = clutter_actor_get_stage(bar->priv->trough);
 
613
 
 
614
      if (bar->priv->capture_handler)
 
615
        {
 
616
          g_signal_handler_disconnect (stage, bar->priv->capture_handler);
 
617
          bar->priv->capture_handler = 0;
 
618
        }
 
619
 
 
620
      clutter_set_motion_events_enabled (TRUE);
 
621
      g_signal_emit (bar, signals[SCROLL_STOP], 0);
 
622
 
 
623
      /* check if the mouse pointer has left the handle during the drag and
 
624
       * remove the hover state if it has */
 
625
      target = clutter_stage_get_actor_at_pos ((ClutterStage*) stage,
 
626
                                               CLUTTER_PICK_REACTIVE,
 
627
                                               ((ClutterButtonEvent*) event)->x,
 
628
                                               ((ClutterButtonEvent*) event)->y);
 
629
      if (target != bar->priv->handle)
 
630
        {
 
631
          st_widget_set_style_pseudo_class ((StWidget*) bar->priv->handle, NULL);
 
632
        }
 
633
 
 
634
 
 
635
    }
 
636
 
 
637
  return TRUE;
 
638
}
 
639
 
 
640
static gboolean
 
641
handle_button_press_event_cb (ClutterActor       *actor,
 
642
                              ClutterButtonEvent *event,
 
643
                              StScrollBar        *bar)
 
644
{
 
645
  StScrollBarPrivate *priv = bar->priv;
 
646
 
 
647
  if (event->button != 1)
 
648
    return FALSE;
 
649
 
 
650
  if (!clutter_actor_transform_stage_point (priv->handle,
 
651
                                            event->x,
 
652
                                            event->y,
 
653
                                            &priv->x_origin,
 
654
                                            &priv->y_origin))
 
655
    return FALSE;
 
656
 
 
657
  /* Account for the scrollbar-trough-handle nesting. */
 
658
  priv->x_origin += clutter_actor_get_x (priv->trough);
 
659
  priv->y_origin += clutter_actor_get_y (priv->trough);
 
660
 
 
661
  /* Turn off picking for motion events */
 
662
  clutter_set_motion_events_enabled (FALSE);
 
663
 
 
664
  priv->capture_handler = g_signal_connect_after (
 
665
    clutter_actor_get_stage (priv->trough),
 
666
    "captured-event",
 
667
    G_CALLBACK (handle_capture_event_cb),
 
668
    bar);
 
669
  g_signal_emit (bar, signals[SCROLL_START], 0);
 
670
 
 
671
  return TRUE;
 
672
}
 
673
 
 
674
static void
 
675
animation_completed_cb (ClutterAnimation   *animation,
 
676
                        StScrollBarPrivate *priv)
 
677
{
 
678
  g_object_unref (priv->paging_animation);
 
679
  priv->paging_animation = NULL;
 
680
}
 
681
 
 
682
static gboolean
 
683
trough_paging_cb (StScrollBar *self)
 
684
{
 
685
  gfloat handle_pos, event_pos, tx, ty;
 
686
  gdouble value;
 
687
  gdouble page_increment;
 
688
  gboolean ret;
 
689
 
 
690
  gulong mode;
 
691
  ClutterAnimation *a;
 
692
  GValue v = { 0, };
 
693
  ClutterTimeline *t;
 
694
 
 
695
  if (self->priv->paging_event_no == 0)
 
696
    {
 
697
      /* Scroll on after initial timeout. */
 
698
      mode = CLUTTER_EASE_OUT_CUBIC;
 
699
      ret = FALSE;
 
700
      self->priv->paging_event_no = 1;
 
701
      self->priv->paging_source_id = g_timeout_add (
 
702
        PAGING_INITIAL_REPEAT_TIMEOUT,
 
703
        (GSourceFunc) trough_paging_cb,
 
704
        self);
 
705
    }
 
706
  else if (self->priv->paging_event_no == 1)
 
707
    {
 
708
      /* Scroll on after subsequent timeout. */
 
709
      ret = FALSE;
 
710
      mode = CLUTTER_EASE_IN_CUBIC;
 
711
      self->priv->paging_event_no = 2;
 
712
      self->priv->paging_source_id = g_timeout_add (
 
713
        PAGING_SUBSEQUENT_REPEAT_TIMEOUT,
 
714
        (GSourceFunc) trough_paging_cb,
 
715
        self);
 
716
    }
 
717
  else
 
718
    {
 
719
      /* Keep scrolling. */
 
720
      ret = TRUE;
 
721
      mode = CLUTTER_LINEAR;
 
722
      self->priv->paging_event_no++;
 
723
    }
 
724
 
 
725
  /* Do the scrolling */
 
726
  st_adjustment_get_values (self->priv->adjustment,
 
727
                            &value, NULL, NULL,
 
728
                            NULL, &page_increment, NULL);
 
729
 
 
730
  if (self->priv->vertical)
 
731
    handle_pos = clutter_actor_get_y (self->priv->handle);
 
732
  else
 
733
    handle_pos = clutter_actor_get_x (self->priv->handle);
 
734
 
 
735
  clutter_actor_transform_stage_point (CLUTTER_ACTOR (self->priv->trough),
 
736
                                       self->priv->move_x,
 
737
                                       self->priv->move_y,
 
738
                                       &tx, &ty);
 
739
 
 
740
  if (self->priv->vertical)
 
741
    event_pos = ty;
 
742
  else
 
743
    event_pos = tx;
 
744
 
 
745
  if (event_pos > handle_pos)
 
746
    {
 
747
      if (self->priv->paging_direction == NONE)
 
748
        {
 
749
          /* Remember direction. */
 
750
          self->priv->paging_direction = DOWN;
 
751
        }
 
752
      if (self->priv->paging_direction == UP)
 
753
        {
 
754
          /* Scrolled far enough. */
 
755
          return FALSE;
 
756
        }
 
757
      value += page_increment;
 
758
    }
 
759
  else
 
760
    {
 
761
      if (self->priv->paging_direction == NONE)
 
762
        {
 
763
          /* Remember direction. */
 
764
          self->priv->paging_direction = UP;
 
765
        }
 
766
      if (self->priv->paging_direction == DOWN)
 
767
        {
 
768
          /* Scrolled far enough. */
 
769
          return FALSE;
 
770
        }
 
771
      value -= page_increment;
 
772
    }
 
773
 
 
774
  if (self->priv->paging_animation)
 
775
    {
 
776
      clutter_animation_completed (self->priv->paging_animation);
 
777
    }
 
778
 
 
779
  /* FIXME: Creating a new animation for each scroll is probably not the best
 
780
  * idea, but it's a lot less involved than extenind the current animation */
 
781
  a = self->priv->paging_animation = g_object_new (CLUTTER_TYPE_ANIMATION,
 
782
                                                   "object", self->priv->adjustment,
 
783
                                                   "duration", PAGING_SUBSEQUENT_REPEAT_TIMEOUT,
 
784
                                                   "mode", mode,
 
785
                                                   NULL);
 
786
  g_value_init (&v, G_TYPE_DOUBLE);
 
787
  g_value_set_double (&v, value);
 
788
  clutter_animation_bind (self->priv->paging_animation, "value", &v);
 
789
  t = clutter_animation_get_timeline (self->priv->paging_animation);
 
790
  g_signal_connect (a, "completed", G_CALLBACK (animation_completed_cb),
 
791
                    self->priv);
 
792
  clutter_timeline_start (t);
 
793
 
 
794
  return ret;
 
795
}
 
796
 
 
797
static gboolean
 
798
trough_button_press_event_cb (ClutterActor       *actor,
 
799
                              ClutterButtonEvent *event,
 
800
                              StScrollBar        *self)
 
801
{
 
802
  g_return_val_if_fail (self, FALSE);
 
803
 
 
804
  if (event->button != 1)
 
805
    return FALSE;
 
806
 
 
807
  if (self->priv->adjustment == NULL)
 
808
    return FALSE;
 
809
 
 
810
  self->priv->move_x = event->x;
 
811
  self->priv->move_y = event->y;
 
812
  self->priv->paging_direction = NONE;
 
813
  self->priv->paging_event_no = 0;
 
814
  trough_paging_cb (self);
 
815
 
 
816
  return TRUE;
 
817
}
 
818
 
 
819
static gboolean
 
820
trough_button_release_event_cb (ClutterActor       *actor,
 
821
                                ClutterButtonEvent *event,
 
822
                                StScrollBar        *self)
 
823
{
 
824
  if (event->button != 1)
 
825
    return FALSE;
 
826
 
 
827
  if (self->priv->paging_source_id)
 
828
    {
 
829
      g_source_remove (self->priv->paging_source_id);
 
830
      self->priv->paging_source_id = 0;
 
831
    }
 
832
 
 
833
  return TRUE;
 
834
}
 
835
 
 
836
static gboolean
 
837
trough_leave_event_cb (ClutterActor *actor,
 
838
                       ClutterEvent *event,
 
839
                       StScrollBar  *self)
 
840
{
 
841
  if (self->priv->paging_source_id)
 
842
    {
 
843
      g_source_remove (self->priv->paging_source_id);
 
844
      self->priv->paging_source_id = 0;
 
845
      return TRUE;
 
846
    }
 
847
 
 
848
  return FALSE;
 
849
}
 
850
 
 
851
static void
 
852
stepper_animation_completed_cb (ClutterAnimation *a,
 
853
                                gpointer          data)
 
854
{
 
855
  g_object_unref (a);
 
856
}
 
857
 
 
858
static void
 
859
stepper_move_on (StScrollBarPrivate *priv,
 
860
                 gint                mode)
 
861
{
 
862
  ClutterAnimation *a;
 
863
  ClutterTimeline *t;
 
864
  GValue v = { 0, };
 
865
  double value, inc;
 
866
 
 
867
  a = g_object_new (CLUTTER_TYPE_ANIMATION,
 
868
                    "object", priv->adjustment,
 
869
                    "duration", PAGING_SUBSEQUENT_REPEAT_TIMEOUT,
 
870
                    "mode", mode,
 
871
                    NULL);
 
872
 
 
873
  g_signal_connect (a, "completed", G_CALLBACK (stepper_animation_completed_cb),
 
874
                    NULL);
 
875
 
 
876
  g_object_get (priv->adjustment,
 
877
                "step-increment", &inc,
 
878
                "value", &value,
 
879
                NULL);
 
880
 
 
881
  if (priv->stepper_forward)
 
882
    value = value + inc;
 
883
  else
 
884
    value = value - inc;
 
885
 
 
886
  g_value_init (&v, G_TYPE_DOUBLE);
 
887
  g_value_set_double (&v, value);
 
888
  clutter_animation_bind (a, "value", &v);
 
889
 
 
890
  t = clutter_animation_get_timeline (a);
 
891
  clutter_timeline_start (t);
 
892
}
 
893
 
 
894
static gboolean
 
895
stepper_button_subsequent_timeout (StScrollBarPrivate *priv)
 
896
{
 
897
  stepper_move_on (priv, CLUTTER_LINEAR);
 
898
 
 
899
  return TRUE;
 
900
}
 
901
 
 
902
static gboolean
 
903
stepper_button_repeat_timeout (StScrollBarPrivate *priv)
 
904
{
 
905
  priv->stepper_source_id = 0;
 
906
 
 
907
  stepper_move_on (priv, CLUTTER_EASE_IN_CUBIC);
 
908
 
 
909
  priv->stepper_source_id = g_timeout_add (PAGING_SUBSEQUENT_REPEAT_TIMEOUT,
 
910
                                           (GSourceFunc)
 
911
                                           stepper_button_subsequent_timeout,
 
912
                                           priv);
 
913
  return FALSE;
 
914
}
 
915
 
 
916
static gboolean
 
917
stepper_button_press_event_cb (ClutterActor       *actor,
 
918
                               ClutterButtonEvent *event,
 
919
                               StScrollBar        *bar)
 
920
{
 
921
  StScrollBarPrivate *priv = bar->priv;
 
922
 
 
923
  if (event->button != 1)
 
924
    return FALSE;
 
925
 
 
926
  if (bar->priv->adjustment == NULL)
 
927
    return FALSE;
 
928
 
 
929
  bar->priv->stepper_forward = (actor == priv->fw_stepper);
 
930
 
 
931
  stepper_move_on (priv, CLUTTER_EASE_OUT_CUBIC);
 
932
 
 
933
  priv->stepper_source_id = g_timeout_add (PAGING_INITIAL_REPEAT_TIMEOUT,
 
934
                                           (GSourceFunc)
 
935
                                           stepper_button_repeat_timeout,
 
936
                                           priv);
 
937
 
 
938
  return TRUE;
 
939
}
 
940
 
 
941
static gboolean
 
942
stepper_button_release_cb (ClutterActor       *actor,
 
943
                           ClutterButtonEvent *event,
 
944
                           StScrollBar        *self)
 
945
{
 
946
  if (event->button != 1)
 
947
    return FALSE;
 
948
 
 
949
  g_source_remove (self->priv->stepper_source_id);
 
950
 
 
951
  return FALSE;
 
952
}
 
953
 
 
954
static void
 
955
st_scroll_bar_notify_reactive (StScrollBar *self)
 
956
{
 
957
  StScrollBarPrivate *priv = self->priv;
 
958
 
 
959
  gboolean reactive = CLUTTER_ACTOR_IS_REACTIVE (self);
 
960
 
 
961
  clutter_actor_set_reactive (CLUTTER_ACTOR (priv->bw_stepper), reactive);
 
962
  clutter_actor_set_reactive (CLUTTER_ACTOR (priv->fw_stepper), reactive);
 
963
  clutter_actor_set_reactive (CLUTTER_ACTOR (priv->trough), reactive);
 
964
  clutter_actor_set_reactive (CLUTTER_ACTOR (priv->handle), reactive);
 
965
}
 
966
 
 
967
static void
 
968
st_scroll_bar_init (StScrollBar *self)
 
969
{
 
970
  self->priv = ST_SCROLL_BAR_GET_PRIVATE (self);
 
971
 
 
972
  self->priv->bw_stepper = (ClutterActor *) st_button_new ();
 
973
  clutter_actor_set_name (CLUTTER_ACTOR (self->priv->bw_stepper),
 
974
                          "backward-stepper");
 
975
  clutter_actor_set_parent (CLUTTER_ACTOR (self->priv->bw_stepper),
 
976
                            CLUTTER_ACTOR (self));
 
977
  g_signal_connect (self->priv->bw_stepper, "button-press-event",
 
978
                    G_CALLBACK (stepper_button_press_event_cb), self);
 
979
  g_signal_connect (self->priv->bw_stepper, "button-release-event",
 
980
                    G_CALLBACK (stepper_button_release_cb), self);
 
981
 
 
982
  self->priv->fw_stepper = (ClutterActor *) st_button_new ();
 
983
  clutter_actor_set_name (CLUTTER_ACTOR (self->priv->fw_stepper),
 
984
                          "forward-stepper");
 
985
  clutter_actor_set_parent (CLUTTER_ACTOR (self->priv->fw_stepper),
 
986
                            CLUTTER_ACTOR (self));
 
987
  g_signal_connect (self->priv->fw_stepper, "button-press-event",
 
988
                    G_CALLBACK (stepper_button_press_event_cb), self);
 
989
  g_signal_connect (self->priv->fw_stepper, "button-release-event",
 
990
                    G_CALLBACK (stepper_button_release_cb), self);
 
991
 
 
992
  self->priv->trough = (ClutterActor *) st_bin_new ();
 
993
  clutter_actor_set_reactive ((ClutterActor *) self->priv->trough, TRUE);
 
994
  clutter_actor_set_name (CLUTTER_ACTOR (self->priv->trough), "trough");
 
995
  clutter_actor_set_parent (CLUTTER_ACTOR (self->priv->trough),
 
996
                            CLUTTER_ACTOR (self));
 
997
  g_signal_connect (self->priv->trough, "button-press-event",
 
998
                    G_CALLBACK (trough_button_press_event_cb), self);
 
999
  g_signal_connect (self->priv->trough, "button-release-event",
 
1000
                    G_CALLBACK (trough_button_release_event_cb), self);
 
1001
  g_signal_connect (self->priv->trough, "leave-event",
 
1002
                    G_CALLBACK (trough_leave_event_cb), self);
 
1003
 
 
1004
  self->priv->handle = (ClutterActor *) st_button_new ();
 
1005
  clutter_actor_set_name (CLUTTER_ACTOR (self->priv->handle), "hhandle");
 
1006
  clutter_actor_set_parent (CLUTTER_ACTOR (self->priv->handle),
 
1007
                            self->priv->trough);
 
1008
  g_signal_connect (self->priv->handle, "button-press-event",
 
1009
                    G_CALLBACK (handle_button_press_event_cb), self);
 
1010
 
 
1011
  clutter_actor_set_reactive (CLUTTER_ACTOR (self), TRUE);
 
1012
 
 
1013
  g_signal_connect (self, "notify::reactive",
 
1014
                    G_CALLBACK (st_scroll_bar_notify_reactive), NULL);
 
1015
}
 
1016
 
 
1017
StWidget *
 
1018
st_scroll_bar_new (StAdjustment *adjustment)
 
1019
{
 
1020
  return g_object_new (ST_TYPE_SCROLL_BAR,
 
1021
                       "adjustment", adjustment,
 
1022
                       NULL);
 
1023
}
 
1024
 
 
1025
void
 
1026
st_scroll_bar_set_adjustment (StScrollBar  *bar,
 
1027
                              StAdjustment *adjustment)
 
1028
{
 
1029
  StScrollBarPrivate *priv;
 
1030
 
 
1031
  g_return_if_fail (ST_IS_SCROLL_BAR (bar));
 
1032
 
 
1033
  priv = bar->priv;
 
1034
  if (priv->adjustment)
 
1035
    {
 
1036
      g_signal_handlers_disconnect_by_func (priv->adjustment,
 
1037
                                            clutter_actor_queue_relayout,
 
1038
                                            bar);
 
1039
      g_signal_handlers_disconnect_by_func (priv->adjustment,
 
1040
                                            clutter_actor_queue_relayout,
 
1041
                                            bar);
 
1042
      g_object_unref (priv->adjustment);
 
1043
      priv->adjustment = NULL;
 
1044
    }
 
1045
 
 
1046
  if (adjustment)
 
1047
    {
 
1048
      priv->adjustment = g_object_ref (adjustment);
 
1049
 
 
1050
      g_signal_connect_swapped (priv->adjustment, "notify::value",
 
1051
                                G_CALLBACK (clutter_actor_queue_relayout),
 
1052
                                bar);
 
1053
      g_signal_connect_swapped (priv->adjustment, "changed",
 
1054
                                G_CALLBACK (clutter_actor_queue_relayout),
 
1055
                                bar);
 
1056
 
 
1057
      clutter_actor_queue_relayout (CLUTTER_ACTOR (bar));
 
1058
    }
 
1059
}
 
1060
 
 
1061
/**
 
1062
 * st_scroll_bar_get_adjustment:
 
1063
 * @bar: a #StScrollbar
 
1064
 *
 
1065
 * Gets the adjustment object that stores the current position
 
1066
 * of the scrollbar.
 
1067
 *
 
1068
 * Return value: (transfer none): the adjustment
 
1069
 */
 
1070
StAdjustment *
 
1071
st_scroll_bar_get_adjustment (StScrollBar *bar)
 
1072
{
 
1073
  g_return_val_if_fail (ST_IS_SCROLL_BAR (bar), NULL);
 
1074
 
 
1075
  return bar->priv->adjustment;
 
1076
}
 
1077