~ubuntu-branches/ubuntu/natty/libchamplain/natty

« back to all changes in this revision

Viewing changes to tidy/tidy-finger-scroll.c

  • Committer: Bazaar Package Importer
  • Author(s): Sjoerd Simons, Laurent Bigonville, Sjoerd Simons
  • Date: 2009-09-15 00:01:41 UTC
  • mfrom: (1.1.3 upstream) (2.1.3 sid)
  • Revision ID: james.westby@ubuntu.com-20090915000141-i8fg5n1t02zxo79m
Tags: 0.4.0-1
[ Laurent Bigonville ]
* debian/control: Add libchamplain-0.3-dev dependency to
  libchamplain-gtk-0.3-dev

[ Sjoerd Simons ]
* New upstream release (0.4.0)

Show diffs side-by-side

added added

removed removed

Lines of Context:
36
36
 
37
37
typedef struct {
38
38
  /* Units to store the origin of a click when scrolling */
39
 
  ClutterUnit x;
40
 
  ClutterUnit y;
 
39
  gfloat x;
 
40
  gfloat y;
41
41
  GTimeVal    time;
42
42
} TidyFingerScrollMotion;
43
43
 
45
45
{
46
46
  /* Scroll mode */
47
47
  TidyFingerScrollMode mode;
48
 
  
 
48
 
49
49
  GArray                *motion_buffer;
50
50
  guint                  last_motion;
51
 
  
 
51
 
52
52
  /* Variables for storing acceleration information for kinetic mode */
53
53
  ClutterTimeline       *deceleration_timeline;
54
 
  ClutterUnit            dx;
55
 
  ClutterUnit            dy;
56
 
  ClutterFixed           decel_rate;
57
 
  
 
54
  gdouble                dx;
 
55
  gdouble                dy;
 
56
  gdouble                decel_rate;
 
57
 
58
58
};
59
59
 
60
60
enum {
68
68
                                 GValue *value, GParamSpec *pspec)
69
69
{
70
70
  TidyFingerScrollPrivate *priv = TIDY_FINGER_SCROLL (object)->priv;
71
 
  
 
71
 
72
72
  switch (property_id)
73
73
    {
74
74
    case PROP_MODE :
75
75
      g_value_set_enum (value, priv->mode);
76
76
      break;
77
77
    case PROP_DECEL_RATE :
78
 
      g_value_set_double (value, CLUTTER_FIXED_TO_FLOAT (priv->decel_rate));
 
78
      g_value_set_double (value, priv->decel_rate);
79
79
      break;
80
80
    case PROP_BUFFER :
81
81
      g_value_set_uint (value, priv->motion_buffer->len);
90
90
                                 const GValue *value, GParamSpec *pspec)
91
91
{
92
92
  TidyFingerScrollPrivate *priv = TIDY_FINGER_SCROLL (object)->priv;
93
 
  
 
93
 
94
94
  switch (property_id)
95
95
    {
96
96
    case PROP_MODE :
98
98
      g_object_notify (object, "mode");
99
99
      break;
100
100
    case PROP_DECEL_RATE :
101
 
      priv->decel_rate = CLUTTER_FLOAT_TO_FIXED (g_value_get_double (value));
 
101
      priv->decel_rate = g_value_get_double (value);
102
102
      g_object_notify (object, "decel-rate");
103
103
      break;
104
104
    case PROP_BUFFER :
121
121
      g_object_unref (priv->deceleration_timeline);
122
122
      priv->deceleration_timeline = NULL;
123
123
    }
124
 
  
 
124
 
125
125
  G_OBJECT_CLASS (tidy_finger_scroll_parent_class)->dispose (object);
126
126
}
127
127
 
131
131
  TidyFingerScrollPrivate *priv = TIDY_FINGER_SCROLL (object)->priv;
132
132
 
133
133
  g_array_free (priv->motion_buffer, TRUE);
134
 
  
 
134
 
135
135
  G_OBJECT_CLASS (tidy_finger_scroll_parent_class)->finalize (object);
136
136
}
137
137
 
164
164
                                                        "Rate at which the view "
165
165
                                                        "will decelerate in "
166
166
                                                        "kinetic mode.",
167
 
                                                        CLUTTER_FIXED_TO_FLOAT (CFX_ONE + CFX_MIN),
168
 
                                                        CLUTTER_FIXED_TO_FLOAT (CFX_MAX),
 
167
                                                        G_MINFLOAT + 1,
 
168
                                                        G_MAXFLOAT,
169
169
                                                        1.1,
170
170
                                                        G_PARAM_READWRITE));
171
171
 
184
184
                 ClutterMotionEvent *event,
185
185
                 TidyFingerScroll *scroll)
186
186
{
187
 
  ClutterUnit x, y;
188
 
  
 
187
  gfloat x, y;
 
188
 
189
189
  TidyFingerScrollPrivate *priv = scroll->priv;
190
190
 
191
191
  if (clutter_actor_transform_stage_point (actor,
192
 
                                           CLUTTER_UNITS_FROM_DEVICE(event->x),
193
 
                                           CLUTTER_UNITS_FROM_DEVICE(event->y),
 
192
                                           event->x,
 
193
                                           event->y,
194
194
                                           &x, &y))
195
195
    {
196
196
      TidyFingerScrollMotion *motion;
197
197
      ClutterActor *child =
198
198
        tidy_scroll_view_get_child (TIDY_SCROLL_VIEW(scroll));
199
 
      
 
199
 
200
200
      if (child)
201
201
        {
202
 
          ClutterFixed dx, dy;
 
202
          gdouble dx, dy;
203
203
          TidyAdjustment *hadjust, *vadjust;
204
 
          
 
204
 
205
205
          tidy_scrollable_get_adjustments (TIDY_SCROLLABLE (child),
206
206
                                           &hadjust,
207
207
                                           &vadjust);
208
208
 
209
209
          motion = &g_array_index (priv->motion_buffer,
210
210
                                   TidyFingerScrollMotion, priv->last_motion);
211
 
          dx = CLUTTER_UNITS_TO_FIXED(motion->x - x) +
212
 
               tidy_adjustment_get_valuex (hadjust);
213
 
          dy = CLUTTER_UNITS_TO_FIXED(motion->y - y) +
214
 
               tidy_adjustment_get_valuex (vadjust);
215
 
          
216
 
          tidy_adjustment_set_valuex (hadjust, dx);
217
 
          tidy_adjustment_set_valuex (vadjust, dy);
 
211
          dx = (motion->x - x) +
 
212
               tidy_adjustment_get_value (hadjust);
 
213
          dy = (motion->y - y) +
 
214
               tidy_adjustment_get_value (vadjust);
 
215
 
 
216
          tidy_adjustment_set_value (hadjust, dx);
 
217
          tidy_adjustment_set_value (vadjust, dy);
218
218
        }
219
 
      
 
219
 
220
220
      priv->last_motion ++;
221
221
      if (priv->last_motion == priv->motion_buffer->len)
222
222
        {
224
224
          g_array_set_size (priv->motion_buffer, priv->last_motion);
225
225
          priv->last_motion --;
226
226
        }
227
 
      
 
227
 
228
228
      motion = &g_array_index (priv->motion_buffer,
229
229
                               TidyFingerScrollMotion, priv->last_motion);
230
230
      motion->x = x;
239
239
clamp_adjustments (TidyFingerScroll *scroll)
240
240
{
241
241
  ClutterActor *child = tidy_scroll_view_get_child (TIDY_SCROLL_VIEW (scroll));
242
 
  
 
242
 
243
243
  if (child)
244
244
    {
245
245
      guint fps, n_frames;
246
246
      TidyAdjustment *hadj, *vadj;
247
247
      gboolean snap;
248
 
      
 
248
 
249
249
      tidy_scrollable_get_adjustments (TIDY_SCROLLABLE (child),
250
250
                                       &hadj, &vadj);
251
 
      
 
251
 
252
252
      /* FIXME: Hard-coded value here */
253
253
      fps = clutter_get_default_frame_rate ();
254
254
      n_frames = fps / 6;
255
 
      
 
255
 
256
256
      snap = TRUE;
257
257
      if (tidy_adjustment_get_elastic (hadj))
258
258
        snap = !tidy_adjustment_clamp (hadj, TRUE, n_frames, fps);
259
 
      
 
259
 
260
260
      /* Snap to the nearest step increment on hadjustment */
261
261
      if (snap)
262
262
        {
263
263
          gdouble d, value, lower, step_increment;
264
 
          
 
264
 
265
265
          tidy_adjustment_get_values (hadj, &value, &lower, NULL,
266
266
                                      &step_increment, NULL, NULL);
267
267
          d = (rint ((value - lower) / step_increment) *
268
268
              step_increment) + lower;
269
269
          tidy_adjustment_set_value (hadj, d);
270
270
        }
271
 
      
 
271
 
272
272
      snap = TRUE;
273
273
      if (tidy_adjustment_get_elastic (vadj))
274
274
        snap = !tidy_adjustment_clamp (vadj, TRUE, n_frames, fps);
277
277
      if (snap)
278
278
        {
279
279
          gdouble d, value, lower, step_increment;
280
 
          
 
280
 
281
281
          tidy_adjustment_get_values (vadj, &value, &lower, NULL,
282
282
                                      &step_increment, NULL, NULL);
283
283
          d = (rint ((value - lower) / step_increment) *
303
303
{
304
304
  TidyFingerScrollPrivate *priv = scroll->priv;
305
305
  ClutterActor *child = tidy_scroll_view_get_child (TIDY_SCROLL_VIEW(scroll));
306
 
  
 
306
 
307
307
  if (child)
308
308
    {
309
 
      ClutterFixed value, lower, upper, page_size;
 
309
      gdouble    value, lower, upper, page_size;
310
310
      TidyAdjustment *hadjust, *vadjust;
311
311
      gint i;
312
312
      gboolean stop = TRUE;
313
 
      
 
313
 
314
314
      tidy_scrollable_get_adjustments (TIDY_SCROLLABLE (child),
315
315
                                       &hadjust,
316
316
                                       &vadjust);
317
 
      
318
 
      for (i = 0; i < clutter_timeline_get_delta (timeline, NULL); i++)
 
317
 
 
318
      for (i = 0; i < clutter_timeline_get_delta (timeline) / 15; i++)
319
319
        {
320
 
          tidy_adjustment_set_valuex (hadjust,
 
320
          tidy_adjustment_set_value (hadjust,
321
321
                                      priv->dx +
322
 
                                        tidy_adjustment_get_valuex (hadjust));
323
 
          tidy_adjustment_set_valuex (vadjust,
 
322
                                        tidy_adjustment_get_value (hadjust));
 
323
          tidy_adjustment_set_value (vadjust,
324
324
                                      priv->dy +
325
 
                                        tidy_adjustment_get_valuex (vadjust));
326
 
          priv->dx = clutter_qdivx (priv->dx, priv->decel_rate);
327
 
          priv->dy = clutter_qdivx (priv->dy, priv->decel_rate);
 
325
                                        tidy_adjustment_get_value (vadjust));
 
326
          priv->dx = (priv->dx / priv->decel_rate);
 
327
          priv->dy = (priv->dy / priv->decel_rate);
328
328
        }
329
 
      
 
329
 
330
330
      /* Check if we've hit the upper or lower bounds and stop the timeline */
331
 
      tidy_adjustment_get_valuesx (hadjust, &value, &lower, &upper,
 
331
      tidy_adjustment_get_values (hadjust, &value, &lower, &upper,
332
332
                                   NULL, NULL, &page_size);
333
333
      if (((priv->dx > 0) && (value < upper - page_size)) ||
334
334
          ((priv->dx < 0) && (value > lower)))
335
335
        stop = FALSE;
336
 
      
 
336
 
337
337
      if (stop)
338
338
        {
339
 
          tidy_adjustment_get_valuesx (vadjust, &value, &lower, &upper,
 
339
          tidy_adjustment_get_values (vadjust, &value, &lower, &upper,
340
340
                                       NULL, NULL, &page_size);
341
341
          if (((priv->dy > 0) && (value < upper - page_size)) ||
342
342
              ((priv->dy < 0) && (value > lower)))
343
343
            stop = FALSE;
344
344
        }
345
 
      
 
345
 
346
346
      if (stop)
347
347
        {
348
348
          clutter_timeline_stop (timeline);
363
363
 
364
364
  if (event->button != 1)
365
365
    return FALSE;
366
 
  
 
366
 
367
367
  g_signal_handlers_disconnect_by_func (actor,
368
368
                                        motion_event_cb,
369
369
                                        scroll);
370
370
  g_signal_handlers_disconnect_by_func (actor,
371
371
                                        button_release_event_cb,
372
372
                                        scroll);
373
 
  
 
373
 
374
374
  clutter_ungrab_pointer ();
375
375
 
376
376
  if ((priv->mode == TIDY_FINGER_SCROLL_MODE_KINETIC) && (child))
377
377
    {
378
 
      ClutterUnit x, y;
379
 
      
 
378
      gfloat x, y;
 
379
 
380
380
      if (clutter_actor_transform_stage_point (actor,
381
 
                                               CLUTTER_UNITS_FROM_DEVICE(event->x),
382
 
                                               CLUTTER_UNITS_FROM_DEVICE(event->y),
 
381
                                               event->x,
 
382
                                               event->y,
383
383
                                               &x, &y))
384
384
        {
385
 
          ClutterUnit frac, x_origin, y_origin;
 
385
          double frac, x_origin, y_origin;
386
386
          GTimeVal release_time, motion_time;
387
387
          TidyAdjustment *hadjust, *vadjust;
388
388
          glong time_diff;
389
389
          gint i;
390
 
          
 
390
 
391
391
          /* Get time delta */
392
392
          g_get_current_time (&release_time);
393
 
          
 
393
 
394
394
          /* Get average position/time of last x mouse events */
395
395
          priv->last_motion ++;
396
396
          x_origin = y_origin = 0;
399
399
            {
400
400
              TidyFingerScrollMotion *motion =
401
401
                &g_array_index (priv->motion_buffer, TidyFingerScrollMotion, i);
402
 
              
 
402
 
403
403
              /* FIXME: This doesn't guard against overflows - Should
404
404
               *        either fix that, or calculate the correct maximum
405
405
               *        value for the buffer size
406
406
               */
 
407
 
407
408
              x_origin += motion->x;
408
409
              y_origin += motion->y;
409
410
              motion_time.tv_sec += motion->time.tv_sec;
410
411
              motion_time.tv_usec += motion->time.tv_usec;
411
412
            }
412
 
          x_origin = CLUTTER_UNITS_FROM_FIXED (
413
 
            clutter_qdivx (CLUTTER_UNITS_TO_FIXED (x_origin),
414
 
                           CLUTTER_INT_TO_FIXED (priv->last_motion)));
415
 
          y_origin = CLUTTER_UNITS_FROM_FIXED (
416
 
            clutter_qdivx (CLUTTER_UNITS_TO_FIXED (y_origin),
417
 
                           CLUTTER_INT_TO_FIXED (priv->last_motion)));
 
413
          x_origin /= priv->last_motion;
 
414
          y_origin /= priv->last_motion;
418
415
          motion_time.tv_sec /= priv->last_motion;
419
416
          motion_time.tv_usec /= priv->last_motion;
420
 
          
 
417
 
421
418
          if (motion_time.tv_sec == release_time.tv_sec)
422
419
            time_diff = release_time.tv_usec - motion_time.tv_usec;
423
420
          else
424
421
            time_diff = release_time.tv_usec +
425
422
                        (G_USEC_PER_SEC - motion_time.tv_usec);
426
 
          
427
 
          /* Work out the fraction of 1/60th of a second that has elapsed */
428
 
          frac = clutter_qdivx (CLUTTER_FLOAT_TO_FIXED (time_diff/1000.0),
429
 
                                CLUTTER_FLOAT_TO_FIXED (1000.0/60.0));
430
 
          
431
 
          /* See how many units to move in 1/60th of a second */
432
 
          priv->dx = CLUTTER_UNITS_FROM_FIXED(clutter_qdivx (
433
 
                     CLUTTER_UNITS_TO_FIXED(x_origin - x), frac));
434
 
          priv->dy = CLUTTER_UNITS_FROM_FIXED(clutter_qdivx (
435
 
                     CLUTTER_UNITS_TO_FIXED(y_origin - y), frac));
436
 
          
437
 
          /* Get adjustments to do step-increment snapping */
438
 
          tidy_scrollable_get_adjustments (TIDY_SCROLLABLE (child),
439
 
                                           &hadjust,
440
 
                                           &vadjust);
441
 
 
442
 
          if (ABS(CLUTTER_UNITS_TO_INT(priv->dx)) > 1 ||
443
 
              ABS(CLUTTER_UNITS_TO_INT(priv->dy)) > 1)
444
 
            {
445
 
              gdouble value, lower, step_increment, d, a, x, y, n;
446
 
              
447
 
              /* TODO: Convert this all to fixed point? */
448
 
              
449
 
              /* We want n, where x / y^n < z,
450
 
               * x = Distance to move per frame
451
 
               * y = Deceleration rate
452
 
               * z = maximum distance from target
453
 
               *
454
 
               * Rearrange to n = log (x / z) / log (y)
455
 
               * To simplify, z = 1, so n = log (x) / log (y)
456
 
               *
457
 
               * As z = 1, this will cause stops to be slightly abrupt - 
458
 
               * add a constant 15 frames to compensate.
459
 
               */
460
 
              x = CLUTTER_FIXED_TO_FLOAT (MAX(ABS(priv->dx), ABS(priv->dy)));
461
 
              y = CLUTTER_FIXED_TO_FLOAT (priv->decel_rate);
462
 
              n = logf (x) / logf (y) + 15.0;
463
 
 
464
 
              /* Now we have n, adjust dx/dy so that we finish on a step
465
 
               * boundary.
466
 
               *
467
 
               * Distance moved, using the above variable names:
468
 
               *
469
 
               * d = x + x/y + x/y^2 + ... + x/y^n
470
 
               *
471
 
               * Using geometric series,
472
 
               *
473
 
               * d = (1 - 1/y^(n+1))/(1 - 1/y)*x
474
 
               * 
475
 
               * Let a = (1 - 1/y^(n+1))/(1 - 1/y),
476
 
               *
477
 
               * d = a * x
478
 
               *
479
 
               * Find d and find its nearest page boundary, then solve for x
480
 
               *
481
 
               * x = d / a
482
 
               */
483
 
              
484
 
              /* Get adjustments, work out y^n */
485
 
              a = (1.0 - 1.0 / pow (y, n + 1)) / (1.0 - 1.0 / y);
486
 
 
487
 
              /* Solving for dx */
488
 
              d = a * CLUTTER_UNITS_TO_FLOAT (priv->dx);
489
 
              tidy_adjustment_get_values (hadjust, &value, &lower, NULL,
490
 
                                          &step_increment, NULL, NULL);
491
 
              d = ((rint (((value + d) - lower) / step_increment) *
492
 
                    step_increment) + lower) - value;
493
 
              priv->dx = CLUTTER_UNITS_FROM_FLOAT (d / a);
494
 
 
495
 
              /* Solving for dy */
496
 
              d = a * CLUTTER_UNITS_TO_FLOAT (priv->dy);
497
 
              tidy_adjustment_get_values (vadjust, &value, &lower, NULL,
498
 
                                          &step_increment, NULL, NULL);
499
 
              d = ((rint (((value + d) - lower) / step_increment) *
500
 
                    step_increment) + lower) - value;
501
 
              priv->dy = CLUTTER_UNITS_FROM_FLOAT (d / a);
502
 
              
503
 
              priv->deceleration_timeline = clutter_timeline_new ((gint)n, 60);
504
 
            }
505
 
          else
506
 
            {
507
 
              gdouble value, lower, step_increment, d, a, y;
508
 
              
509
 
              /* Start a short effects timeline to snap to the nearest step 
510
 
               * boundary (see equations above)
511
 
               */
512
 
              y = CLUTTER_FIXED_TO_FLOAT (priv->decel_rate);
513
 
              a = (1.0 - 1.0 / pow (y, 4 + 1)) / (1.0 - 1.0 / y);
514
 
              
515
 
              tidy_adjustment_get_values (hadjust, &value, &lower, NULL,
516
 
                                          &step_increment, NULL, NULL);
517
 
              d = ((rint ((value - lower) / step_increment) *
518
 
                    step_increment) + lower) - value;
519
 
              priv->dx = CLUTTER_UNITS_FROM_FLOAT (d / a);
520
 
              
521
 
              tidy_adjustment_get_values (vadjust, &value, &lower, NULL,
522
 
                                          &step_increment, NULL, NULL);
523
 
              d = ((rint ((value - lower) / step_increment) *
524
 
                    step_increment) + lower) - value;
525
 
              priv->dy = CLUTTER_UNITS_FROM_FLOAT (d / a);
526
 
              
527
 
              priv->deceleration_timeline = clutter_timeline_new (4, 60);
528
 
            }
529
 
 
530
 
          g_signal_connect (priv->deceleration_timeline, "new_frame",
531
 
                            G_CALLBACK (deceleration_new_frame_cb), scroll);
532
 
          g_signal_connect (priv->deceleration_timeline, "completed",
533
 
                            G_CALLBACK (deceleration_completed_cb), scroll);
534
 
          clutter_timeline_start (priv->deceleration_timeline);
535
 
          decelerating = TRUE;
 
423
 
 
424
          /* On a macbook that's running Ubuntu 9.04 sometimes 'time_diff' is 0
 
425
             and this causes a division by 0 when computing 'frac'. This check
 
426
             avoids this error.
 
427
           */
 
428
          if (time_diff != 0)
 
429
            {
 
430
              /* Work out the fraction of 1/60th of a second that has elapsed */
 
431
              frac = (time_diff/1000.0) / (1000.0/60.0);
 
432
 
 
433
              /* See how many units to move in 1/60th of a second */
 
434
              priv->dx = (x_origin - x) / frac;
 
435
              priv->dy = (y_origin - y) / frac;
 
436
 
 
437
              /* Get adjustments to do step-increment snapping */
 
438
              tidy_scrollable_get_adjustments (TIDY_SCROLLABLE (child),
 
439
                                               &hadjust,
 
440
                                               &vadjust);
 
441
 
 
442
              if (ABS(priv->dx) > 1 ||
 
443
                  ABS(priv->dy) > 1)
 
444
                {
 
445
                  gdouble value, lower, step_increment, d, a, x, y, n;
 
446
 
 
447
                  /* TODO: Convert this all to fixed point? */
 
448
 
 
449
                  /* We want n, where x / y    n < z,
 
450
                   * x = Distance to move per frame
 
451
                   * y = Deceleration rate
 
452
                   * z = maximum distance from target
 
453
                   *
 
454
                   * Rearrange to n = log (x / z) / log (y)
 
455
                   * To simplify, z = 1, so n = log (x) / log (y)
 
456
                   *
 
457
                   * As z = 1, this will cause stops to be slightly abrupt -
 
458
                   * add a constant 15 frames to compensate.
 
459
                   */
 
460
                  x = MAX(ABS(priv->dx), ABS(priv->dy));
 
461
                  y = priv->decel_rate;
 
462
                  n = logf (x) / logf (y) + 15.0;
 
463
 
 
464
                  /* Now we have n, adjust dx/dy so that we finish on a step
 
465
                   * boundary.
 
466
                   *
 
467
                   * Distance moved, using the above variable names:
 
468
                   *
 
469
                   * d = x + x/y + x/y    2 + ... + x/y    n
 
470
                   *
 
471
                   * Using geometric series,
 
472
                   *
 
473
                   * d = (1 - 1/y    (n+1))/(1 - 1/y)*x
 
474
                   *
 
475
                   * Let a = (1 - 1/y    (n+1))/(1 - 1/y),
 
476
                   *
 
477
                   * d = a * x
 
478
                   *
 
479
                   * Find d and find its nearest page boundary, then solve for x
 
480
                   *
 
481
                   * x = d / a
 
482
                   */
 
483
 
 
484
                  /* Get adjustments, work out y    n */
 
485
                  a = (1.0 - 1.0 / pow (y, n + 1)) / (1.0 - 1.0 / y);
 
486
 
 
487
                  /* Solving for dx */
 
488
                  d = a * priv->dx;
 
489
                  tidy_adjustment_get_values (hadjust, &value, &lower, NULL,
 
490
                                              &step_increment, NULL, NULL);
 
491
                  d = ((rint (((value + d) - lower) / step_increment) *
 
492
                        step_increment) + lower) - value;
 
493
                  priv->dx = (d / a);
 
494
 
 
495
                  /* Solving for dy */
 
496
                  d = a * (priv->dy);
 
497
                  tidy_adjustment_get_values (vadjust, &value, &lower, NULL,
 
498
                                              &step_increment, NULL, NULL);
 
499
                  d = ((rint (((value + d) - lower) / step_increment) *
 
500
                        step_increment) + lower) - value;
 
501
                  priv->dy = (d / a);
 
502
 
 
503
                  priv->deceleration_timeline = clutter_timeline_new ((n / 60) * 1000.0);
 
504
                }
 
505
              else
 
506
                {
 
507
                  gdouble value, lower, step_increment, d, a, y;
 
508
 
 
509
                  /* Start a short effects timeline to snap to the nearest step
 
510
                   * boundary (see equations above)
 
511
                   */
 
512
                  y = priv->decel_rate;
 
513
                  a = (1.0 - 1.0 / pow (y, 4 + 1)) / (1.0 - 1.0 / y);
 
514
 
 
515
                  tidy_adjustment_get_values (hadjust, &value, &lower, NULL,
 
516
                                              &step_increment, NULL, NULL);
 
517
                  d = ((rint ((value - lower) / step_increment) *
 
518
                        step_increment) + lower) - value;
 
519
                  priv->dx = (d / a);
 
520
 
 
521
                  tidy_adjustment_get_values (vadjust, &value, &lower, NULL,
 
522
                                              &step_increment, NULL, NULL);
 
523
                  d = ((rint ((value - lower) / step_increment) *
 
524
                        step_increment) + lower) - value;
 
525
                  priv->dy = (d / a);
 
526
 
 
527
                  priv->deceleration_timeline = clutter_timeline_new (250);
 
528
                }
 
529
 
 
530
              g_signal_connect (priv->deceleration_timeline, "new_frame",
 
531
                                G_CALLBACK (deceleration_new_frame_cb), scroll);
 
532
              g_signal_connect (priv->deceleration_timeline, "completed",
 
533
                                G_CALLBACK (deceleration_completed_cb), scroll);
 
534
              clutter_timeline_start (priv->deceleration_timeline);
 
535
              decelerating = TRUE;
 
536
            }
536
537
        }
537
538
    }
538
539
 
540
541
  if (priv->last_motion <= 1)
541
542
      moved = FALSE;
542
543
  priv->last_motion = 0;
543
 
  
 
544
 
544
545
  if (!decelerating)
545
546
    {
546
547
      clamp_adjustments (scroll);
547
548
    }
548
 
  
 
549
 
549
550
  /* Pass through events to children.
550
551
   * FIXME: this probably breaks click-count.
551
552
   */
552
553
  if (moved == FALSE)
553
554
    clutter_event_put ((ClutterEvent *)event);
554
 
  
555
 
  return TRUE;
 
555
 
 
556
  return moved;
556
557
}
557
558
 
558
559
static gboolean
570
571
                                            button_release_event_cb,
571
572
                                            scroll);
572
573
    }
573
 
  
 
574
 
574
575
  return FALSE;
575
576
}
576
577
 
580
581
                   TidyFingerScroll *scroll)
581
582
{
582
583
  TidyFingerScrollPrivate *priv = scroll->priv;
583
 
  
 
584
 
584
585
  if (event->type == CLUTTER_BUTTON_PRESS)
585
586
    {
586
587
      TidyFingerScrollMotion *motion;
587
588
      ClutterButtonEvent *bevent = (ClutterButtonEvent *)event;
588
 
      
 
589
 
 
590
      if (bevent->source != actor)
 
591
        return FALSE;
 
592
 
589
593
      /* Reset motion buffer */
590
594
      priv->last_motion = 0;
591
595
      motion = &g_array_index (priv->motion_buffer, TidyFingerScrollMotion, 0);
592
 
      
 
596
 
593
597
      if ((bevent->button == 1) &&
594
598
          (clutter_actor_transform_stage_point (actor,
595
 
                                           CLUTTER_UNITS_FROM_DEVICE(bevent->x),
596
 
                                           CLUTTER_UNITS_FROM_DEVICE(bevent->y),
 
599
                                           bevent->x,
 
600
                                           bevent->y,
597
601
                                           &motion->x, &motion->y)))
598
602
        {
599
603
          g_get_current_time (&motion->time);
600
 
          
 
604
 
601
605
          if (priv->deceleration_timeline)
602
606
            {
603
607
              clutter_timeline_stop (priv->deceleration_timeline);
604
608
              g_object_unref (priv->deceleration_timeline);
605
609
              priv->deceleration_timeline = NULL;
606
610
            }
607
 
          
 
611
 
608
612
          clutter_grab_pointer (actor);
609
 
          
 
613
 
610
614
          /* Add a high priority idle to check the grab after the event
611
615
           * emission is finished.
612
616
           */
614
618
                           (GSourceFunc)after_event_cb,
615
619
                           scroll,
616
620
                           NULL);
617
 
          
 
621
 
618
622
          g_signal_connect (actor,
619
623
                            "motion-event",
620
624
                            G_CALLBACK (motion_event_cb),
625
629
                            scroll);
626
630
        }
627
631
    }
628
 
  
 
632
 
629
633
  return FALSE;
630
634
}
631
635
 
634
638
{
635
639
  ClutterActor *scrollbar;
636
640
  TidyFingerScrollPrivate *priv = self->priv = FINGER_SCROLL_PRIVATE (self);
637
 
  
 
641
 
638
642
  priv->motion_buffer = g_array_sized_new (FALSE, TRUE,
639
643
                                           sizeof (TidyFingerScrollMotion), 3);
640
644
  g_array_set_size (priv->motion_buffer, 3);
641
 
  priv->decel_rate = CLUTTER_FLOAT_TO_FIXED (1.1f);
642
 
  
 
645
  priv->decel_rate = 1.1f;
 
646
 
643
647
  clutter_actor_set_reactive (CLUTTER_ACTOR (self), TRUE);
644
648
  g_signal_connect (CLUTTER_ACTOR (self),
645
649
                    "captured-event",
646
650
                    G_CALLBACK (captured_event_cb),
647
651
                    self);
648
 
  
649
 
  
 
652
 
 
653
 
650
654
}
651
655
 
652
656
ClutterActor *
660
664
tidy_finger_scroll_stop (TidyFingerScroll *scroll)
661
665
{
662
666
  TidyFingerScrollPrivate *priv;
663
 
  
 
667
 
664
668
  g_return_if_fail (TIDY_IS_FINGER_SCROLL (scroll));
665
 
  
 
669
 
666
670
  priv = scroll->priv;
667
671
 
668
672
  if (priv->deceleration_timeline)