~carsten-munk/m-r/libhildon-2.1.24

« back to all changes in this revision

Viewing changes to src/hildon-pannable-area.c

  • Committer: Carsten V. Munk
  • Date: 2008-12-08 15:00:57 UTC
  • Revision ID: cvm@cs.au.dk-20081208150057-7e0a50c8zdupbzar
initial commit of 2.1.24

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * This file is a part of hildon
 
3
 *
 
4
 * Copyright (C) 2008 Nokia Corporation, all rights reserved.
 
5
 *
 
6
 * Contact: Karl Lattimer <karl.lattimer@nokia.com>
 
7
 *
 
8
 * This widget is based on MokoFingerScroll from libmokoui
 
9
 * OpenMoko Application Framework UI Library
 
10
 * Authored by Chris Lord <chris@openedhand.com>
 
11
 * Copyright (C) 2006-2007 OpenMoko Inc.
 
12
 *
 
13
 * This program is free software; you can redistribute it and/or modify
 
14
 * it under the terms of the GNU Lesser Public License as published by
 
15
 * the Free Software Foundation; version 2 of the license.
 
16
 *
 
17
 * This program is distributed in the hope that it will be useful,
 
18
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
19
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
20
 * GNU Lesser Public License for more details.
 
21
 *
 
22
 */
 
23
 
 
24
/**
 
25
 * SECTION: hildon-pannable-area
 
26
 * @short_description: A scrolling widget designed for touch screens
 
27
 * @see_also: #GtkScrolledWindow
 
28
 *
 
29
 * #HildonPannableArea implements a scrolled window designed to be used with a
 
30
 * touch screen interface. The user scrolls the child widget by activating the
 
31
 * pointing device and dragging it over the widget.
 
32
 *
 
33
 */
 
34
 
 
35
#include <math.h>
 
36
#include <cairo.h>
 
37
#include <gdk/gdkx.h>
 
38
 
 
39
#include "hildon-pannable-area.h"
 
40
#include "hildon-marshalers.h"
 
41
#include "hildon-enum-types.h"
 
42
 
 
43
#define SMOOTH_FACTOR 0.85
 
44
#define FORCE 5
 
45
#define BOUNCE_STEPS 6
 
46
#define SCROLL_BAR_MIN_SIZE 5
 
47
#define RATIO_TOLERANCE 0.000001
 
48
#define DND_THRESHOLD_INC 20
 
49
#define SCROLLBAR_FADE_DELAY 30
 
50
 
 
51
G_DEFINE_TYPE (HildonPannableArea, hildon_pannable_area, GTK_TYPE_BIN)
 
52
 
 
53
#define PANNABLE_AREA_PRIVATE(o)                                \
 
54
  (G_TYPE_INSTANCE_GET_PRIVATE ((o), HILDON_TYPE_PANNABLE_AREA, \
 
55
                                HildonPannableAreaPrivate))
 
56
 
 
57
struct _HildonPannableAreaPrivate {
 
58
  HildonPannableAreaMode mode;
 
59
  HildonMovementMode mov_mode;
 
60
  GdkWindow *event_window;
 
61
  gdouble x;            /* Used to store mouse co-ordinates of the first or */
 
62
  gdouble y;            /* previous events in a press-motion pair */
 
63
  gdouble ex;           /* Used to store mouse co-ordinates of the last */
 
64
  gdouble ey;           /* motion event in acceleration mode */
 
65
  gboolean enabled;
 
66
  gboolean clicked;
 
67
  guint32 last_time;    /* Last event time, to stop infinite loops */
 
68
  gint last_type;
 
69
  gboolean moved;
 
70
  gdouble vmin;
 
71
  gdouble vmax;
 
72
  gdouble vfast_factor;
 
73
  gdouble decel;
 
74
  gdouble scroll_time;
 
75
  gdouble vel_factor;
 
76
  guint sps;
 
77
  gdouble vel_x;
 
78
  gdouble vel_y;
 
79
  GdkWindow *child;
 
80
  gint ix;                      /* Initial click mouse co-ordinates */
 
81
  gint iy;
 
82
  gint cx;                      /* Initial click child window mouse co-ordinates */
 
83
  gint cy;
 
84
  guint idle_id;
 
85
  gdouble scroll_to_x;
 
86
  gdouble scroll_to_y;
 
87
  gint overshot_dist_x;
 
88
  gint overshot_dist_y;
 
89
  gint overshooting_y;
 
90
  gint overshooting_x;
 
91
  gdouble scroll_indicator_alpha;
 
92
  gint scroll_indicator_timeout;
 
93
  gint scroll_indicator_event_interrupt;
 
94
  gint scroll_delay_counter;
 
95
  gint vovershoot_max;
 
96
  gint hovershoot_max;
 
97
  gboolean initial_hint;
 
98
  gboolean initial_effect;
 
99
  gboolean first_drag;
 
100
 
 
101
  gboolean hscroll_visible;
 
102
  gboolean vscroll_visible;
 
103
  GdkRectangle hscroll_rect;
 
104
  GdkRectangle vscroll_rect;
 
105
  guint area_width;
 
106
 
 
107
  GtkAdjustment *hadjust;
 
108
  GtkAdjustment *vadjust;
 
109
 
 
110
  GtkPolicyType vscrollbar_policy;
 
111
  GtkPolicyType hscrollbar_policy;
 
112
};
 
113
 
 
114
/*signals*/
 
115
enum {
 
116
  HORIZONTAL_MOVEMENT,
 
117
  VERTICAL_MOVEMENT,
 
118
  LAST_SIGNAL
 
119
};
 
120
 
 
121
static guint pannable_area_signals [LAST_SIGNAL] = { 0 };
 
122
 
 
123
enum {
 
124
  PROP_ENABLED = 1,
 
125
  PROP_MODE,
 
126
  PROP_MOVEMENT_MODE,
 
127
  PROP_VELOCITY_MIN,
 
128
  PROP_VELOCITY_MAX,
 
129
  PROP_VELOCITY_FAST_FACTOR,
 
130
  PROP_DECELERATION,
 
131
  PROP_SPS,
 
132
  PROP_VSCROLLBAR_POLICY,
 
133
  PROP_HSCROLLBAR_POLICY,
 
134
  PROP_VOVERSHOOT_MAX,
 
135
  PROP_HOVERSHOOT_MAX,
 
136
  PROP_SCROLL_TIME,
 
137
  PROP_INITIAL_HINT
 
138
};
 
139
 
 
140
static void hildon_pannable_area_class_init (HildonPannableAreaClass * klass);
 
141
static void hildon_pannable_area_init (HildonPannableArea * area);
 
142
static void hildon_pannable_area_get_property (GObject * object,
 
143
                                               guint property_id,
 
144
                                               GValue * value,
 
145
                                               GParamSpec * pspec);
 
146
static void hildon_pannable_area_set_property (GObject * object,
 
147
                                               guint property_id,
 
148
                                               const GValue * value,
 
149
                                               GParamSpec * pspec);
 
150
static void hildon_pannable_area_dispose (GObject * object);
 
151
static void hildon_pannable_area_realize (GtkWidget * widget);
 
152
static void hildon_pannable_area_unrealize (GtkWidget * widget);
 
153
static void hildon_pannable_area_size_request (GtkWidget * widget,
 
154
                                               GtkRequisition * requisition);
 
155
static void hildon_pannable_area_size_allocate (GtkWidget * widget,
 
156
                                                GtkAllocation * allocation);
 
157
static void hildon_pannable_area_style_set (GtkWidget * widget,
 
158
                                            GtkStyle * previous_style);
 
159
static void hildon_pannable_area_map (GtkWidget * widget);
 
160
static void hildon_pannable_area_unmap (GtkWidget * widget);
 
161
static void hildon_pannable_area_grab_notify (GtkWidget *widget,
 
162
                                              gboolean was_grabbed,
 
163
                                              gpointer user_data);
 
164
static void rgb_from_gdkcolor (GdkColor *color, gdouble *r, gdouble *g, gdouble *b);
 
165
static void hildon_pannable_draw_vscroll (GtkWidget * widget,
 
166
                                          GdkColor *back_color,
 
167
                                          GdkColor *scroll_color);
 
168
static void hildon_pannable_draw_hscroll (GtkWidget * widget,
 
169
                                          GdkColor *back_color,
 
170
                                          GdkColor *scroll_color);
 
171
static void hildon_pannable_area_initial_effect (GtkWidget * widget);
 
172
static void hildon_pannable_area_redraw (HildonPannableArea * area);
 
173
static gboolean hildon_pannable_area_scroll_indicator_fade(HildonPannableArea * area);
 
174
static gboolean hildon_pannable_area_expose_event (GtkWidget * widget,
 
175
                                                   GdkEventExpose * event);
 
176
static GdkWindow * hildon_pannable_area_get_topmost (GdkWindow * window,
 
177
                                                     gint x, gint y,
 
178
                                                     gint * tx, gint * ty);
 
179
static void synth_crossing (GdkWindow * child,
 
180
                            gint x, gint y,
 
181
                            gint x_root, gint y_root,
 
182
                            guint32 time, gboolean in);
 
183
static gboolean hildon_pannable_area_button_press_cb (GtkWidget * widget,
 
184
                                                      GdkEventButton * event);
 
185
static void hildon_pannable_area_refresh (HildonPannableArea * area);
 
186
static void hildon_pannable_axis_scroll (HildonPannableArea *area,
 
187
                                         GtkAdjustment *adjust,
 
188
                                         gdouble *vel,
 
189
                                         gdouble inc,
 
190
                                         gint *overshooting,
 
191
                                         gint *overshot_dist,
 
192
                                         gdouble *scroll_to,
 
193
                                         gint overshoot_max,
 
194
                                         gboolean *s);
 
195
static void hildon_pannable_area_scroll (HildonPannableArea *area,
 
196
                                         gdouble x, gdouble y);
 
197
static gboolean hildon_pannable_area_timeout (HildonPannableArea * area);
 
198
static void hildon_pannable_area_calculate_velocity (gdouble *vel,
 
199
                                                     gdouble delta,
 
200
                                                     gdouble dist,
 
201
                                                     gdouble vmax,
 
202
                                                     guint sps);
 
203
static gboolean hildon_pannable_area_motion_notify_cb (GtkWidget * widget,
 
204
                                                       GdkEventMotion * event);
 
205
static gboolean hildon_pannable_area_button_release_cb (GtkWidget * widget,
 
206
                                                        GdkEventButton * event);
 
207
static gboolean hildon_pannable_area_scroll_cb (GtkWidget *widget,
 
208
                                                GdkEventScroll *event);
 
209
static void hildon_pannable_area_child_mapped (GtkWidget *widget,
 
210
                                               GdkEvent  *event,
 
211
                                               gpointer user_data);
 
212
static void hildon_pannable_area_add (GtkContainer *container, GtkWidget *child);
 
213
static void hildon_pannable_area_remove (GtkContainer *container, GtkWidget *child);
 
214
static void hildon_pannable_calculate_vel_factor (HildonPannableArea * self);
 
215
 
 
216
 
 
217
static void
 
218
hildon_pannable_area_class_init (HildonPannableAreaClass * klass)
 
219
{
 
220
  GObjectClass *object_class = G_OBJECT_CLASS (klass);
 
221
  GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
 
222
  GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
 
223
 
 
224
 
 
225
  g_type_class_add_private (klass, sizeof (HildonPannableAreaPrivate));
 
226
 
 
227
  object_class->get_property = hildon_pannable_area_get_property;
 
228
  object_class->set_property = hildon_pannable_area_set_property;
 
229
  object_class->dispose = hildon_pannable_area_dispose;
 
230
 
 
231
  widget_class->realize = hildon_pannable_area_realize;
 
232
  widget_class->unrealize = hildon_pannable_area_unrealize;
 
233
  widget_class->map = hildon_pannable_area_map;
 
234
  widget_class->unmap = hildon_pannable_area_unmap;
 
235
  widget_class->size_request = hildon_pannable_area_size_request;
 
236
  widget_class->size_allocate = hildon_pannable_area_size_allocate;
 
237
  widget_class->expose_event = hildon_pannable_area_expose_event;
 
238
  widget_class->style_set = hildon_pannable_area_style_set;
 
239
  widget_class->button_press_event = hildon_pannable_area_button_press_cb;
 
240
  widget_class->button_release_event = hildon_pannable_area_button_release_cb;
 
241
  widget_class->motion_notify_event = hildon_pannable_area_motion_notify_cb;
 
242
  widget_class->scroll_event = hildon_pannable_area_scroll_cb;
 
243
 
 
244
  container_class->add = hildon_pannable_area_add;
 
245
  container_class->remove = hildon_pannable_area_remove;
 
246
 
 
247
  klass->horizontal_movement = NULL;
 
248
  klass->vertical_movement = NULL;
 
249
 
 
250
  g_object_class_install_property (object_class,
 
251
                                   PROP_ENABLED,
 
252
                                   g_param_spec_boolean ("enabled",
 
253
                                                         "Enabled",
 
254
                                                         "Enable or disable finger-scroll.",
 
255
                                                         TRUE,
 
256
                                                         G_PARAM_READWRITE |
 
257
                                                         G_PARAM_CONSTRUCT));
 
258
 
 
259
  g_object_class_install_property (object_class,
 
260
                                   PROP_VSCROLLBAR_POLICY,
 
261
                                   g_param_spec_enum ("vscrollbar_policy",
 
262
                                                      "vscrollbar policy",
 
263
                                                      "Visual policy of the vertical scrollbar",
 
264
                                                      GTK_TYPE_POLICY_TYPE,
 
265
                                                      GTK_POLICY_AUTOMATIC,
 
266
                                                      G_PARAM_READWRITE |
 
267
                                                      G_PARAM_CONSTRUCT));
 
268
 
 
269
  g_object_class_install_property (object_class,
 
270
                                   PROP_HSCROLLBAR_POLICY,
 
271
                                   g_param_spec_enum ("hscrollbar_policy",
 
272
                                                      "hscrollbar policy",
 
273
                                                      "Visual policy of the horizontal scrollbar",
 
274
                                                      GTK_TYPE_POLICY_TYPE,
 
275
                                                      GTK_POLICY_AUTOMATIC,
 
276
                                                      G_PARAM_READWRITE |
 
277
                                                      G_PARAM_CONSTRUCT));
 
278
 
 
279
  g_object_class_install_property (object_class,
 
280
                                   PROP_MODE,
 
281
                                   g_param_spec_enum ("mode",
 
282
                                                      "Scroll mode",
 
283
                                                      "Change the finger-scrolling mode.",
 
284
                                                      HILDON_TYPE_PANNABLE_AREA_MODE,
 
285
                                                      HILDON_PANNABLE_AREA_MODE_AUTO,
 
286
                                                      G_PARAM_READWRITE |
 
287
                                                      G_PARAM_CONSTRUCT));
 
288
 
 
289
  g_object_class_install_property (object_class,
 
290
                                   PROP_MOVEMENT_MODE,
 
291
                                   g_param_spec_flags ("mov_mode",
 
292
                                                       "Scroll movement mode",
 
293
                                                       "Controls if the widget can scroll vertically, horizontally or both",
 
294
                                                       HILDON_TYPE_MOVEMENT_MODE,
 
295
                                                       HILDON_MOVEMENT_MODE_VERT,
 
296
                                                       G_PARAM_READWRITE |
 
297
                                                       G_PARAM_CONSTRUCT));
 
298
 
 
299
  g_object_class_install_property (object_class,
 
300
                                   PROP_VELOCITY_MIN,
 
301
                                   g_param_spec_double ("velocity_min",
 
302
                                                        "Minimum scroll velocity",
 
303
                                                        "Minimum distance the child widget should scroll "
 
304
                                                        "per 'frame', in pixels.",
 
305
                                                        0, G_MAXDOUBLE, 0,
 
306
                                                        G_PARAM_READWRITE |
 
307
                                                        G_PARAM_CONSTRUCT));
 
308
 
 
309
  g_object_class_install_property (object_class,
 
310
                                   PROP_VELOCITY_MAX,
 
311
                                   g_param_spec_double ("velocity_max",
 
312
                                                        "Maximum scroll velocity",
 
313
                                                        "Maximum distance the child widget should scroll "
 
314
                                                        "per 'frame', in pixels.",
 
315
                                                        0, G_MAXDOUBLE, 60,
 
316
                                                        G_PARAM_READWRITE |
 
317
                                                        G_PARAM_CONSTRUCT));
 
318
 
 
319
  g_object_class_install_property (object_class,
 
320
                                   PROP_VELOCITY_FAST_FACTOR,
 
321
                                   g_param_spec_double ("velocity_fast_factor",
 
322
                                                        "Fast velocity factor",
 
323
                                                        "Minimum velocity that is considered 'fast': "
 
324
                                                        "children widgets won't receive button presses. "
 
325
                                                        "Expressed as a fraction of the maximum velocity.",
 
326
                                                        0, 1, 0.02,
 
327
                                                        G_PARAM_READWRITE |
 
328
                                                        G_PARAM_CONSTRUCT));
 
329
 
 
330
  g_object_class_install_property (object_class,
 
331
                                   PROP_DECELERATION,
 
332
                                   g_param_spec_double ("deceleration",
 
333
                                                        "Deceleration multiplier",
 
334
                                                        "The multiplier used when decelerating when in "
 
335
                                                        "acceleration scrolling mode.",
 
336
                                                        0, 1.0, 0.90,
 
337
                                                        G_PARAM_READWRITE |
 
338
                                                        G_PARAM_CONSTRUCT));
 
339
 
 
340
  g_object_class_install_property (object_class,
 
341
                                   PROP_SPS,
 
342
                                   g_param_spec_uint ("sps",
 
343
                                                      "Scrolls per second",
 
344
                                                      "Amount of scroll events to generate per second.",
 
345
                                                      0, G_MAXUINT, 25,
 
346
                                                      G_PARAM_READWRITE |
 
347
                                                      G_PARAM_CONSTRUCT));
 
348
 
 
349
  g_object_class_install_property (object_class,
 
350
                                   PROP_VOVERSHOOT_MAX,
 
351
                                   g_param_spec_int ("vovershoot_max",
 
352
                                                     "Vertical overshoot distance",
 
353
                                                     "Space we allow the widget to pass over its vertical limits when hitting the edges, set 0 in order to deactivate overshooting.",
 
354
                                                     0, G_MAXINT, 150,
 
355
                                                     G_PARAM_READWRITE |
 
356
                                                     G_PARAM_CONSTRUCT));
 
357
 
 
358
  g_object_class_install_property (object_class,
 
359
                                   PROP_HOVERSHOOT_MAX,
 
360
                                   g_param_spec_int ("hovershoot_max",
 
361
                                                     "Horizontal overshoot distance",
 
362
                                                     "Space we allow the widget to pass over its horizontal limits when hitting the edges, set 0 in order to deactivate overshooting.",
 
363
                                                     0, G_MAXINT, 150,
 
364
                                                     G_PARAM_READWRITE |
 
365
                                                     G_PARAM_CONSTRUCT));
 
366
 
 
367
  g_object_class_install_property (object_class,
 
368
                                   PROP_SCROLL_TIME,
 
369
                                   g_param_spec_double ("scroll_time",
 
370
                                                        "Time to scroll to a position",
 
371
                                                        "The time to scroll to a position when calling the hildon_pannable_scroll_to function"
 
372
                                                        "acceleration scrolling mode.",
 
373
                                                        1.0, 20.0, 10.0,
 
374
                                                        G_PARAM_READWRITE |
 
375
                                                        G_PARAM_CONSTRUCT));
 
376
 
 
377
  g_object_class_install_property (object_class,
 
378
                                   PROP_INITIAL_HINT,
 
379
                                   g_param_spec_boolean ("initial-hint",
 
380
                                                         "Initial hint",
 
381
                                                         "Whether to hint the user about the pannability of the container.",
 
382
                                                         FALSE,
 
383
                                                         G_PARAM_READWRITE |
 
384
                                                         G_PARAM_CONSTRUCT));
 
385
 
 
386
  gtk_widget_class_install_style_property (widget_class,
 
387
                                           g_param_spec_uint
 
388
                                           ("indicator-width",
 
389
                                            "Width of the scroll indicators",
 
390
                                            "Pixel width used to draw the scroll indicators.",
 
391
                                            0, G_MAXUINT, 8,
 
392
                                            G_PARAM_READWRITE));
 
393
 
 
394
  pannable_area_signals[HORIZONTAL_MOVEMENT] =
 
395
    g_signal_new ("horizontal_movement",
 
396
                  G_TYPE_FROM_CLASS (object_class),
 
397
                  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
 
398
                  G_STRUCT_OFFSET (HildonPannableAreaClass, horizontal_movement),
 
399
                  NULL, NULL,
 
400
                  _hildon_marshal_VOID__INT_DOUBLE_DOUBLE,
 
401
                  G_TYPE_NONE, 3,
 
402
                  G_TYPE_INT,
 
403
                  G_TYPE_DOUBLE,
 
404
                  G_TYPE_DOUBLE);
 
405
 
 
406
  pannable_area_signals[VERTICAL_MOVEMENT] =
 
407
    g_signal_new ("vertical_movement",
 
408
                  G_TYPE_FROM_CLASS (object_class),
 
409
                  G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
 
410
                  G_STRUCT_OFFSET (HildonPannableAreaClass, vertical_movement),
 
411
                  NULL, NULL,
 
412
                  _hildon_marshal_VOID__INT_DOUBLE_DOUBLE,
 
413
                  G_TYPE_NONE, 3,
 
414
                  G_TYPE_INT,
 
415
                  G_TYPE_DOUBLE,
 
416
                  G_TYPE_DOUBLE);
 
417
 
 
418
 
 
419
}
 
420
 
 
421
static void
 
422
hildon_pannable_area_init (HildonPannableArea * area)
 
423
{
 
424
  HildonPannableAreaPrivate *priv = PANNABLE_AREA_PRIVATE (area);
 
425
 
 
426
  area->priv = priv;
 
427
 
 
428
  priv->moved = FALSE;
 
429
  priv->clicked = FALSE;
 
430
  priv->last_time = 0;
 
431
  priv->last_type = 0;
 
432
  priv->vscroll_visible = TRUE;
 
433
  priv->hscroll_visible = TRUE;
 
434
  priv->area_width = 6;
 
435
  priv->overshot_dist_x = 0;
 
436
  priv->overshot_dist_y = 0;
 
437
  priv->overshooting_y = 0;
 
438
  priv->overshooting_x = 0;
 
439
  priv->idle_id = 0;
 
440
  priv->vel_x = 0;
 
441
  priv->vel_y = 0;
 
442
  priv->scroll_indicator_alpha = 0.0;
 
443
  priv->scroll_indicator_timeout = 0;
 
444
  priv->scroll_indicator_event_interrupt = 0;
 
445
  priv->scroll_delay_counter = SCROLLBAR_FADE_DELAY;
 
446
  priv->scroll_to_x = -1;
 
447
  priv->scroll_to_y = -1;
 
448
  priv->first_drag = TRUE;
 
449
  priv->initial_effect = TRUE;
 
450
 
 
451
  hildon_pannable_calculate_vel_factor (area);
 
452
 
 
453
  gtk_widget_add_events (GTK_WIDGET (area), GDK_POINTER_MOTION_HINT_MASK);
 
454
 
 
455
  priv->hadjust =
 
456
    GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
 
457
  priv->vadjust =
 
458
    GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
 
459
 
 
460
  g_object_ref_sink (G_OBJECT (priv->hadjust));
 
461
  g_object_ref_sink (G_OBJECT (priv->vadjust));
 
462
 
 
463
  g_signal_connect_swapped (G_OBJECT (priv->hadjust), "value-changed",
 
464
                            G_CALLBACK (hildon_pannable_area_redraw), area);
 
465
  g_signal_connect_swapped (G_OBJECT (priv->vadjust), "value-changed",
 
466
                            G_CALLBACK (hildon_pannable_area_redraw), area);
 
467
  g_signal_connect (G_OBJECT (area), "grab-notify",
 
468
                    G_CALLBACK (hildon_pannable_area_grab_notify), NULL);
 
469
}
 
470
 
 
471
static void
 
472
hildon_pannable_area_get_property (GObject * object,
 
473
                                   guint property_id,
 
474
                                   GValue * value,
 
475
                                   GParamSpec * pspec)
 
476
{
 
477
  HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (object)->priv;
 
478
 
 
479
  switch (property_id) {
 
480
  case PROP_ENABLED:
 
481
    g_value_set_boolean (value, priv->enabled);
 
482
    break;
 
483
  case PROP_MODE:
 
484
    g_value_set_enum (value, priv->mode);
 
485
    break;
 
486
  case PROP_MOVEMENT_MODE:
 
487
    g_value_set_flags (value, priv->mov_mode);
 
488
    break;
 
489
  case PROP_VELOCITY_MIN:
 
490
    g_value_set_double (value, priv->vmin);
 
491
    break;
 
492
  case PROP_VELOCITY_MAX:
 
493
    g_value_set_double (value, priv->vmax);
 
494
    break;
 
495
  case PROP_VELOCITY_FAST_FACTOR:
 
496
    g_value_set_double (value, priv->vfast_factor);
 
497
    break;
 
498
  case PROP_DECELERATION:
 
499
    g_value_set_double (value, priv->decel);
 
500
    break;
 
501
  case PROP_SPS:
 
502
    g_value_set_uint (value, priv->sps);
 
503
    break;
 
504
  case PROP_VSCROLLBAR_POLICY:
 
505
    g_value_set_enum (value, priv->vscrollbar_policy);
 
506
    break;
 
507
  case PROP_HSCROLLBAR_POLICY:
 
508
    g_value_set_enum (value, priv->hscrollbar_policy);
 
509
    break;
 
510
  case PROP_VOVERSHOOT_MAX:
 
511
    g_value_set_int (value, priv->vovershoot_max);
 
512
    break;
 
513
  case PROP_HOVERSHOOT_MAX:
 
514
    g_value_set_int (value, priv->hovershoot_max);
 
515
    break;
 
516
  case PROP_SCROLL_TIME:
 
517
    g_value_set_double (value, priv->scroll_time);
 
518
    break;
 
519
  case PROP_INITIAL_HINT:
 
520
    g_value_set_boolean (value, priv->initial_hint);
 
521
    break;
 
522
 
 
523
  default:
 
524
    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
 
525
  }
 
526
}
 
527
 
 
528
static void
 
529
hildon_pannable_area_set_property (GObject * object,
 
530
                                   guint property_id,
 
531
                                   const GValue * value,
 
532
                                   GParamSpec * pspec)
 
533
{
 
534
  HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (object)->priv;
 
535
  gboolean enabled;
 
536
 
 
537
  switch (property_id) {
 
538
  case PROP_ENABLED:
 
539
    enabled = g_value_get_boolean (value);
 
540
 
 
541
    if ((priv->enabled != enabled) && (GTK_WIDGET_REALIZED (object))) {
 
542
      if (enabled)
 
543
        gdk_window_raise (priv->event_window);
 
544
      else
 
545
        gdk_window_lower (priv->event_window);
 
546
    }
 
547
 
 
548
    priv->enabled = enabled;
 
549
    break;
 
550
  case PROP_MODE:
 
551
    priv->mode = g_value_get_enum (value);
 
552
    break;
 
553
  case PROP_MOVEMENT_MODE:
 
554
    priv->mov_mode = g_value_get_flags (value);
 
555
    break;
 
556
  case PROP_VELOCITY_MIN:
 
557
    priv->vmin = g_value_get_double (value);
 
558
    break;
 
559
  case PROP_VELOCITY_MAX:
 
560
    priv->vmax = g_value_get_double (value);
 
561
    break;
 
562
  case PROP_VELOCITY_FAST_FACTOR:
 
563
    priv->vfast_factor = g_value_get_double (value);
 
564
    break;
 
565
  case PROP_DECELERATION:
 
566
    hildon_pannable_calculate_vel_factor (HILDON_PANNABLE_AREA (object));
 
567
 
 
568
    priv->decel = g_value_get_double (value);
 
569
    break;
 
570
  case PROP_SPS:
 
571
    priv->sps = g_value_get_uint (value);
 
572
    break;
 
573
  case PROP_VSCROLLBAR_POLICY:
 
574
    priv->vscrollbar_policy = g_value_get_enum (value);
 
575
 
 
576
    gtk_widget_queue_resize (GTK_WIDGET (object));
 
577
    break;
 
578
  case PROP_HSCROLLBAR_POLICY:
 
579
    priv->hscrollbar_policy = g_value_get_enum (value);
 
580
 
 
581
    gtk_widget_queue_resize (GTK_WIDGET (object));
 
582
    break;
 
583
  case PROP_VOVERSHOOT_MAX:
 
584
    priv->vovershoot_max = g_value_get_int (value);
 
585
    break;
 
586
  case PROP_HOVERSHOOT_MAX:
 
587
    priv->hovershoot_max = g_value_get_int (value);
 
588
    break;
 
589
  case PROP_SCROLL_TIME:
 
590
    priv->scroll_time = g_value_get_double (value);
 
591
 
 
592
    hildon_pannable_calculate_vel_factor (HILDON_PANNABLE_AREA (object));
 
593
    break;
 
594
  case PROP_INITIAL_HINT:
 
595
    priv->initial_hint = g_value_get_boolean (value);
 
596
    break;
 
597
 
 
598
  default:
 
599
    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
 
600
  }
 
601
}
 
602
 
 
603
static void
 
604
hildon_pannable_area_dispose (GObject * object)
 
605
{
 
606
  HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (object)->priv;
 
607
  GtkWidget *child = gtk_bin_get_child (GTK_BIN (object));
 
608
 
 
609
  if (priv->idle_id) {
 
610
    g_source_remove (priv->idle_id);
 
611
    priv->idle_id = 0;
 
612
  }
 
613
 
 
614
  if (priv->scroll_indicator_timeout){
 
615
    g_source_remove (priv->scroll_indicator_timeout);
 
616
    priv->scroll_indicator_timeout = 0;
 
617
  }
 
618
 
 
619
  if (priv->hadjust) {
 
620
    g_object_unref (priv->hadjust);
 
621
    priv->hadjust = NULL;
 
622
  }
 
623
  if (priv->vadjust) {
 
624
    g_object_unref (priv->vadjust);
 
625
    priv->vadjust = NULL;
 
626
  }
 
627
 
 
628
  if (child) {
 
629
    g_signal_handlers_disconnect_by_func (GTK_WIDGET (child),
 
630
                                          G_CALLBACK (hildon_pannable_area_child_mapped),
 
631
                                          object);
 
632
  }
 
633
 
 
634
  if (G_OBJECT_CLASS (hildon_pannable_area_parent_class)->dispose)
 
635
    G_OBJECT_CLASS (hildon_pannable_area_parent_class)->dispose (object);
 
636
}
 
637
 
 
638
static void
 
639
hildon_pannable_area_realize (GtkWidget * widget)
 
640
{
 
641
  GdkWindowAttr attributes;
 
642
  gint attributes_mask;
 
643
  gint border_width;
 
644
  HildonPannableAreaPrivate *priv;
 
645
 
 
646
  priv = HILDON_PANNABLE_AREA (widget)->priv;
 
647
 
 
648
  GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
 
649
 
 
650
  border_width = GTK_CONTAINER (widget)->border_width;
 
651
 
 
652
  attributes.x = widget->allocation.x + border_width;
 
653
  attributes.y = widget->allocation.y + border_width;
 
654
  attributes.width = MAX (widget->allocation.width - 2 * border_width -
 
655
                          (priv->vscroll_visible ? priv->vscroll_rect.width : 0), 0);
 
656
  attributes.height = MAX (widget->allocation.height - 2 * border_width -
 
657
                           (priv->hscroll_visible ? priv->hscroll_rect.height : 0), 0);
 
658
  attributes.window_type = GDK_WINDOW_CHILD;
 
659
  attributes.event_mask = gtk_widget_get_events (widget)
 
660
    | GDK_BUTTON_MOTION_MASK
 
661
    | GDK_BUTTON_PRESS_MASK
 
662
    | GDK_BUTTON_RELEASE_MASK
 
663
    | GDK_SCROLL_MASK
 
664
    | GDK_EXPOSURE_MASK | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK;
 
665
 
 
666
  widget->window = gtk_widget_get_parent_window (widget);
 
667
  g_object_ref (widget->window);
 
668
 
 
669
  attributes.wclass = GDK_INPUT_ONLY;
 
670
  attributes_mask = GDK_WA_X | GDK_WA_Y;
 
671
 
 
672
  priv->event_window = gdk_window_new (widget->window,
 
673
                                       &attributes, attributes_mask);
 
674
  gdk_window_set_user_data (priv->event_window, widget);
 
675
 
 
676
  widget->style = gtk_style_attach (widget->style, widget->window);
 
677
}
 
678
 
 
679
static void
 
680
hildon_pannable_area_unrealize (GtkWidget * widget)
 
681
{
 
682
  HildonPannableAreaPrivate *priv;
 
683
 
 
684
  priv = HILDON_PANNABLE_AREA (widget)->priv;
 
685
 
 
686
  if (priv->event_window != NULL) {
 
687
    gdk_window_set_user_data (priv->event_window, NULL);
 
688
    gdk_window_destroy (priv->event_window);
 
689
    priv->event_window = NULL;
 
690
  }
 
691
 
 
692
  if (GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->unrealize)
 
693
    (*GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->unrealize)(widget);
 
694
}
 
695
 
 
696
static void
 
697
hildon_pannable_area_size_request (GtkWidget * widget,
 
698
                                   GtkRequisition * requisition)
 
699
{
 
700
  GtkRequisition child_requisition;
 
701
  HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
 
702
  GtkWidget *child = gtk_bin_get_child (GTK_BIN (widget));
 
703
 
 
704
  if (child && GTK_WIDGET_VISIBLE (child))
 
705
    {
 
706
      gtk_widget_size_request (child, &child_requisition);
 
707
    }
 
708
 
 
709
  if (priv->hscrollbar_policy == GTK_POLICY_NEVER) {
 
710
    requisition->width = child_requisition.width;
 
711
  } else {
 
712
    requisition->width = priv->area_width;
 
713
  }
 
714
 
 
715
  if (priv->vscrollbar_policy == GTK_POLICY_NEVER) {
 
716
    requisition->height = child_requisition.height;
 
717
  } else {
 
718
    requisition->height = priv->area_width;
 
719
  }
 
720
 
 
721
  requisition->width += 2 * GTK_CONTAINER (widget)->border_width;
 
722
  requisition->height += 2 * GTK_CONTAINER (widget)->border_width;
 
723
}
 
724
 
 
725
static void
 
726
hildon_pannable_area_size_allocate (GtkWidget * widget,
 
727
                                    GtkAllocation * allocation)
 
728
{
 
729
  GtkAllocation child_allocation;
 
730
  HildonPannableAreaPrivate *priv;
 
731
  GtkWidget *child = gtk_bin_get_child (GTK_BIN (widget));
 
732
 
 
733
  widget->allocation = *allocation;
 
734
 
 
735
  priv = HILDON_PANNABLE_AREA (widget)->priv;
 
736
 
 
737
  child_allocation.x = allocation->x + GTK_CONTAINER (widget)->border_width;
 
738
  child_allocation.y = allocation->y + GTK_CONTAINER (widget)->border_width;
 
739
  child_allocation.width = MAX (allocation->width -
 
740
                                2 * GTK_CONTAINER (widget)->border_width -
 
741
                                (priv->vscroll_visible ? priv->vscroll_rect.width : 0), 0);
 
742
  child_allocation.height = MAX (allocation->height -
 
743
                                 2 * GTK_CONTAINER (widget)->border_width -
 
744
                                 (priv->hscroll_visible ? priv->hscroll_rect.height : 0), 0);
 
745
 
 
746
  if (GTK_WIDGET_REALIZED (widget)) {
 
747
    if (priv->event_window != NULL)
 
748
      gdk_window_move_resize (priv->event_window,
 
749
                              child_allocation.x,
 
750
                              child_allocation.y,
 
751
                              child_allocation.width,
 
752
                              child_allocation.height);
 
753
  }
 
754
 
 
755
  if (priv->overshot_dist_y > 0) {
 
756
    child_allocation.y = MIN (child_allocation.y + priv->overshot_dist_y,
 
757
                              allocation->y + child_allocation.height);
 
758
    child_allocation.height = MAX (child_allocation.height - priv->overshot_dist_y, 0);
 
759
  } else if (priv->overshot_dist_y < 0) {
 
760
    child_allocation.height = MAX (child_allocation.height + priv->overshot_dist_y, 0);
 
761
  }
 
762
 
 
763
  if (priv->overshot_dist_x > 0) {
 
764
    child_allocation.x = MIN (child_allocation.x + priv->overshot_dist_x,
 
765
                              allocation->x + child_allocation.width);
 
766
    child_allocation.width = MAX (child_allocation.width - priv->overshot_dist_x, 0);
 
767
  } else if (priv->overshot_dist_x < 0) {
 
768
    child_allocation.width = MAX (child_allocation.width + priv->overshot_dist_x, 0);
 
769
  }
 
770
 
 
771
  if (child)
 
772
    gtk_widget_size_allocate (child, &child_allocation);
 
773
 
 
774
  /* we have to do this after child size_allocate because page_size is
 
775
   * changed when we allocate the size of the children */
 
776
  if (priv->overshot_dist_y < 0) {
 
777
    gtk_adjustment_set_value (priv->vadjust, priv->vadjust->upper -
 
778
                              priv->vadjust->page_size);
 
779
  }
 
780
 
 
781
  if (priv->overshot_dist_x < 0) {
 
782
    gtk_adjustment_set_value (priv->hadjust, priv->hadjust->upper -
 
783
                              priv->hadjust->page_size);
 
784
  }
 
785
 
 
786
  hildon_pannable_area_refresh (HILDON_PANNABLE_AREA (widget));
 
787
}
 
788
 
 
789
static void
 
790
hildon_pannable_area_style_set (GtkWidget * widget,
 
791
                                GtkStyle * previous_style)
 
792
{
 
793
  HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
 
794
 
 
795
  GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->
 
796
    style_set (widget, previous_style);
 
797
 
 
798
  gtk_widget_style_get (widget, "indicator-width", &priv->area_width, NULL);
 
799
}
 
800
 
 
801
static void
 
802
hildon_pannable_area_map (GtkWidget * widget)
 
803
{
 
804
  HildonPannableAreaPrivate *priv;
 
805
 
 
806
  priv = HILDON_PANNABLE_AREA (widget)->priv;
 
807
 
 
808
  if (priv->event_window != NULL && !priv->enabled)
 
809
    gdk_window_show (priv->event_window);
 
810
 
 
811
  (*GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->map) (widget);
 
812
 
 
813
  if (priv->event_window != NULL && priv->enabled)
 
814
    gdk_window_show (priv->event_window);
 
815
}
 
816
 
 
817
static void
 
818
hildon_pannable_area_unmap (GtkWidget * widget)
 
819
{
 
820
  HildonPannableAreaPrivate *priv;
 
821
 
 
822
  priv = HILDON_PANNABLE_AREA (widget)->priv;
 
823
 
 
824
  if (priv->event_window != NULL)
 
825
    gdk_window_hide (priv->event_window);
 
826
 
 
827
  (*GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->unmap) (widget);
 
828
}
 
829
 
 
830
static void
 
831
hildon_pannable_area_grab_notify (GtkWidget *widget,
 
832
                                  gboolean was_grabbed,
 
833
                                  gpointer user_data)
 
834
{
 
835
  /* an internal widget has grabbed the focus and now has returned it,
 
836
     we have to do some release actions */
 
837
  if (was_grabbed) {
 
838
    HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
 
839
 
 
840
    priv->scroll_indicator_event_interrupt = 0;
 
841
    priv->scroll_delay_counter = SCROLLBAR_FADE_DELAY;
 
842
 
 
843
    if (!priv->scroll_indicator_timeout) {
 
844
      priv->scroll_indicator_timeout = gdk_threads_add_timeout
 
845
        ((gint) (1000.0 / (gdouble) priv->sps),
 
846
         (GSourceFunc) hildon_pannable_area_scroll_indicator_fade, widget);
 
847
    }
 
848
 
 
849
    priv->last_type = 3;
 
850
    priv->moved = FALSE;
 
851
  }
 
852
}
 
853
 
 
854
static void
 
855
rgb_from_gdkcolor (GdkColor *color, gdouble *r, gdouble *g, gdouble *b)
 
856
{
 
857
  *r = (color->red >> 8) / 255.0;
 
858
  *g = (color->green >> 8) / 255.0;
 
859
  *b = (color->blue >> 8) / 255.0;
 
860
}
 
861
 
 
862
static void
 
863
hildon_pannable_draw_vscroll (GtkWidget * widget,
 
864
                              GdkColor *back_color,
 
865
                              GdkColor *scroll_color)
 
866
{
 
867
  HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
 
868
  gfloat y, height;
 
869
  cairo_t *cr;
 
870
  cairo_pattern_t *pattern;
 
871
  gdouble r, g, b;
 
872
  gint radius = (priv->vscroll_rect.width/2) - 1;
 
873
 
 
874
  cr = gdk_cairo_create(widget->window);
 
875
 
 
876
  /* Draw the background */
 
877
  rgb_from_gdkcolor (back_color, &r, &g, &b);
 
878
  cairo_set_source_rgb (cr, r, g, b);
 
879
  cairo_rectangle(cr, priv->vscroll_rect.x, priv->vscroll_rect.y,
 
880
                  priv->vscroll_rect.width,
 
881
                  priv->vscroll_rect.height);
 
882
  cairo_fill_preserve (cr);
 
883
  cairo_clip (cr);
 
884
 
 
885
  /* Calculate the scroll bar height and position */
 
886
  y = widget->allocation.y +
 
887
    ((priv->vadjust->value / priv->vadjust->upper) *
 
888
     (widget->allocation.height -
 
889
      (priv->hscroll_visible ? priv->area_width : 0)));
 
890
  height = (widget->allocation.y +
 
891
            (((priv->vadjust->value +
 
892
               priv->vadjust->page_size) /
 
893
              priv->vadjust->upper) *
 
894
             (widget->allocation.height -
 
895
              (priv->hscroll_visible ? priv->area_width : 0)))) - y;
 
896
 
 
897
  /* Set a minimum height */
 
898
  height = MAX (SCROLL_BAR_MIN_SIZE, height);
 
899
 
 
900
  /* Check the max y position */
 
901
  y = MIN (y, widget->allocation.y + widget->allocation.height -
 
902
           (priv->hscroll_visible ? priv->hscroll_rect.height : 0) -
 
903
           height);
 
904
 
 
905
  /* Draw the scrollbar */
 
906
  rgb_from_gdkcolor (scroll_color, &r, &g, &b);
 
907
 
 
908
  pattern = cairo_pattern_create_linear(radius+1, y, radius+1,y + height);
 
909
  cairo_pattern_add_color_stop_rgb(pattern, 0, r, g, b);
 
910
  cairo_pattern_add_color_stop_rgb(pattern, 1, r/2, g/2, b/2);
 
911
  cairo_set_source(cr, pattern);
 
912
  cairo_fill(cr);
 
913
  cairo_pattern_destroy(pattern);
 
914
 
 
915
  cairo_arc(cr, priv->vscroll_rect.x + radius + 1, y + radius + 1, radius, G_PI, 0);
 
916
  cairo_line_to(cr, priv->vscroll_rect.x + (radius * 2) + 1, y + height - radius);
 
917
  cairo_arc(cr, priv->vscroll_rect.x + radius + 1, y + height - radius, radius, 0, G_PI);
 
918
  cairo_line_to(cr, priv->vscroll_rect.x + 1, y + height - radius);
 
919
  cairo_clip (cr);
 
920
 
 
921
  cairo_paint_with_alpha(cr, priv->scroll_indicator_alpha);
 
922
 
 
923
  cairo_destroy(cr);
 
924
}
 
925
 
 
926
static void
 
927
hildon_pannable_draw_hscroll (GtkWidget * widget,
 
928
                              GdkColor *back_color,
 
929
                              GdkColor *scroll_color)
 
930
{
 
931
  HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
 
932
  gfloat x, width;
 
933
  cairo_t *cr;
 
934
  cairo_pattern_t *pattern;
 
935
  gdouble r, g, b;
 
936
  gint radius = (priv->hscroll_rect.height/2) - 1;
 
937
 
 
938
  cr = gdk_cairo_create(widget->window);
 
939
 
 
940
  /* Draw the background */
 
941
  rgb_from_gdkcolor (back_color, &r, &g, &b);
 
942
  cairo_set_source_rgb (cr, r, g, b);
 
943
  cairo_rectangle(cr, priv->hscroll_rect.x, priv->hscroll_rect.y,
 
944
                  priv->hscroll_rect.width,
 
945
                  priv->hscroll_rect.height);
 
946
  cairo_fill_preserve (cr);
 
947
  cairo_clip (cr);
 
948
 
 
949
  /* calculate the scrollbar width and position */
 
950
  x = widget->allocation.x +
 
951
    ((priv->hadjust->value / priv->hadjust->upper) *
 
952
     (widget->allocation.width - (priv->vscroll_visible ? priv->area_width : 0)));
 
953
  width =
 
954
    (widget->allocation.x +
 
955
     (((priv->hadjust->value +
 
956
        priv->hadjust->page_size) / priv->hadjust->upper) *
 
957
      (widget->allocation.width -
 
958
       (priv->vscroll_visible ? priv->area_width : 0)))) - x;
 
959
 
 
960
  /* Set a minimum width */
 
961
  width = MAX (SCROLL_BAR_MIN_SIZE, width);
 
962
 
 
963
  /* Check the max x position */
 
964
  x = MIN (x, widget->allocation.x + widget->allocation.width -
 
965
           (priv->vscroll_visible ? priv->vscroll_rect.width : 0) -
 
966
           width);
 
967
 
 
968
  /* Draw the scrollbar */
 
969
  rgb_from_gdkcolor (scroll_color, &r, &g, &b);
 
970
 
 
971
  pattern = cairo_pattern_create_linear(x, radius+1, x+width, radius+1);
 
972
  cairo_pattern_add_color_stop_rgb(pattern, 0, r, g, b);
 
973
  cairo_pattern_add_color_stop_rgb(pattern, 1, r/2, g/2, b/2);
 
974
  cairo_set_source(cr, pattern);
 
975
  cairo_fill(cr);
 
976
  cairo_pattern_destroy(pattern);
 
977
 
 
978
  cairo_arc_negative(cr, x + radius + 1, priv->hscroll_rect.y + radius + 1, radius, 3*G_PI_2, G_PI_2);
 
979
  cairo_line_to(cr, x + width - radius, priv->hscroll_rect.y + (radius * 2) + 1);
 
980
  cairo_arc_negative(cr, x + width - radius, priv->hscroll_rect.y + radius + 1, radius, G_PI_2, 3*G_PI_2);
 
981
  cairo_line_to(cr, x + width - radius, priv->hscroll_rect.y + 1);
 
982
  cairo_clip (cr);
 
983
 
 
984
  cairo_paint_with_alpha(cr, priv->scroll_indicator_alpha);
 
985
 
 
986
  cairo_destroy(cr);
 
987
}
 
988
 
 
989
static void
 
990
hildon_pannable_area_initial_effect (GtkWidget * widget)
 
991
{
 
992
  HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
 
993
  gboolean hscroll_visible, vscroll_visible;
 
994
 
 
995
  if (priv->initial_hint) {
 
996
    if (((priv->vovershoot_max != 0)||(priv->hovershoot_max != 0)) &&
 
997
        ((priv->mode == HILDON_PANNABLE_AREA_MODE_AUTO) ||
 
998
         (priv->mode == HILDON_PANNABLE_AREA_MODE_ACCEL))) {
 
999
      vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
 
1000
                 priv->vadjust->page_size);
 
1001
      hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
 
1002
                 priv->hadjust->page_size);
 
1003
      /* If scrolling is possible in both axes, only hint about scrolling in
 
1004
         the vertical one. */
 
1005
      if ((vscroll_visible)&&(priv->vovershoot_max != 0)) {
 
1006
        priv->overshot_dist_y = priv->vovershoot_max;
 
1007
        priv->vel_y = priv->vmax * 0.1;
 
1008
      } else if ((hscroll_visible)&&(priv->hovershoot_max != 0)) {
 
1009
        priv->overshot_dist_x = priv->hovershoot_max;
 
1010
        priv->vel_x = priv->vmax * 0.1;
 
1011
      }
 
1012
 
 
1013
      if (vscroll_visible || hscroll_visible) {
 
1014
        priv->idle_id = gdk_threads_add_timeout ((gint) (1000.0 / (gdouble) priv->sps),
 
1015
                                                 (GSourceFunc)
 
1016
                                                 hildon_pannable_area_timeout, widget);
 
1017
      }
 
1018
    }
 
1019
 
 
1020
    if (priv->vscroll_visible || priv->hscroll_visible) {
 
1021
      priv->scroll_indicator_alpha = 1.0;
 
1022
 
 
1023
      priv->scroll_indicator_timeout =
 
1024
        gdk_threads_add_timeout ((gint) (1000.0 / (gdouble) priv->sps),
 
1025
                                 (GSourceFunc) hildon_pannable_area_scroll_indicator_fade,
 
1026
                                 widget);
 
1027
    }
 
1028
  }
 
1029
}
 
1030
 
 
1031
static void
 
1032
hildon_pannable_area_redraw (HildonPannableArea * area)
 
1033
{
 
1034
  HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (area)->priv;
 
1035
 
 
1036
  /* Redraw scroll indicators */
 
1037
  if (GTK_WIDGET_DRAWABLE (area)) {
 
1038
      if (priv->hscroll_visible) {
 
1039
        gdk_window_invalidate_rect (GTK_WIDGET (area)->window,
 
1040
                                    &priv->hscroll_rect, FALSE);
 
1041
      }
 
1042
 
 
1043
      if (priv->vscroll_visible) {
 
1044
        gdk_window_invalidate_rect (GTK_WIDGET (area)->window,
 
1045
                                    &priv->vscroll_rect, FALSE);
 
1046
      }
 
1047
  }
 
1048
}
 
1049
 
 
1050
static gboolean
 
1051
hildon_pannable_area_scroll_indicator_fade(HildonPannableArea * area)
 
1052
{
 
1053
  gint retval = TRUE;
 
1054
  HildonPannableAreaPrivate *priv = area->priv;
 
1055
 
 
1056
  /* if moving do not fade out */
 
1057
  if (((ABS (priv->vel_y)>1.0)||
 
1058
       (ABS (priv->vel_x)>1.0))&&(!priv->clicked)) {
 
1059
 
 
1060
    return TRUE;
 
1061
  }
 
1062
 
 
1063
  if (priv->scroll_indicator_event_interrupt) {
 
1064
    /* Stop a fade out, and fade back in */
 
1065
    if (priv->scroll_indicator_alpha >= 0.9) {
 
1066
      priv->scroll_indicator_timeout = 0;
 
1067
      priv->scroll_indicator_alpha = 1.0;
 
1068
      retval = FALSE;
 
1069
    } else {
 
1070
      priv->scroll_indicator_alpha += 0.2;
 
1071
    }
 
1072
 
 
1073
    hildon_pannable_area_redraw (area);
 
1074
  }
 
1075
 
 
1076
  if ((priv->scroll_indicator_alpha > 0.9) &&
 
1077
      (priv->scroll_delay_counter > 0)) {
 
1078
    priv->scroll_delay_counter--;
 
1079
 
 
1080
    return TRUE;
 
1081
  }
 
1082
 
 
1083
  if (!priv->scroll_indicator_event_interrupt) {
 
1084
    /* Continue fade out */
 
1085
    if (priv->scroll_indicator_alpha <= 0.1) {
 
1086
      priv->scroll_indicator_timeout = 0;
 
1087
      priv->scroll_delay_counter = SCROLLBAR_FADE_DELAY;
 
1088
      priv->scroll_indicator_alpha = 0.0;
 
1089
      retval = FALSE;
 
1090
    } else {
 
1091
      priv->scroll_indicator_alpha -= 0.2;
 
1092
    }
 
1093
 
 
1094
    hildon_pannable_area_redraw (area);
 
1095
  }
 
1096
 
 
1097
  return retval;
 
1098
}
 
1099
 
 
1100
static gboolean
 
1101
hildon_pannable_area_expose_event (GtkWidget * widget,
 
1102
                                   GdkEventExpose * event)
 
1103
{
 
1104
 
 
1105
  HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
 
1106
  GdkColor back_color = widget->style->bg[GTK_STATE_NORMAL];
 
1107
  GdkColor scroll_color = widget->style->base[GTK_STATE_SELECTED];
 
1108
 
 
1109
  if (gtk_bin_get_child (GTK_BIN (widget))) {
 
1110
 
 
1111
    if (priv->scroll_indicator_alpha > 0.1) {
 
1112
      if (priv->vscroll_visible) {
 
1113
        hildon_pannable_draw_vscroll (widget, &back_color, &scroll_color);
 
1114
      }
 
1115
      if (priv->hscroll_visible) {
 
1116
        hildon_pannable_draw_hscroll (widget, &back_color, &scroll_color);
 
1117
      }
 
1118
    }
 
1119
 
 
1120
    /* draw overshooting rectangles */
 
1121
    if (priv->overshot_dist_y > 0) {
 
1122
      gint overshot_height;
 
1123
 
 
1124
      overshot_height = MIN (priv->overshot_dist_y, widget->allocation.height -
 
1125
                             (priv->hscroll_visible ? priv->hscroll_rect.height : 0));
 
1126
 
 
1127
      gdk_draw_rectangle (widget->window,
 
1128
                          widget->style->bg_gc[GTK_STATE_NORMAL],
 
1129
                          TRUE,
 
1130
                          widget->allocation.x,
 
1131
                          widget->allocation.y,
 
1132
                          widget->allocation.width -
 
1133
                          (priv->vscroll_visible ? priv->vscroll_rect.width : 0),
 
1134
                          overshot_height);
 
1135
    } else if (priv->overshot_dist_y < 0) {
 
1136
      gint overshot_height;
 
1137
      gint overshot_y;
 
1138
 
 
1139
      overshot_height =
 
1140
        MAX (priv->overshot_dist_y,
 
1141
             -(widget->allocation.height -
 
1142
               (priv->hscroll_visible ? priv->hscroll_rect.height : 0)));
 
1143
 
 
1144
      overshot_y = MAX (widget->allocation.y +
 
1145
                        widget->allocation.height +
 
1146
                        overshot_height -
 
1147
                        (priv->hscroll_visible ? priv->hscroll_rect.height : 0), 0);
 
1148
 
 
1149
      gdk_draw_rectangle (widget->window,
 
1150
                          widget->style->bg_gc[GTK_STATE_NORMAL],
 
1151
                          TRUE,
 
1152
                          widget->allocation.x,
 
1153
                          overshot_y,
 
1154
                          widget->allocation.width -
 
1155
                          priv->vscroll_rect.width,
 
1156
                          -overshot_height);
 
1157
    }
 
1158
 
 
1159
    if (priv->overshot_dist_x > 0) {
 
1160
      gint overshot_width;
 
1161
 
 
1162
      overshot_width = MIN (priv->overshot_dist_x, widget->allocation.width -
 
1163
                             (priv->vscroll_visible ? priv->vscroll_rect.width : 0));
 
1164
 
 
1165
      gdk_draw_rectangle (widget->window,
 
1166
                          widget->style->bg_gc[GTK_STATE_NORMAL],
 
1167
                          TRUE,
 
1168
                          widget->allocation.x,
 
1169
                          widget->allocation.y,
 
1170
                          overshot_width,
 
1171
                          widget->allocation.height -
 
1172
                          (priv->hscroll_visible ? priv->hscroll_rect.height : 0));
 
1173
    } else if (priv->overshot_dist_x < 0) {
 
1174
      gint overshot_width;
 
1175
      gint overshot_x;
 
1176
 
 
1177
      overshot_width =
 
1178
        MAX (priv->overshot_dist_x,
 
1179
             -(widget->allocation.width -
 
1180
               (priv->vscroll_visible ? priv->vscroll_rect.width : 0)));
 
1181
 
 
1182
      overshot_x = MAX (widget->allocation.x +
 
1183
                        widget->allocation.width +
 
1184
                        overshot_width -
 
1185
                        (priv->vscroll_visible ? priv->vscroll_rect.width : 0), 0);
 
1186
 
 
1187
      gdk_draw_rectangle (widget->window,
 
1188
                          widget->style->bg_gc[GTK_STATE_NORMAL],
 
1189
                          TRUE,
 
1190
                          overshot_x,
 
1191
                          widget->allocation.y,
 
1192
                          -overshot_width,
 
1193
                          widget->allocation.height -
 
1194
                          priv->hscroll_rect.height);
 
1195
    }
 
1196
 
 
1197
  }
 
1198
 
 
1199
  if (G_UNLIKELY (priv->initial_effect)) {
 
1200
 
 
1201
    hildon_pannable_area_initial_effect (widget);
 
1202
 
 
1203
    priv->initial_effect = FALSE;
 
1204
  }
 
1205
 
 
1206
  return GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->expose_event (widget, event);
 
1207
}
 
1208
 
 
1209
static GdkWindow *
 
1210
hildon_pannable_area_get_topmost (GdkWindow * window,
 
1211
                                  gint x, gint y,
 
1212
                                  gint * tx, gint * ty)
 
1213
{
 
1214
  /* Find the GdkWindow at the given point, by recursing from a given
 
1215
   * parent GdkWindow. Optionally return the co-ordinates transformed
 
1216
   * relative to the child window.
 
1217
   */
 
1218
  gint width, height;
 
1219
 
 
1220
  gdk_drawable_get_size (GDK_DRAWABLE (window), &width, &height);
 
1221
  if ((x < 0) || (x >= width) || (y < 0) || (y >= height))
 
1222
    return NULL;
 
1223
 
 
1224
  while (window) {
 
1225
    gint child_x = 0, child_y = 0;
 
1226
    GList *c, *children = gdk_window_peek_children (window);
 
1227
    GdkWindow *old_window = window;
 
1228
 
 
1229
    for (c = children; c; c = c->next) {
 
1230
      GdkWindow *child = (GdkWindow *) c->data;
 
1231
      gint wx, wy;
 
1232
 
 
1233
      gdk_drawable_get_size (GDK_DRAWABLE (child), &width, &height);
 
1234
      gdk_window_get_position (child, &wx, &wy);
 
1235
 
 
1236
      if (((x >= wx) && (x < (wx + width)) && (y >= wy)
 
1237
           && (y < (wy + height))) && (gdk_window_is_visible (child))) {
 
1238
        child_x = x - wx;
 
1239
        child_y = y - wy;
 
1240
        window = child;
 
1241
      }
 
1242
    }
 
1243
 
 
1244
    if (window == old_window)
 
1245
      break;
 
1246
 
 
1247
    x = child_x;
 
1248
    y = child_y;
 
1249
  }
 
1250
 
 
1251
  if (tx)
 
1252
    *tx = x;
 
1253
  if (ty)
 
1254
    *ty = y;
 
1255
 
 
1256
  return window;
 
1257
}
 
1258
 
 
1259
static void
 
1260
synth_crossing (GdkWindow * child,
 
1261
                gint x, gint y,
 
1262
                gint x_root, gint y_root,
 
1263
                guint32 time, gboolean in)
 
1264
{
 
1265
  GdkEventCrossing *crossing_event;
 
1266
  GdkEventType type = in ? GDK_ENTER_NOTIFY : GDK_LEAVE_NOTIFY;
 
1267
 
 
1268
  /* Send synthetic enter event */
 
1269
  crossing_event = (GdkEventCrossing *) gdk_event_new (type);
 
1270
  ((GdkEventAny *) crossing_event)->type = type;
 
1271
  ((GdkEventAny *) crossing_event)->window = g_object_ref (child);
 
1272
  ((GdkEventAny *) crossing_event)->send_event = FALSE;
 
1273
  crossing_event->subwindow = g_object_ref (child);
 
1274
  crossing_event->time = time;
 
1275
  crossing_event->x = x;
 
1276
  crossing_event->y = y;
 
1277
  crossing_event->x_root = x_root;
 
1278
  crossing_event->y_root = y_root;
 
1279
  crossing_event->mode = GDK_CROSSING_NORMAL;
 
1280
  crossing_event->detail = GDK_NOTIFY_UNKNOWN;
 
1281
  crossing_event->focus = FALSE;
 
1282
  crossing_event->state = 0;
 
1283
  gdk_event_put ((GdkEvent *) crossing_event);
 
1284
  gdk_event_free ((GdkEvent *) crossing_event);
 
1285
}
 
1286
 
 
1287
static gboolean
 
1288
hildon_pannable_area_button_press_cb (GtkWidget * widget,
 
1289
                                      GdkEventButton * event)
 
1290
{
 
1291
  gint x, y;
 
1292
  HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
 
1293
 
 
1294
  if ((!priv->enabled) || (event->button != 1) ||
 
1295
      ((event->time == priv->last_time) &&
 
1296
       (priv->last_type == 1)) || (gtk_bin_get_child (GTK_BIN (widget)) == NULL))
 
1297
    return TRUE;
 
1298
 
 
1299
  priv->scroll_indicator_event_interrupt = 1;
 
1300
  priv->scroll_delay_counter = SCROLLBAR_FADE_DELAY;
 
1301
 
 
1302
  if (!priv->scroll_indicator_timeout){
 
1303
    priv->scroll_indicator_timeout = gdk_threads_add_timeout
 
1304
      ((gint) (1000.0 / (gdouble) (priv->sps*2)),
 
1305
       (GSourceFunc) hildon_pannable_area_scroll_indicator_fade, widget);
 
1306
  }
 
1307
 
 
1308
  priv->last_time = event->time;
 
1309
  priv->last_type = 1;
 
1310
 
 
1311
  priv->scroll_to_x = -1;
 
1312
  priv->scroll_to_y = -1;
 
1313
 
 
1314
  if (priv->clicked && priv->child) {
 
1315
    /* Widget stole focus on last click, send crossing-out event */
 
1316
    synth_crossing (priv->child, 0, 0, event->x_root, event->y_root,
 
1317
                    event->time, FALSE);
 
1318
  }
 
1319
 
 
1320
  priv->x = event->x;
 
1321
  priv->y = event->y;
 
1322
  priv->ix = priv->x;
 
1323
  priv->iy = priv->y;
 
1324
 
 
1325
  /* Don't allow a click if we're still moving fast */
 
1326
  if ((ABS (priv->vel_x) <= (priv->vmax * priv->vfast_factor)) &&
 
1327
      (ABS (priv->vel_y) <= (priv->vmax * priv->vfast_factor)))
 
1328
    priv->child =
 
1329
      hildon_pannable_area_get_topmost (gtk_bin_get_child (GTK_BIN (widget))->window,
 
1330
                                        event->x, event->y, &x, &y);
 
1331
  else
 
1332
    priv->child = NULL;
 
1333
 
 
1334
  priv->clicked = TRUE;
 
1335
 
 
1336
  /* Stop scrolling on mouse-down (so you can flick, then hold to stop) */
 
1337
  priv->vel_x = 0;
 
1338
  priv->vel_y = 0;
 
1339
 
 
1340
  if (priv->child) {
 
1341
 
 
1342
    g_object_add_weak_pointer ((GObject *) priv->child,
 
1343
                               (gpointer) & priv->child);
 
1344
 
 
1345
    event = (GdkEventButton *) gdk_event_copy ((GdkEvent *) event);
 
1346
    event->x = x;
 
1347
    event->y = y;
 
1348
    priv->cx = x;
 
1349
    priv->cy = y;
 
1350
 
 
1351
    synth_crossing (priv->child, x, y, event->x_root,
 
1352
                    event->y_root, event->time, TRUE);
 
1353
 
 
1354
    /* Send synthetic click (button press/release) event */
 
1355
    ((GdkEventAny *) event)->window = g_object_ref (priv->child);
 
1356
 
 
1357
    gdk_event_put ((GdkEvent *) event);
 
1358
    gdk_event_free ((GdkEvent *) event);
 
1359
  } else
 
1360
    priv->child = NULL;
 
1361
 
 
1362
  return TRUE;
 
1363
}
 
1364
 
 
1365
static void
 
1366
hildon_pannable_area_refresh (HildonPannableArea * area)
 
1367
{
 
1368
  HildonPannableAreaPrivate *priv = area->priv;
 
1369
  gboolean prev_hscroll_visible, prev_vscroll_visible;
 
1370
 
 
1371
  if (!gtk_bin_get_child (GTK_BIN (area))) {
 
1372
    priv->vscroll_visible = FALSE;
 
1373
    priv->hscroll_visible = FALSE;
 
1374
    return;
 
1375
  }
 
1376
 
 
1377
  prev_hscroll_visible = priv->hscroll_visible;
 
1378
  prev_vscroll_visible = priv->vscroll_visible;
 
1379
 
 
1380
  switch (priv->hscrollbar_policy) {
 
1381
  case GTK_POLICY_ALWAYS:
 
1382
    priv->hscroll_visible = TRUE;
 
1383
    break;
 
1384
  case GTK_POLICY_NEVER:
 
1385
    priv->hscroll_visible = FALSE;
 
1386
    break;
 
1387
  default:
 
1388
    priv->hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
 
1389
                             priv->hadjust->page_size);
 
1390
  }
 
1391
 
 
1392
  switch (priv->vscrollbar_policy) {
 
1393
  case GTK_POLICY_ALWAYS:
 
1394
    priv->vscroll_visible = TRUE;
 
1395
    break;
 
1396
  case GTK_POLICY_NEVER:
 
1397
    priv->vscroll_visible = FALSE;
 
1398
    break;
 
1399
  default:
 
1400
    priv->vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
 
1401
                             priv->vadjust->page_size);
 
1402
  }
 
1403
 
 
1404
  /* Store the vscroll/hscroll areas for redrawing */
 
1405
  if (priv->vscroll_visible) {
 
1406
    GtkAllocation *allocation = &GTK_WIDGET (area)->allocation;
 
1407
    priv->vscroll_rect.x = allocation->x + allocation->width -
 
1408
      priv->area_width;
 
1409
    priv->vscroll_rect.y = allocation->y;
 
1410
    priv->vscroll_rect.width = priv->area_width;
 
1411
    priv->vscroll_rect.height = allocation->height -
 
1412
      (priv->hscroll_visible ? priv->area_width : 0);
 
1413
  }
 
1414
  if (priv->hscroll_visible) {
 
1415
    GtkAllocation *allocation = &GTK_WIDGET (area)->allocation;
 
1416
    priv->hscroll_rect.y = allocation->y + allocation->height -
 
1417
      priv->area_width;
 
1418
    priv->hscroll_rect.x = allocation->x;
 
1419
    priv->hscroll_rect.height = priv->area_width;
 
1420
    priv->hscroll_rect.width = allocation->width -
 
1421
      (priv->vscroll_visible ? priv->area_width : 0);
 
1422
  }
 
1423
 
 
1424
  if (GTK_WIDGET_DRAWABLE (area)) {
 
1425
    if (priv->hscroll_visible != prev_hscroll_visible) {
 
1426
      gtk_widget_queue_resize (GTK_WIDGET (area));
 
1427
    }
 
1428
 
 
1429
    if (priv->vscroll_visible != prev_vscroll_visible) {
 
1430
      gtk_widget_queue_resize (GTK_WIDGET (area));
 
1431
    }
 
1432
  }
 
1433
 
 
1434
}
 
1435
 
 
1436
/* Scroll by a particular amount (in pixels). Optionally, return if
 
1437
 * the scroll on a particular axis was successful.
 
1438
 */
 
1439
static void
 
1440
hildon_pannable_axis_scroll (HildonPannableArea *area,
 
1441
                             GtkAdjustment *adjust,
 
1442
                             gdouble *vel,
 
1443
                             gdouble inc,
 
1444
                             gint *overshooting,
 
1445
                             gint *overshot_dist,
 
1446
                             gdouble *scroll_to,
 
1447
                             gint overshoot_max,
 
1448
                             gboolean *s)
 
1449
{
 
1450
  gdouble dist;
 
1451
  HildonPannableAreaPrivate *priv = area->priv;
 
1452
 
 
1453
  dist = gtk_adjustment_get_value (adjust) - inc;
 
1454
 
 
1455
  /* Overshooting
 
1456
   * We use overshot_dist to define the distance of the current overshoot,
 
1457
   * and overshooting to define the direction/whether or not we are overshot
 
1458
   */
 
1459
  if (!(*overshooting)) {
 
1460
 
 
1461
    /* Initiation of the overshoot happens when the finger is released
 
1462
     * and the current position of the pannable contents are out of range
 
1463
     */
 
1464
    if (dist < adjust->lower) {
 
1465
      if (s) *s = FALSE;
 
1466
 
 
1467
      dist = adjust->lower;
 
1468
 
 
1469
      if (overshoot_max!=0) {
 
1470
        *overshooting = 1;
 
1471
        *scroll_to = -1;
 
1472
        *overshot_dist = CLAMP (*overshot_dist + *vel, 0, overshoot_max);
 
1473
        gtk_widget_queue_resize (GTK_WIDGET (area));
 
1474
      } else {
 
1475
        *vel = 0.0;
 
1476
      }
 
1477
    } else if (dist > adjust->upper - adjust->page_size) {
 
1478
      if (s) *s = FALSE;
 
1479
 
 
1480
      dist = adjust->upper - adjust->page_size;
 
1481
 
 
1482
      if (overshoot_max!=0) {
 
1483
        *overshooting = 1;
 
1484
        *scroll_to = -1;
 
1485
        *overshot_dist = CLAMP (*overshot_dist + *vel, -overshoot_max, 0);
 
1486
        gtk_widget_queue_resize (GTK_WIDGET (area));
 
1487
      } else {
 
1488
        *vel = 0.0;
 
1489
      }
 
1490
    } else {
 
1491
      if ((*scroll_to) != -1) {
 
1492
        if (((inc < 0)&&(*scroll_to <= dist))||
 
1493
            ((inc > 0)&&(*scroll_to >= dist))) {
 
1494
          dist = *scroll_to;
 
1495
          *scroll_to = -1;
 
1496
          *vel = 0;
 
1497
        }
 
1498
      }
 
1499
    }
 
1500
 
 
1501
    gtk_adjustment_set_value (adjust, dist);
 
1502
  } else {
 
1503
    if (!priv->clicked) {
 
1504
 
 
1505
      /* When the overshoot has started we continue for BOUNCE_STEPS more steps into the overshoot
 
1506
       * before we reverse direction. The deceleration factor is calculated based on
 
1507
       * the percentage distance from the first item with each iteration, therefore always
 
1508
       * returning us to the top/bottom most element
 
1509
       */
 
1510
      if (*overshot_dist > 0) {
 
1511
 
 
1512
        if ((*overshooting < BOUNCE_STEPS) && (*vel > 0)) {
 
1513
          (*overshooting)++;
 
1514
          *vel = (((gdouble)*overshot_dist)/overshoot_max) * (*vel);
 
1515
        } else if ((*overshooting >= BOUNCE_STEPS) && (*vel > 0)) {
 
1516
          *vel *= -1;
 
1517
          (*overshooting)--;
 
1518
        } else if ((*overshooting > 1) && (*vel < 0)) {
 
1519
          (*overshooting)--;
 
1520
          /* we add the MAX in order to avoid very small speeds */
 
1521
          *vel = MIN ((((gdouble)*overshot_dist)/overshoot_max) * (*vel), -10.0);
 
1522
        }
 
1523
 
 
1524
        *overshot_dist = CLAMP (*overshot_dist + *vel, 0, overshoot_max);
 
1525
 
 
1526
        gtk_widget_queue_resize (GTK_WIDGET (area));
 
1527
 
 
1528
      } else if (*overshot_dist < 0) {
 
1529
 
 
1530
        if ((*overshooting < BOUNCE_STEPS) && (*vel < 0)) {
 
1531
          (*overshooting)++;
 
1532
          *vel = (((gdouble)*overshot_dist)/overshoot_max) * (*vel) * -1;
 
1533
        } else if ((*overshooting >= BOUNCE_STEPS) && (*vel < 0)) {
 
1534
          *vel *= -1;
 
1535
          (*overshooting)--;
 
1536
        } else if ((*overshooting > 1) && (*vel > 0)) {
 
1537
          (*overshooting)--;
 
1538
          /* we add the MIN in order to avoid very small speeds */
 
1539
          *vel = MAX ((((gdouble)*overshot_dist)/overshoot_max) * (*vel) * -1, 10.0);
 
1540
        }
 
1541
 
 
1542
        *overshot_dist = CLAMP (*overshot_dist + (*vel), -overshoot_max, 0);
 
1543
 
 
1544
        gtk_widget_queue_resize (GTK_WIDGET (area));
 
1545
 
 
1546
      } else {
 
1547
        *overshooting = 0;
 
1548
        *vel = 0;
 
1549
        gtk_widget_queue_resize (GTK_WIDGET (area));
 
1550
      }
 
1551
    } else {
 
1552
      if (*overshot_dist > 0) {
 
1553
        *overshot_dist = CLAMP ((*overshot_dist) + inc, 0, overshoot_max);
 
1554
      } else if (*overshot_dist < 0) {
 
1555
        *overshot_dist = CLAMP ((*overshot_dist) + inc, -1 * overshoot_max, 0);
 
1556
      } else {
 
1557
        *overshooting = 0;
 
1558
        gtk_adjustment_set_value (adjust, dist);
 
1559
      }
 
1560
      gtk_widget_queue_resize (GTK_WIDGET (area));
 
1561
    }
 
1562
  }
 
1563
}
 
1564
 
 
1565
static void
 
1566
hildon_pannable_area_scroll (HildonPannableArea *area,
 
1567
                             gdouble x, gdouble y)
 
1568
{
 
1569
  gboolean sx, sy;
 
1570
  HildonPannableAreaPrivate *priv = area->priv;
 
1571
  gboolean hscroll_visible, vscroll_visible;
 
1572
 
 
1573
  if (gtk_bin_get_child (GTK_BIN (area)) == NULL)
 
1574
    return;
 
1575
 
 
1576
  vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
 
1577
             priv->vadjust->page_size);
 
1578
  hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
 
1579
             priv->hadjust->page_size);
 
1580
 
 
1581
  sx = TRUE;
 
1582
  sy = TRUE;
 
1583
 
 
1584
  if (vscroll_visible) {
 
1585
    hildon_pannable_axis_scroll (area, priv->vadjust, &priv->vel_y, y,
 
1586
                                 &priv->overshooting_y, &priv->overshot_dist_y,
 
1587
                                 &priv->scroll_to_y, priv->vovershoot_max, &sy);
 
1588
  } else {
 
1589
    priv->vel_y = 0;
 
1590
  }
 
1591
 
 
1592
  if (hscroll_visible) {
 
1593
    hildon_pannable_axis_scroll (area, priv->hadjust, &priv->vel_x, x,
 
1594
                                 &priv->overshooting_x, &priv->overshot_dist_x,
 
1595
                                 &priv->scroll_to_x, priv->hovershoot_max, &sx);
 
1596
  } else {
 
1597
    priv->vel_x = 0;
 
1598
  }
 
1599
 
 
1600
  /* If the scroll on a particular axis wasn't succesful, reset the
 
1601
   * initial scroll position to the new mouse co-ordinate. This means
 
1602
   * when you get to the top of the page, dragging down works immediately.
 
1603
   */
 
1604
  if (!sx) {
 
1605
    priv->x = priv->ex;
 
1606
  }
 
1607
 
 
1608
  if (!sy) {
 
1609
    priv->y = priv->ey;
 
1610
  }
 
1611
 
 
1612
}
 
1613
 
 
1614
static gboolean
 
1615
hildon_pannable_area_timeout (HildonPannableArea * area)
 
1616
{
 
1617
  HildonPannableAreaPrivate *priv = area->priv;
 
1618
 
 
1619
  if ((!priv->enabled) || (priv->mode == HILDON_PANNABLE_AREA_MODE_PUSH)) {
 
1620
    priv->idle_id = 0;
 
1621
 
 
1622
    return FALSE;
 
1623
  }
 
1624
 
 
1625
  if (!priv->clicked) {
 
1626
    /* Decelerate gradually when pointer is raised */
 
1627
    if ((!priv->overshot_dist_y) &&
 
1628
        (!priv->overshot_dist_x)) {
 
1629
 
 
1630
      /* in case we move to a specific point do not decelerate when arriving */
 
1631
      if ((priv->scroll_to_x != -1)||(priv->scroll_to_y != -1)) {
 
1632
 
 
1633
        if (ABS (priv->vel_x) >= 1.5) {
 
1634
          priv->vel_x *= priv->decel;
 
1635
        }
 
1636
 
 
1637
        if (ABS (priv->vel_y) >= 1.5) {
 
1638
          priv->vel_y *= priv->decel;
 
1639
        }
 
1640
 
 
1641
      } else {
 
1642
        priv->vel_x *= priv->decel;
 
1643
        priv->vel_y *= priv->decel;
 
1644
 
 
1645
        if ((ABS (priv->vel_x) < 1.0) && (ABS (priv->vel_y) < 1.0)) {
 
1646
          priv->vel_x = 0;
 
1647
          priv->vel_y = 0;
 
1648
          priv->idle_id = 0;
 
1649
 
 
1650
          return FALSE;
 
1651
        }
 
1652
      }
 
1653
    }
 
1654
  } else if (priv->mode == HILDON_PANNABLE_AREA_MODE_AUTO) {
 
1655
    priv->idle_id = 0;
 
1656
 
 
1657
    return FALSE;
 
1658
  }
 
1659
 
 
1660
  hildon_pannable_area_scroll (area, priv->vel_x, priv->vel_y);
 
1661
 
 
1662
  return TRUE;
 
1663
}
 
1664
 
 
1665
static void
 
1666
hildon_pannable_area_calculate_velocity (gdouble *vel,
 
1667
                                         gdouble delta,
 
1668
                                         gdouble dist,
 
1669
                                         gdouble vmax,
 
1670
                                         guint sps)
 
1671
{
 
1672
  gdouble rawvel;
 
1673
 
 
1674
  if (ABS (dist) >= 0.00001) {
 
1675
    rawvel = ((dist / ABS (delta)) *
 
1676
              (gdouble) sps) * FORCE;
 
1677
    *vel = *vel * (1 - SMOOTH_FACTOR) +
 
1678
      rawvel * SMOOTH_FACTOR;
 
1679
    *vel = *vel > 0 ? MIN (*vel, vmax)
 
1680
      : MAX (dist, -1 * vmax);
 
1681
  }
 
1682
}
 
1683
 
 
1684
static gboolean
 
1685
hildon_pannable_area_motion_notify_cb (GtkWidget * widget,
 
1686
                                       GdkEventMotion * event)
 
1687
{
 
1688
  HildonPannableArea *area = HILDON_PANNABLE_AREA (widget);
 
1689
  HildonPannableAreaPrivate *priv = area->priv;
 
1690
  gint dnd_threshold;
 
1691
  gdouble x, y;
 
1692
  gdouble delta;
 
1693
 
 
1694
  if (gtk_bin_get_child (GTK_BIN (widget)) == NULL)
 
1695
    return TRUE;
 
1696
 
 
1697
  if ((!priv->enabled) || (!priv->clicked) ||
 
1698
      ((event->time == priv->last_time) && (priv->last_type == 2))) {
 
1699
    gdk_window_get_pointer (widget->window, NULL, NULL, 0);
 
1700
    return TRUE;
 
1701
  }
 
1702
 
 
1703
  if (priv->last_type == 1) {
 
1704
    priv->first_drag = TRUE;
 
1705
  }
 
1706
 
 
1707
  /* Only start the scroll if the mouse cursor passes beyond the
 
1708
   * DnD threshold for dragging.
 
1709
   */
 
1710
  g_object_get (G_OBJECT (gtk_settings_get_default ()),
 
1711
                "gtk-dnd-drag-threshold", &dnd_threshold, NULL);
 
1712
  x = event->x - priv->x;
 
1713
  y = event->y - priv->y;
 
1714
 
 
1715
  if (priv->first_drag && (!priv->moved) &&
 
1716
      ((ABS (x) > (dnd_threshold+DND_THRESHOLD_INC))
 
1717
       || (ABS (y) > (dnd_threshold+DND_THRESHOLD_INC)))) {
 
1718
    priv->moved = TRUE;
 
1719
    x = 0;
 
1720
    y = 0;
 
1721
 
 
1722
    if (priv->first_drag) {
 
1723
 
 
1724
      if (ABS (priv->iy - event->y) >=
 
1725
          ABS (priv->ix - event->x)) {
 
1726
        gboolean vscroll_visible;
 
1727
 
 
1728
        g_signal_emit (area,
 
1729
                       pannable_area_signals[VERTICAL_MOVEMENT],
 
1730
                       0, (priv->iy > event->y) ?
 
1731
                       HILDON_MOVEMENT_UP :
 
1732
                       HILDON_MOVEMENT_DOWN,
 
1733
                       (gdouble)priv->ix, (gdouble)priv->iy);
 
1734
 
 
1735
        vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
 
1736
                   priv->vadjust->page_size);
 
1737
 
 
1738
        if (!((vscroll_visible)&&
 
1739
              (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT)))
 
1740
          priv->moved = FALSE;
 
1741
 
 
1742
      } else {
 
1743
        gboolean hscroll_visible;
 
1744
 
 
1745
        g_signal_emit (area,
 
1746
                       pannable_area_signals[HORIZONTAL_MOVEMENT],
 
1747
                       0, (priv->ix > event->x) ?
 
1748
                       HILDON_MOVEMENT_LEFT :
 
1749
                       HILDON_MOVEMENT_RIGHT,
 
1750
                       (gdouble)priv->ix, (gdouble)priv->iy);
 
1751
 
 
1752
        hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
 
1753
                           priv->hadjust->page_size);
 
1754
 
 
1755
        if (!((hscroll_visible)&&
 
1756
              (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ)))
 
1757
          priv->moved = FALSE;
 
1758
      }
 
1759
    }
 
1760
 
 
1761
    priv->first_drag = FALSE;
 
1762
 
 
1763
    if ((priv->mode != HILDON_PANNABLE_AREA_MODE_PUSH) &&
 
1764
        (priv->mode != HILDON_PANNABLE_AREA_MODE_AUTO)) {
 
1765
 
 
1766
      if (!priv->idle_id)
 
1767
        priv->idle_id = gdk_threads_add_timeout ((gint)
 
1768
                                                 (1000.0 / (gdouble) priv->sps),
 
1769
                                                 (GSourceFunc)
 
1770
                                                 hildon_pannable_area_timeout, area);
 
1771
    }
 
1772
  }
 
1773
 
 
1774
  if (priv->moved) {
 
1775
    switch (priv->mode) {
 
1776
    case HILDON_PANNABLE_AREA_MODE_PUSH:
 
1777
      /* Scroll by the amount of pixels the cursor has moved
 
1778
       * since the last motion event.
 
1779
       */
 
1780
      hildon_pannable_area_scroll (area, x, y);
 
1781
      priv->x = event->x;
 
1782
      priv->y = event->y;
 
1783
      break;
 
1784
    case HILDON_PANNABLE_AREA_MODE_ACCEL:
 
1785
      /* Set acceleration relative to the initial click */
 
1786
      priv->ex = event->x;
 
1787
      priv->ey = event->y;
 
1788
      priv->vel_x = ((x > 0) ? 1 : -1) *
 
1789
        (((ABS (x) /
 
1790
           (gdouble) widget->allocation.width) *
 
1791
          (priv->vmax - priv->vmin)) + priv->vmin);
 
1792
      priv->vel_y = ((y > 0) ? 1 : -1) *
 
1793
        (((ABS (y) /
 
1794
           (gdouble) widget->allocation.height) *
 
1795
          (priv->vmax - priv->vmin)) + priv->vmin);
 
1796
      break;
 
1797
    case HILDON_PANNABLE_AREA_MODE_AUTO:
 
1798
 
 
1799
      delta = event->time - priv->last_time;
 
1800
 
 
1801
      if (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT) {
 
1802
        gdouble dist = event->y - priv->y;
 
1803
 
 
1804
        hildon_pannable_area_calculate_velocity (&priv->vel_y,
 
1805
                                                 delta,
 
1806
                                                 dist,
 
1807
                                                 priv->vmax,
 
1808
                                                 priv->sps);
 
1809
      } else {
 
1810
        y = 0;
 
1811
        priv->vel_y = 0;
 
1812
      }
 
1813
 
 
1814
 
 
1815
      if (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ) {
 
1816
        gdouble dist = event->x - priv->x;
 
1817
 
 
1818
        hildon_pannable_area_calculate_velocity (&priv->vel_x,
 
1819
                                                 delta,
 
1820
                                                 dist,
 
1821
                                                 priv->vmax,
 
1822
                                                 priv->sps);
 
1823
      } else {
 
1824
        x = 0;
 
1825
        priv->vel_x = 0;
 
1826
      }
 
1827
 
 
1828
      hildon_pannable_area_scroll (area, x, y);
 
1829
 
 
1830
      if (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ)
 
1831
        priv->x = event->x;
 
1832
      if (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT)
 
1833
        priv->y = event->y;
 
1834
 
 
1835
      break;
 
1836
 
 
1837
    default:
 
1838
      break;
 
1839
    }
 
1840
  }
 
1841
 
 
1842
  if (priv->child) {
 
1843
    /* Send motion notify to child */
 
1844
    priv->last_time = event->time;
 
1845
    priv->last_type = 2;
 
1846
    event = (GdkEventMotion *) gdk_event_copy ((GdkEvent *) event);
 
1847
    event->x = priv->cx + (event->x - priv->ix);
 
1848
    event->y = priv->cy + (event->y - priv->iy);
 
1849
    event->window = g_object_ref (priv->child);
 
1850
    gdk_event_put ((GdkEvent *) event);
 
1851
    gdk_event_free ((GdkEvent *) event);
 
1852
  }
 
1853
 
 
1854
  gdk_window_get_pointer (widget->window, NULL, NULL, 0);
 
1855
 
 
1856
  return TRUE;
 
1857
}
 
1858
 
 
1859
static gboolean
 
1860
hildon_pannable_area_button_release_cb (GtkWidget * widget,
 
1861
                                        GdkEventButton * event)
 
1862
{
 
1863
  HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
 
1864
  gint x, y;
 
1865
  GdkWindow *child;
 
1866
 
 
1867
  if (gtk_bin_get_child (GTK_BIN (widget)) == NULL)
 
1868
    return TRUE;
 
1869
 
 
1870
  priv->scroll_indicator_event_interrupt = 0;
 
1871
  priv->scroll_delay_counter = SCROLLBAR_FADE_DELAY;
 
1872
 
 
1873
  if ((ABS (priv->vel_y) > 1.0)||
 
1874
      (ABS (priv->vel_x) > 1.0)) {
 
1875
    priv->scroll_indicator_alpha = 1.0;
 
1876
  }
 
1877
 
 
1878
  if (!priv->scroll_indicator_timeout) {
 
1879
    priv->scroll_indicator_timeout = gdk_threads_add_timeout
 
1880
      ((gint) (1000.0 / (gdouble) priv->sps),
 
1881
       (GSourceFunc) hildon_pannable_area_scroll_indicator_fade, widget);
 
1882
  }
 
1883
 
 
1884
  if ((!priv->clicked) || (!priv->enabled) || (event->button != 1) ||
 
1885
      ((event->time == priv->last_time) && (priv->last_type == 3)))
 
1886
    return TRUE;
 
1887
 
 
1888
  priv->clicked = FALSE;
 
1889
 
 
1890
  if (priv->mode == HILDON_PANNABLE_AREA_MODE_AUTO ||
 
1891
      priv->mode == HILDON_PANNABLE_AREA_MODE_ACCEL) {
 
1892
 
 
1893
    /* If overshoot has been initiated with a finger down, on release set max speed */
 
1894
    if (priv->overshot_dist_y != 0) {
 
1895
      priv->overshooting_y = BOUNCE_STEPS; /* Hack to stop a bounce in the finger down case */
 
1896
      priv->vel_y = priv->vmax;
 
1897
    }
 
1898
 
 
1899
    if (priv->overshot_dist_x != 0) {
 
1900
      priv->overshooting_x = BOUNCE_STEPS; /* Hack to stop a bounce in the finger down case */
 
1901
      priv->vel_x = priv->vmax;
 
1902
    }
 
1903
 
 
1904
    if (!priv->idle_id)
 
1905
      priv->idle_id = gdk_threads_add_timeout ((gint) (1000.0 / (gdouble) priv->sps),
 
1906
                                               (GSourceFunc)
 
1907
                                               hildon_pannable_area_timeout, widget);
 
1908
  }
 
1909
 
 
1910
  priv->last_time = event->time;
 
1911
  priv->last_type = 3;
 
1912
 
 
1913
  if (!priv->child) {
 
1914
    priv->moved = FALSE;
 
1915
    return TRUE;
 
1916
  }
 
1917
 
 
1918
  child =
 
1919
    hildon_pannable_area_get_topmost (gtk_bin_get_child (GTK_BIN (widget))->window,
 
1920
                                      event->x, event->y, &x, &y);
 
1921
 
 
1922
  event = (GdkEventButton *) gdk_event_copy ((GdkEvent *) event);
 
1923
  event->x = x;
 
1924
  event->y = y;
 
1925
 
 
1926
  /* Leave the widget if we've moved - This doesn't break selection,
 
1927
   * but stops buttons from being clicked.
 
1928
   */
 
1929
  if ((child != priv->child) || (priv->moved)) {
 
1930
    /* Send synthetic leave event */
 
1931
    synth_crossing (priv->child, x, y, event->x_root,
 
1932
                    event->y_root, event->time, FALSE);
 
1933
    /* Send synthetic button release event */
 
1934
    ((GdkEventAny *) event)->window = g_object_ref (priv->child);
 
1935
    gdk_event_put ((GdkEvent *) event);
 
1936
  } else {
 
1937
    /* Send synthetic button release event */
 
1938
    ((GdkEventAny *) event)->window = g_object_ref (child);
 
1939
    gdk_event_put ((GdkEvent *) event);
 
1940
    /* Send synthetic leave event */
 
1941
    synth_crossing (priv->child, x, y, event->x_root,
 
1942
                    event->y_root, event->time, FALSE);
 
1943
  }
 
1944
  g_object_remove_weak_pointer ((GObject *) priv->child,
 
1945
                                (gpointer) & priv->child);
 
1946
 
 
1947
  priv->moved = FALSE;
 
1948
  gdk_event_free ((GdkEvent *) event);
 
1949
 
 
1950
  return TRUE;
 
1951
}
 
1952
 
 
1953
/* utility event handler */
 
1954
static gboolean
 
1955
hildon_pannable_area_scroll_cb (GtkWidget *widget,
 
1956
                                GdkEventScroll *event)
 
1957
{
 
1958
  GtkAdjustment *adj = NULL;
 
1959
  HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
 
1960
 
 
1961
  if ((!priv->enabled) ||
 
1962
      (gtk_bin_get_child (GTK_BIN (widget)) == NULL))
 
1963
    return TRUE;
 
1964
 
 
1965
  priv->scroll_indicator_event_interrupt = 0;
 
1966
  priv->scroll_indicator_alpha = 1.0;
 
1967
  priv->scroll_delay_counter = SCROLLBAR_FADE_DELAY + 20;
 
1968
 
 
1969
  if (!priv->scroll_indicator_timeout) {
 
1970
    priv->scroll_indicator_timeout = gdk_threads_add_timeout
 
1971
      ((gint) (1000.0 / (gdouble) (priv->sps*2)),
 
1972
       (GSourceFunc) hildon_pannable_area_scroll_indicator_fade, widget);
 
1973
  }
 
1974
 
 
1975
  /* Stop inertial scrolling */
 
1976
  if (priv->idle_id) {
 
1977
    priv->vel_x = 0.0;
 
1978
    priv->vel_y = 0.0;
 
1979
    priv->overshooting_x = 0;
 
1980
    priv->overshooting_y = 0;
 
1981
 
 
1982
    if ((priv->overshot_dist_x>0)||(priv->overshot_dist_y>0)) {
 
1983
      priv->overshot_dist_x = 0;
 
1984
      priv->overshot_dist_y = 0;
 
1985
 
 
1986
      gtk_widget_queue_resize (GTK_WIDGET (widget));
 
1987
    }
 
1988
 
 
1989
    g_source_remove (priv->idle_id);
 
1990
    priv->idle_id = 0;
 
1991
  }
 
1992
 
 
1993
  if (event->direction == GDK_SCROLL_UP || event->direction == GDK_SCROLL_DOWN)
 
1994
    adj = priv->vadjust;
 
1995
  else
 
1996
    adj = priv->hadjust;
 
1997
 
 
1998
  if (adj)
 
1999
    {
 
2000
      gdouble delta, new_value;
 
2001
 
 
2002
      /* from gtkrange.c calculate delta*/
 
2003
      delta = pow (adj->page_size, 2.0 / 3.0);
 
2004
 
 
2005
      if (event->direction == GDK_SCROLL_UP ||
 
2006
          event->direction == GDK_SCROLL_LEFT)
 
2007
        delta = - delta;
 
2008
 
 
2009
      new_value = CLAMP (adj->value + delta, adj->lower, adj->upper - adj->page_size);
 
2010
 
 
2011
      gtk_adjustment_set_value (adj, new_value);
 
2012
    }
 
2013
 
 
2014
  return TRUE;
 
2015
}
 
2016
 
 
2017
static void
 
2018
hildon_pannable_area_child_mapped (GtkWidget *widget,
 
2019
                                   GdkEvent  *event,
 
2020
                                   gpointer user_data)
 
2021
{
 
2022
  HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (user_data)->priv;
 
2023
 
 
2024
  if (priv->event_window != NULL && priv->enabled)
 
2025
    gdk_window_raise (priv->event_window);
 
2026
}
 
2027
 
 
2028
static void
 
2029
hildon_pannable_area_add (GtkContainer *container, GtkWidget *child)
 
2030
{
 
2031
  HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (container)->priv;
 
2032
 
 
2033
  g_return_if_fail (gtk_bin_get_child (GTK_BIN (container)) == NULL);
 
2034
 
 
2035
  gtk_widget_set_parent (child, GTK_WIDGET (container));
 
2036
  GTK_BIN (container)->child = child;
 
2037
 
 
2038
  g_signal_connect_after (child, "map-event",
 
2039
                          G_CALLBACK (hildon_pannable_area_child_mapped),
 
2040
                          container);
 
2041
 
 
2042
  if (!gtk_widget_set_scroll_adjustments (child, priv->hadjust, priv->vadjust)) {
 
2043
    g_warning ("%s: cannot add non scrollable widget, "
 
2044
               "wrap it in a viewport", __FUNCTION__);
 
2045
  }
 
2046
}
 
2047
 
 
2048
static void
 
2049
hildon_pannable_area_remove (GtkContainer *container, GtkWidget *child)
 
2050
{
 
2051
  g_return_if_fail (HILDON_IS_PANNABLE_AREA (container));
 
2052
  g_return_if_fail (child != NULL);
 
2053
  g_return_if_fail (gtk_bin_get_child (GTK_BIN (container)) == child);
 
2054
 
 
2055
  gtk_widget_set_scroll_adjustments (child, NULL, NULL);
 
2056
 
 
2057
  g_signal_handlers_disconnect_by_func (GTK_WIDGET (child),
 
2058
                                        G_CALLBACK (hildon_pannable_area_child_mapped),
 
2059
                                        container);
 
2060
 
 
2061
  /* chain parent class handler to remove child */
 
2062
  GTK_CONTAINER_CLASS (hildon_pannable_area_parent_class)->remove (container, child);
 
2063
}
 
2064
 
 
2065
static void
 
2066
hildon_pannable_calculate_vel_factor (HildonPannableArea * self)
 
2067
{
 
2068
  HildonPannableAreaPrivate *priv = self->priv;
 
2069
  gfloat fct = 0;
 
2070
  gfloat fct_i = 1;
 
2071
  gint i, n;
 
2072
 
 
2073
  n = ceil (priv->sps * priv->scroll_time);
 
2074
 
 
2075
  for (i = 0; i < n && fct_i >= RATIO_TOLERANCE; i++) {
 
2076
    fct_i *= priv->decel;
 
2077
    fct += fct_i;
 
2078
  }
 
2079
 
 
2080
    priv->vel_factor = fct;
 
2081
}
 
2082
 
 
2083
/**
 
2084
 * hildon_pannable_area_new:
 
2085
 *
 
2086
 * Create a new pannable area widget
 
2087
 *
 
2088
 * Returns: the newly created #HildonPannableArea
 
2089
 */
 
2090
 
 
2091
GtkWidget *
 
2092
hildon_pannable_area_new (void)
 
2093
{
 
2094
  return g_object_new (HILDON_TYPE_PANNABLE_AREA, NULL);
 
2095
}
 
2096
 
 
2097
/**
 
2098
 * hildon_pannable_area_new_full:
 
2099
 * @mode: #HildonPannableAreaMode
 
2100
 * @enabled: Value for the enabled property
 
2101
 * @vel_min: Value for the velocity-min property
 
2102
 * @vel_max: Value for the velocity-max property
 
2103
 * @decel: Value for the deceleration property
 
2104
 * @sps: Value for the sps property
 
2105
 *
 
2106
 * Create a new #HildonPannableArea widget and set various properties
 
2107
 *
 
2108
 * returns: the newly create #HildonPannableArea
 
2109
 */
 
2110
 
 
2111
GtkWidget *
 
2112
hildon_pannable_area_new_full (gint mode, gboolean enabled,
 
2113
                               gdouble vel_min, gdouble vel_max,
 
2114
                               gdouble decel, guint sps)
 
2115
{
 
2116
  return g_object_new (HILDON_TYPE_PANNABLE_AREA,
 
2117
                       "mode", mode,
 
2118
                       "enabled", enabled,
 
2119
                       "velocity_min", vel_min,
 
2120
                       "velocity_max", vel_max,
 
2121
                       "deceleration", decel, "sps", sps, NULL);
 
2122
}
 
2123
 
 
2124
/**
 
2125
 * hildon_pannable_area_add_with_viewport:
 
2126
 * @area: A #HildonPannableArea
 
2127
 * @child: Child widget to add to the viewport
 
2128
 *
 
2129
 * Convenience function used to add a child to a #GtkViewport, and add the
 
2130
 * viewport to the scrolled window.
 
2131
 */
 
2132
 
 
2133
void
 
2134
hildon_pannable_area_add_with_viewport (HildonPannableArea * area,
 
2135
                                        GtkWidget * child)
 
2136
{
 
2137
  GtkBin *bin;
 
2138
  GtkWidget *viewport;
 
2139
 
 
2140
  g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
 
2141
  g_return_if_fail (GTK_IS_WIDGET (child));
 
2142
  g_return_if_fail (child->parent == NULL);
 
2143
 
 
2144
  bin = GTK_BIN (area);
 
2145
 
 
2146
  if (bin->child != NULL)
 
2147
    {
 
2148
      g_return_if_fail (GTK_IS_VIEWPORT (bin->child));
 
2149
      g_return_if_fail (GTK_BIN (bin->child)->child == NULL);
 
2150
 
 
2151
      viewport = bin->child;
 
2152
    }
 
2153
  else
 
2154
    {
 
2155
      HildonPannableAreaPrivate *priv = area->priv;
 
2156
 
 
2157
      viewport = gtk_viewport_new (priv->hadjust,
 
2158
                                   priv->vadjust);
 
2159
      gtk_viewport_set_shadow_type (GTK_VIEWPORT (viewport), GTK_SHADOW_NONE);
 
2160
      gtk_container_add (GTK_CONTAINER (area), viewport);
 
2161
    }
 
2162
 
 
2163
  gtk_widget_show (viewport);
 
2164
  gtk_container_add (GTK_CONTAINER (viewport), child);
 
2165
}
 
2166
 
 
2167
/**
 
2168
 * hildon_pannable_area_scroll_to:
 
2169
 * @area: A #HildonPannableArea.
 
2170
 * @x: The x coordinate of the destination point or -1 to ignore this axis.
 
2171
 * @y: The y coordinate of the destination point or -1 to ignore this axis.
 
2172
 *
 
2173
 * Smoothly scrolls @area to ensure that (@x, @y) is a visible point
 
2174
 * on the widget. To move in only one coordinate, you must set the other one
 
2175
 * to -1. Notice that, in %HILDON_PANNABLE_AREA_MODE_PUSH mode, this function
 
2176
 * works just like hildon_pannable_area_jump_to().
 
2177
 *
 
2178
 * This function is useful if you need to present the user with a particular
 
2179
 * element inside a scrollable widget, like #GtkTreeView. For instance,
 
2180
 * the following example shows how to scroll inside a #GtkTreeView to
 
2181
 * make visible an item, indicated by the #GtkTreeIter @iter.
 
2182
 *
 
2183
 * <example>
 
2184
 * <programlisting>
 
2185
 *  GtkTreePath *path;
 
2186
 *  GdkRectangle *rect;
 
2187
 *  <!-- -->
 
2188
 *  path = gtk_tree_model_get_path (model, &amp;iter);
 
2189
 *  gtk_tree_view_get_background_area (GTK_TREE_VIEW (treeview),
 
2190
 *                                     path, NULL, &amp;rect);
 
2191
 *  gtk_tree_view_convert_bin_window_to_tree_coords (GTK_TREE_VIEW (treeview),
 
2192
 *                                                   0, rect.y, NULL, &amp;y);
 
2193
 *  hildon_pannable_area_scroll_to (panarea, -1, y);
 
2194
 *  gtk_tree_path_free (path);
 
2195
 * </programlisting>
 
2196
 * </example>
 
2197
 *
 
2198
 * If you want to present a child widget in simpler scenarios,
 
2199
 * use hildon_pannable_area_scroll_to_child() instead.
 
2200
 *
 
2201
 * There is a precondition to this function: the widget must be
 
2202
 * already realized. Check the hildon_pannable_area_jump_to_child() for
 
2203
 * more tips regarding how to call this function during
 
2204
 * initialization.
 
2205
 **/
 
2206
void
 
2207
hildon_pannable_area_scroll_to (HildonPannableArea *area,
 
2208
                                const gint x, const gint y)
 
2209
{
 
2210
  HildonPannableAreaPrivate *priv;
 
2211
  gint width, height;
 
2212
  gint dist_x, dist_y;
 
2213
  gboolean hscroll_visible, vscroll_visible;
 
2214
 
 
2215
  g_return_if_fail (GTK_WIDGET_REALIZED (area));
 
2216
  g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
 
2217
 
 
2218
  priv = area->priv;
 
2219
 
 
2220
  vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
 
2221
             priv->vadjust->page_size);
 
2222
  hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
 
2223
             priv->hadjust->page_size);
 
2224
 
 
2225
  if (((!vscroll_visible)&&(!hscroll_visible)) ||
 
2226
      (x == -1 && y == -1)) {
 
2227
    return;
 
2228
  }
 
2229
 
 
2230
  if (priv->mode == HILDON_PANNABLE_AREA_MODE_PUSH)
 
2231
    hildon_pannable_area_jump_to (area, x, y);
 
2232
 
 
2233
  width = priv->hadjust->upper - priv->hadjust->lower;
 
2234
  height = priv->vadjust->upper - priv->vadjust->lower;
 
2235
 
 
2236
  g_return_if_fail (x < width || y < height);
 
2237
 
 
2238
  if (x > -1) {
 
2239
    priv->scroll_to_x = x - priv->hadjust->page_size/2;
 
2240
    dist_x = priv->scroll_to_x - priv->hadjust->value;
 
2241
    if (dist_x == 0) {
 
2242
      priv->scroll_to_x = -1;
 
2243
    } else {
 
2244
      priv->vel_x = - dist_x/priv->vel_factor;
 
2245
    }
 
2246
  } else {
 
2247
    priv->scroll_to_x = -1;
 
2248
  }
 
2249
 
 
2250
  if (y > -1) {
 
2251
    priv->scroll_to_y = y - priv->vadjust->page_size/2;
 
2252
    dist_y = priv->scroll_to_y - priv->vadjust->value;
 
2253
    if (dist_y == 0) {
 
2254
      priv->scroll_to_y = -1;
 
2255
    } else {
 
2256
      priv->vel_y = - dist_y/priv->vel_factor;
 
2257
    }
 
2258
  } else {
 
2259
    priv->scroll_to_y = y;
 
2260
  }
 
2261
 
 
2262
  if ((priv->scroll_to_y == -1) && (priv->scroll_to_y == -1)) {
 
2263
    return;
 
2264
  }
 
2265
 
 
2266
  priv->scroll_indicator_alpha = 1.0;
 
2267
 
 
2268
  if (!priv->scroll_indicator_timeout)
 
2269
    priv->scroll_indicator_timeout = gdk_threads_add_timeout
 
2270
      ((gint) (1000.0 / (gdouble) priv->sps),
 
2271
       (GSourceFunc) hildon_pannable_area_scroll_indicator_fade, area);
 
2272
 
 
2273
  if (!priv->idle_id)
 
2274
    priv->idle_id = gdk_threads_add_timeout ((gint) (1000.0 / (gdouble) priv->sps),
 
2275
                                             (GSourceFunc)
 
2276
                                             hildon_pannable_area_timeout, area);
 
2277
}
 
2278
 
 
2279
/**
 
2280
 * hildon_pannable_area_jump_to:
 
2281
 * @area: A #HildonPannableArea.
 
2282
 * @x: The x coordinate of the destination point or -1 to ignore this axis.
 
2283
 * @y: The y coordinate of the destination point or -1 to ignore this axis.
 
2284
 *
 
2285
 * Jumps the position of @area to ensure that (@x, @y) is a visible
 
2286
 * point in the widget. In order to move in only one coordinate, you
 
2287
 * must set the other one to -1. See hildon_pannable_area_scroll_to()
 
2288
 * function for an example of how to calculate the position of
 
2289
 * children in scrollable widgets like #GtkTreeview.
 
2290
 *
 
2291
 * There is a precondition to this function: the widget must be
 
2292
 * already realized. Check the hildon_pannable_area_jump_to_child() for
 
2293
 * more tips regarding how to call this function during
 
2294
 * initialization.
 
2295
 **/
 
2296
void
 
2297
hildon_pannable_area_jump_to (HildonPannableArea *area,
 
2298
                              const gint x, const gint y)
 
2299
{
 
2300
  HildonPannableAreaPrivate *priv;
 
2301
  gint width, height;
 
2302
 
 
2303
  g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
 
2304
  g_return_if_fail (GTK_WIDGET_REALIZED (area));
 
2305
  g_return_if_fail (x >= -1 && y >= -1);
 
2306
 
 
2307
  if (x == -1 && y == -1) {
 
2308
    return;
 
2309
  }
 
2310
 
 
2311
  priv = area->priv;
 
2312
 
 
2313
  width = priv->hadjust->upper - priv->hadjust->lower;
 
2314
  height = priv->vadjust->upper - priv->vadjust->lower;
 
2315
 
 
2316
  g_return_if_fail (x < width || y < height);
 
2317
 
 
2318
  if (x != -1) {
 
2319
    gdouble jump_to = x - priv->hadjust->page_size/2;
 
2320
 
 
2321
    if (jump_to > priv->hadjust->upper - priv->hadjust->page_size) {
 
2322
      jump_to = priv->hadjust->upper - priv->hadjust->page_size;
 
2323
    }
 
2324
 
 
2325
    gtk_adjustment_set_value (priv->hadjust, jump_to);
 
2326
  }
 
2327
 
 
2328
  if (y != -1) {
 
2329
    gdouble jump_to =  y - priv->vadjust->page_size/2;
 
2330
 
 
2331
    if (jump_to > priv->vadjust->upper - priv->vadjust->page_size) {
 
2332
      jump_to = priv->vadjust->upper - priv->vadjust->page_size;
 
2333
    }
 
2334
 
 
2335
    gtk_adjustment_set_value (priv->vadjust, jump_to);
 
2336
  }
 
2337
 
 
2338
  priv->scroll_indicator_alpha = 1.0;
 
2339
 
 
2340
  if (priv->scroll_indicator_timeout) {
 
2341
    g_source_remove (priv->scroll_indicator_timeout);
 
2342
    priv->scroll_indicator_timeout = 0;
 
2343
  }
 
2344
 
 
2345
  if (priv->idle_id) {
 
2346
    priv->vel_x = 0.0;
 
2347
    priv->vel_y = 0.0;
 
2348
    priv->overshooting_x = 0;
 
2349
    priv->overshooting_y = 0;
 
2350
 
 
2351
    if ((priv->overshot_dist_x>0)||(priv->overshot_dist_y>0)) {
 
2352
      priv->overshot_dist_x = 0;
 
2353
      priv->overshot_dist_y = 0;
 
2354
 
 
2355
      gtk_widget_queue_resize (GTK_WIDGET (area));
 
2356
    }
 
2357
 
 
2358
    g_source_remove (priv->idle_id);
 
2359
    priv->idle_id = 0;
 
2360
  }
 
2361
}
 
2362
 
 
2363
/**
 
2364
 * hildon_pannable_area_scroll_to_child:
 
2365
 * @area: A #HildonPannableArea.
 
2366
 * @child: A #GtkWidget, descendant of @area.
 
2367
 *
 
2368
 * Smoothly scrolls until @child is visible inside @area. @child must
 
2369
 * be a descendant of @area. If you need to scroll inside a scrollable
 
2370
 * widget, e.g., #GtkTreeview, see hildon_pannable_area_scroll_to().
 
2371
 *
 
2372
 * There is a precondition to this function: the widget must be
 
2373
 * already realized. Check the hildon_pannable_area_jump_to_child() for
 
2374
 * more tips regarding how to call this function during
 
2375
 * initialization.
 
2376
 **/
 
2377
void
 
2378
hildon_pannable_area_scroll_to_child (HildonPannableArea *area, GtkWidget *child)
 
2379
{
 
2380
  GtkWidget *bin_child;
 
2381
  gint x, y;
 
2382
 
 
2383
  g_return_if_fail (GTK_WIDGET_REALIZED (area));
 
2384
  g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
 
2385
  g_return_if_fail (GTK_IS_WIDGET (child));
 
2386
  g_return_if_fail (gtk_widget_is_ancestor (child, GTK_WIDGET (area)));
 
2387
 
 
2388
  if (GTK_BIN (area)->child == NULL)
 
2389
    return;
 
2390
 
 
2391
  /* We need to get to check the child of the inside the area */
 
2392
  bin_child = GTK_BIN (area)->child;
 
2393
 
 
2394
  /* we check if we added a viewport */
 
2395
  if (GTK_IS_VIEWPORT (bin_child)) {
 
2396
    bin_child = GTK_BIN (bin_child)->child;
 
2397
  }
 
2398
 
 
2399
  if (gtk_widget_translate_coordinates (child, bin_child, 0, 0, &x, &y))
 
2400
    hildon_pannable_area_scroll_to (area, x, y);
 
2401
}
 
2402
 
 
2403
/**
 
2404
 * hildon_pannable_area_jump_to_child:
 
2405
 * @area: A #HildonPannableArea.
 
2406
 * @child: A #GtkWidget, descendant of @area.
 
2407
 *
 
2408
 * Jumps to make sure @child is visible inside @area. @child must
 
2409
 * be a descendant of @area. If you want to move inside a scrollable
 
2410
 * widget, like, #GtkTreeview, see hildon_pannable_area_scroll_to().
 
2411
 *
 
2412
 * There is a precondition to this function: the widget must be
 
2413
 * already realized. You can control if the widget is ready with the
 
2414
 * GTK_WIDGET_REALIZED macro. If you want to call this function during
 
2415
 * the initialization process of the widget do it inside a callback to
 
2416
 * the ::realize signal, using g_signal_connect_after() function.
 
2417
 **/
 
2418
void
 
2419
hildon_pannable_area_jump_to_child (HildonPannableArea *area, GtkWidget *child)
 
2420
{
 
2421
  GtkWidget *bin_child;
 
2422
  gint x, y;
 
2423
 
 
2424
  g_return_if_fail (GTK_WIDGET_REALIZED (area));
 
2425
  g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
 
2426
  g_return_if_fail (GTK_IS_WIDGET (child));
 
2427
  g_return_if_fail (gtk_widget_is_ancestor (child, GTK_WIDGET (area)));
 
2428
 
 
2429
  if (gtk_bin_get_child (GTK_BIN (area)) == NULL)
 
2430
    return;
 
2431
 
 
2432
  /* We need to get to check the child of the inside the area */
 
2433
  bin_child = gtk_bin_get_child (GTK_BIN (area));
 
2434
 
 
2435
  /* we check if we added a viewport */
 
2436
  if (GTK_IS_VIEWPORT (bin_child)) {
 
2437
    bin_child = gtk_bin_get_child (GTK_BIN (bin_child));
 
2438
  }
 
2439
 
 
2440
  if (gtk_widget_translate_coordinates (child, bin_child, 0, 0, &x, &y))
 
2441
    hildon_pannable_area_jump_to (area, x, y);
 
2442
}
 
2443
 
 
2444
/**
 
2445
 * hildon_pannable_get_child_widget_at:
 
2446
 * @area: A #HildonPannableArea.
 
2447
 * @x: horizontal coordinate of the point
 
2448
 * @y: vertical coordinate of the point
 
2449
 *
 
2450
 * Get the widget at the point (x, y) inside the pannable area. In
 
2451
 * case no widget found it returns NULL.
 
2452
 *
 
2453
 * returns: the #GtkWidget if we find a widget, NULL in any other case
 
2454
 **/
 
2455
GtkWidget*
 
2456
hildon_pannable_get_child_widget_at (HildonPannableArea *area,
 
2457
                                     gdouble x, gdouble y)
 
2458
{
 
2459
  GdkWindow *window = NULL;
 
2460
  GtkWidget *child_widget = NULL;
 
2461
 
 
2462
  window = hildon_pannable_area_get_topmost
 
2463
    (gtk_bin_get_child (GTK_BIN (area))->window,
 
2464
     x, y, NULL, NULL);
 
2465
 
 
2466
  gdk_window_get_user_data (window, (gpointer) &child_widget);
 
2467
 
 
2468
  return child_widget;
 
2469
}