Make sure that all timeouts in HildonBanner are removed
[hildon] / hildon / hildon-pannable-area.c
1 /*
2  * This file is a part of hildon
3  *
4  * Copyright (C) 2008 Nokia Corporation, all rights reserved.
5  *
6  * Contact: Rodrigo Novo <rodrigo.novo@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 is a container widget that can be "panned" (scrolled)
30  * up and down using the touchscreen with fingers. The widget has no scrollbars,
31  * but it rather shows small scroll indicators to give an idea of the part of the
32  * content that is visible at a time. The scroll indicators appear when a dragging
33  * motion is started on the pannable area.
34  *
35  * The scrolling is "kinetic", meaning the motion can be "flicked" and it will
36  * continue from the initial motion by gradually slowing down to an eventual stop.
37  * The motion can also be stopped immediately by pressing the touchscreen over the
38  * pannable area.
39  */
40
41 #undef HILDON_DISABLE_DEPRECATED
42
43 #include <math.h>
44 #if USE_CAIRO_SCROLLBARS == 1
45 #include <cairo.h>
46 #endif
47 #include <gdk/gdkx.h>
48
49 #include "hildon-pannable-area.h"
50 #include "hildon-marshalers.h"
51 #include "hildon-enum-types.h"
52
53 #define USE_CAIRO_SCROLLBARS 0
54
55 #define SCROLL_BAR_MIN_SIZE 5
56 #define RATIO_TOLERANCE 0.000001
57 #define SCROLL_FADE_TIMEOUT 100
58 #define MOTION_EVENTS_PER_SECOND 25
59 #define CURSOR_STOPPED_TIMEOUT 200
60 #define MAX_SPEED_THRESHOLD 280
61 #define PANNABLE_MAX_WIDTH 788
62 #define PANNABLE_MAX_HEIGHT 378
63 #define ACCEL_FACTOR 27
64 #define MIN_ACCEL_THRESHOLD 40
65 #define FAST_CLICK 125
66
67 G_DEFINE_TYPE (HildonPannableArea, hildon_pannable_area, GTK_TYPE_BIN)
68
69 #define PANNABLE_AREA_PRIVATE(o)                                \
70   (G_TYPE_INSTANCE_GET_PRIVATE ((o), HILDON_TYPE_PANNABLE_AREA, \
71                                 HildonPannableAreaPrivate))
72
73 struct _HildonPannableAreaPrivate {
74   HildonPannableAreaMode mode;
75   HildonMovementMode mov_mode;
76   GdkWindow *event_window;
77   gdouble x;            /* Used to store mouse co-ordinates of the first or */
78   gdouble y;            /* previous events in a press-motion pair */
79   gdouble ex;           /* Used to store mouse co-ordinates of the last */
80   gdouble ey;           /* motion event in acceleration mode */
81   gboolean enabled;
82   gboolean button_pressed;
83   guint32 last_time;    /* Last event time, to stop infinite loops */
84   guint32 last_press_time;
85   gint last_type;
86   gboolean last_in;
87   gboolean moved;
88   gdouble vmin;
89   gdouble vmax;
90   gdouble vmax_overshooting;
91   gdouble accel_vel_x;
92   gdouble accel_vel_y;
93   gdouble vfast_factor;
94   gdouble decel;
95   gdouble drag_inertia;
96   gdouble scroll_time;
97   gdouble vel_factor;
98   guint sps;
99   guint panning_threshold;
100   guint scrollbar_fade_delay;
101   guint bounce_steps;
102   guint force;
103   guint direction_error_margin;
104   gdouble vel_x;
105   gdouble vel_y;
106   gdouble old_vel_x;
107   gdouble old_vel_y;
108   GdkWindow *child;
109   gint child_width;
110   gint child_height;
111   gint ix;                      /* Initial click mouse co-ordinates */
112   gint iy;
113   gint cx;                      /* Initial click child window mouse co-ordinates */
114   gint cy;
115   guint idle_id;
116   gdouble scroll_to_x;
117   gdouble scroll_to_y;
118   gdouble motion_x;
119   gdouble motion_y;
120   gint overshot_dist_x;
121   gint overshot_dist_y;
122   gint overshooting_y;
123   gint overshooting_x;
124   gdouble scroll_indicator_alpha;
125   gint motion_event_scroll_timeout;
126   gint scroll_indicator_timeout;
127   gint scroll_indicator_event_interrupt;
128   gint scroll_delay_counter;
129   gint vovershoot_max;
130   gint hovershoot_max;
131   gboolean initial_hint;
132   gboolean initial_effect;
133   gboolean low_friction_mode;
134   gboolean first_drag;
135
136   gboolean size_request_policy;
137   gboolean hscroll_visible;
138   gboolean vscroll_visible;
139   GdkRectangle hscroll_rect;
140   GdkRectangle vscroll_rect;
141   guint indicator_width;
142
143   GtkAdjustment *hadjust;
144   GtkAdjustment *vadjust;
145   gint x_offset;
146   gint y_offset;
147
148   GtkPolicyType vscrollbar_policy;
149   GtkPolicyType hscrollbar_policy;
150
151   GdkGC *scrollbars_gc;
152   GdkColor scroll_color;
153
154   gboolean center_on_child_focus;
155   gboolean center_on_child_focus_pending;
156 };
157
158 /*signals*/
159 enum {
160   HORIZONTAL_MOVEMENT,
161   VERTICAL_MOVEMENT,
162   PANNING_STARTED,
163   PANNING_FINISHED,
164   LAST_SIGNAL
165 };
166
167 static guint pannable_area_signals [LAST_SIGNAL] = { 0 };
168
169 enum {
170   PROP_ENABLED = 1,
171   PROP_MODE,
172   PROP_MOVEMENT_MODE,
173   PROP_VELOCITY_MIN,
174   PROP_VELOCITY_MAX,
175   PROP_VEL_MAX_OVERSHOOTING,
176   PROP_VELOCITY_FAST_FACTOR,
177   PROP_DECELERATION,
178   PROP_DRAG_INERTIA,
179   PROP_SPS,
180   PROP_PANNING_THRESHOLD,
181   PROP_SCROLLBAR_FADE_DELAY,
182   PROP_BOUNCE_STEPS,
183   PROP_FORCE,
184   PROP_DIRECTION_ERROR_MARGIN,
185   PROP_VSCROLLBAR_POLICY,
186   PROP_HSCROLLBAR_POLICY,
187   PROP_VOVERSHOOT_MAX,
188   PROP_HOVERSHOOT_MAX,
189   PROP_SCROLL_TIME,
190   PROP_INITIAL_HINT,
191   PROP_LOW_FRICTION_MODE,
192   PROP_SIZE_REQUEST_POLICY,
193   PROP_HADJUSTMENT,
194   PROP_VADJUSTMENT,
195   PROP_CENTER_ON_CHILD_FOCUS,
196   PROP_LAST
197 };
198
199 static void hildon_pannable_area_class_init (HildonPannableAreaClass * klass);
200 static void hildon_pannable_area_init (HildonPannableArea * area);
201 static void hildon_pannable_area_get_property (GObject * object,
202                                                guint property_id,
203                                                GValue * value,
204                                                GParamSpec * pspec);
205 static void hildon_pannable_area_set_property (GObject * object,
206                                                guint property_id,
207                                                const GValue * value,
208                                                GParamSpec * pspec);
209 static void hildon_pannable_area_remove_timeouts (GtkWidget * widget);
210 static void hildon_pannable_area_dispose (GObject * object);
211 static void hildon_pannable_area_realize (GtkWidget * widget);
212 static void hildon_pannable_area_unrealize (GtkWidget * widget);
213 static void hildon_pannable_area_size_request (GtkWidget * widget,
214                                                GtkRequisition * requisition);
215 static void hildon_pannable_area_size_allocate (GtkWidget * widget,
216                                                 GtkAllocation * allocation);
217 static void hildon_pannable_area_child_allocate_calculate (GtkWidget * widget,
218                                                            GtkAllocation * allocation,
219                                                            GtkAllocation * child_allocation);
220 static void hildon_pannable_area_style_set (GtkWidget * widget,
221                                             GtkStyle * previous_style);
222 static void hildon_pannable_area_map (GtkWidget * widget);
223 static void hildon_pannable_area_unmap (GtkWidget * widget);
224 static void hildon_pannable_area_grab_notify (GtkWidget *widget,
225                                               gboolean was_grabbed,
226                                               gpointer user_data);
227 #if USE_CAIRO_SCROLLBARS == 1
228 static void rgb_from_gdkcolor (GdkColor *color, gdouble *r, gdouble *g, gdouble *b);
229 #else /* USE_CAIRO_SCROLLBARS */
230 static void tranparency_color (GdkColor *color,
231                                GdkColor colora,
232                                GdkColor colorb,
233                                gdouble transparency);
234 #endif /* USE_CAIRO_SCROLLBARS */
235 static void hildon_pannable_draw_vscroll (GtkWidget * widget,
236                                           GdkColor *back_color,
237                                           GdkColor *scroll_color);
238 static void hildon_pannable_draw_hscroll (GtkWidget * widget,
239                                           GdkColor *back_color,
240                                           GdkColor *scroll_color);
241 static void hildon_pannable_area_initial_effect (GtkWidget * widget);
242 static void hildon_pannable_area_redraw (HildonPannableArea * area);
243 static void hildon_pannable_area_launch_fade_timeout (HildonPannableArea * area,
244                                                       gdouble alpha);
245 static void hildon_pannable_area_adjust_value_changed (HildonPannableArea * area,
246                                                        gpointer data);
247 static void hildon_pannable_area_adjust_changed (HildonPannableArea * area,
248                                                  gpointer data);
249 static gboolean hildon_pannable_area_scroll_indicator_fade(HildonPannableArea * area);
250 static gboolean hildon_pannable_area_expose_event (GtkWidget * widget,
251                                                    GdkEventExpose * event);
252 static GdkWindow * hildon_pannable_area_get_topmost (GdkWindow * window,
253                                                      gint x, gint y,
254                                                      gint * tx, gint * ty,
255                                                      GdkEventMask mask);
256 static void synth_crossing (GdkWindow * child,
257                             gint x, gint y,
258                             gint x_root, gint y_root,
259                             guint32 time, gboolean in);
260 static gboolean hildon_pannable_area_button_press_cb (GtkWidget * widget,
261                                                       GdkEventButton * event);
262 static void hildon_pannable_area_refresh (HildonPannableArea * area);
263 static gboolean hildon_pannable_area_check_scrollbars (HildonPannableArea * area);
264 static void hildon_pannable_axis_scroll (HildonPannableArea *area,
265                                          GtkAdjustment *adjust,
266                                          gdouble *vel,
267                                          gdouble inc,
268                                          gint *overshooting,
269                                          gint *overshot_dist,
270                                          gdouble *scroll_to,
271                                          gint overshoot_max,
272                                          gboolean *s);
273 static void hildon_pannable_area_scroll (HildonPannableArea *area,
274                                          gdouble x, gdouble y);
275 static gboolean hildon_pannable_area_timeout (HildonPannableArea * area);
276 static void hildon_pannable_area_calculate_velocity (gdouble *vel,
277                                                      gdouble delta,
278                                                      gdouble dist,
279                                                      gdouble vmax,
280                                                      gdouble drag_inertia,
281                                                      gdouble force,
282                                                      guint sps);
283 static gboolean hildon_pannable_area_motion_event_scroll_timeout (HildonPannableArea *area);
284 static void hildon_pannable_area_motion_event_scroll (HildonPannableArea *area,
285                                                       gdouble x, gdouble y);
286 static void hildon_pannable_area_check_move (HildonPannableArea *area,
287                                              GdkEventMotion * event,
288                                              gdouble *x,
289                                              gdouble *y);
290 static void hildon_pannable_area_handle_move (HildonPannableArea *area,
291                                               GdkEventMotion * event,
292                                               gdouble *x,
293                                               gdouble *y);
294 static gboolean hildon_pannable_area_motion_notify_cb (GtkWidget * widget,
295                                                        GdkEventMotion * event);
296 static gboolean hildon_pannable_leave_notify_event (GtkWidget *widget,
297                                                     GdkEventCrossing *event);
298 static gboolean hildon_pannable_area_button_release_cb (GtkWidget * widget,
299                                                         GdkEventButton * event);
300 static gboolean hildon_pannable_area_scroll_cb (GtkWidget *widget,
301                                                 GdkEventScroll *event);
302 static void hildon_pannable_area_child_mapped (GtkWidget *widget,
303                                                GdkEvent  *event,
304                                                gpointer user_data);
305 static void hildon_pannable_area_add (GtkContainer *container, GtkWidget *child);
306 static void hildon_pannable_area_remove (GtkContainer *container, GtkWidget *child);
307 static void hildon_pannable_calculate_vel_factor (HildonPannableArea * self);
308 static void hildon_pannable_area_set_focus_child (GtkContainer *container,
309                                                  GtkWidget *child);
310 static void hildon_pannable_area_center_on_child_focus (HildonPannableArea *area);
311
312
313 static void
314 hildon_pannable_area_class_init (HildonPannableAreaClass * klass)
315 {
316   GObjectClass *object_class = G_OBJECT_CLASS (klass);
317   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
318   GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
319
320
321   g_type_class_add_private (klass, sizeof (HildonPannableAreaPrivate));
322
323   object_class->get_property = hildon_pannable_area_get_property;
324   object_class->set_property = hildon_pannable_area_set_property;
325   object_class->dispose = hildon_pannable_area_dispose;
326
327   widget_class->realize = hildon_pannable_area_realize;
328   widget_class->unrealize = hildon_pannable_area_unrealize;
329   widget_class->map = hildon_pannable_area_map;
330   widget_class->unmap = hildon_pannable_area_unmap;
331   widget_class->size_request = hildon_pannable_area_size_request;
332   widget_class->size_allocate = hildon_pannable_area_size_allocate;
333   widget_class->expose_event = hildon_pannable_area_expose_event;
334   widget_class->style_set = hildon_pannable_area_style_set;
335   widget_class->button_press_event = hildon_pannable_area_button_press_cb;
336   widget_class->button_release_event = hildon_pannable_area_button_release_cb;
337   widget_class->motion_notify_event = hildon_pannable_area_motion_notify_cb;
338   widget_class->leave_notify_event = hildon_pannable_leave_notify_event;
339   widget_class->scroll_event = hildon_pannable_area_scroll_cb;
340
341   container_class->add = hildon_pannable_area_add;
342   container_class->remove = hildon_pannable_area_remove;
343   container_class->set_focus_child = hildon_pannable_area_set_focus_child;
344
345   klass->horizontal_movement = NULL;
346   klass->vertical_movement = NULL;
347
348   g_object_class_install_property (object_class,
349                                    PROP_ENABLED,
350                                    g_param_spec_boolean ("enabled",
351                                                          "Enabled",
352                                                          "Enable or disable finger-scroll.",
353                                                          TRUE,
354                                                          G_PARAM_READWRITE |
355                                                          G_PARAM_CONSTRUCT));
356
357   g_object_class_install_property (object_class,
358                                    PROP_VSCROLLBAR_POLICY,
359                                    g_param_spec_enum ("vscrollbar_policy",
360                                                       "vscrollbar policy",
361                                                       "Visual policy of the vertical scrollbar",
362                                                       GTK_TYPE_POLICY_TYPE,
363                                                       GTK_POLICY_AUTOMATIC,
364                                                       G_PARAM_READWRITE |
365                                                       G_PARAM_CONSTRUCT));
366
367   g_object_class_install_property (object_class,
368                                    PROP_HSCROLLBAR_POLICY,
369                                    g_param_spec_enum ("hscrollbar_policy",
370                                                       "hscrollbar policy",
371                                                       "Visual policy of the horizontal scrollbar",
372                                                       GTK_TYPE_POLICY_TYPE,
373                                                       GTK_POLICY_AUTOMATIC,
374                                                       G_PARAM_READWRITE |
375                                                       G_PARAM_CONSTRUCT));
376
377   g_object_class_install_property (object_class,
378                                    PROP_MODE,
379                                    g_param_spec_enum ("mode",
380                                                       "Scroll mode",
381                                                       "Change the finger-scrolling mode.",
382                                                       HILDON_TYPE_PANNABLE_AREA_MODE,
383                                                       HILDON_PANNABLE_AREA_MODE_AUTO,
384                                                       G_PARAM_READWRITE |
385                                                       G_PARAM_CONSTRUCT));
386
387   g_object_class_install_property (object_class,
388                                    PROP_MOVEMENT_MODE,
389                                    g_param_spec_flags ("mov_mode",
390                                                        "Scroll movement mode",
391                                                        "Controls if the widget can scroll vertically, horizontally or both",
392                                                        HILDON_TYPE_MOVEMENT_MODE,
393                                                        HILDON_MOVEMENT_MODE_VERT,
394                                                        G_PARAM_READWRITE |
395                                                        G_PARAM_CONSTRUCT));
396
397   g_object_class_install_property (object_class,
398                                    PROP_VELOCITY_MIN,
399                                    g_param_spec_double ("velocity_min",
400                                                         "Minimum scroll velocity",
401                                                         "Minimum distance the child widget should scroll "
402                                                         "per 'frame', in pixels per frame.",
403                                                         0, G_MAXDOUBLE, 10,
404                                                         G_PARAM_READWRITE |
405                                                         G_PARAM_CONSTRUCT));
406
407   g_object_class_install_property (object_class,
408                                    PROP_VELOCITY_MAX,
409                                    g_param_spec_double ("velocity_max",
410                                                         "Maximum scroll velocity",
411                                                         "Maximum distance the child widget should scroll "
412                                                         "per 'frame', in pixels per frame.",
413                                                         0, G_MAXDOUBLE, 3500,
414                                                         G_PARAM_READWRITE |
415                                                         G_PARAM_CONSTRUCT));
416
417   g_object_class_install_property (object_class,
418                                    PROP_VEL_MAX_OVERSHOOTING,
419                                    g_param_spec_double ("velocity_overshooting_max",
420                                                         "Maximum scroll velocity when overshooting",
421                                                         "Maximum distance the child widget should scroll "
422                                                         "per 'frame', in pixels per frame when it overshoots after hitting the edge.",
423                                                         0, G_MAXDOUBLE, 130,
424                                                         G_PARAM_READWRITE |
425                                                         G_PARAM_CONSTRUCT));
426
427   g_object_class_install_property (object_class,
428                                    PROP_VELOCITY_FAST_FACTOR,
429                                    g_param_spec_double ("velocity_fast_factor",
430                                                         "Fast velocity factor",
431                                                         "Minimum velocity that is considered 'fast': "
432                                                         "children widgets won't receive button presses. "
433                                                         "Expressed as a fraction of the maximum velocity.",
434                                                         0, 1, 0.01,
435                                                         G_PARAM_READWRITE |
436                                                         G_PARAM_CONSTRUCT));
437
438   g_object_class_install_property (object_class,
439                                    PROP_DECELERATION,
440                                    g_param_spec_double ("deceleration",
441                                                         "Deceleration multiplier",
442                                                         "The multiplier used when decelerating when in "
443                                                         "acceleration scrolling mode.",
444                                                         0, 1.0, 0.85,
445                                                         G_PARAM_READWRITE |
446                                                         G_PARAM_CONSTRUCT));
447
448   g_object_class_install_property (object_class,
449                                    PROP_DRAG_INERTIA,
450                                    g_param_spec_double ("drag_inertia",
451                                                         "Inertia of the cursor dragging",
452                                                         "Percentage of the calculated speed in each moment we are are going to use"
453                                                         "to calculate the launch speed, the other part would be the speed"
454                                                         "calculated previously",
455                                                         0, 1.0, 0.85,
456                                                         G_PARAM_READWRITE |
457                                                         G_PARAM_CONSTRUCT));
458
459   g_object_class_install_property (object_class,
460                                    PROP_SPS,
461                                    g_param_spec_uint ("sps",
462                                                       "Scrolls per second",
463                                                       "Amount of scroll events to generate per second.",
464                                                       0, G_MAXUINT, 20,
465                                                       G_PARAM_READWRITE |
466                                                       G_PARAM_CONSTRUCT));
467
468   g_object_class_install_property (object_class,
469                                    PROP_PANNING_THRESHOLD,
470                                    g_param_spec_uint ("panning_threshold",
471                                                       "Threshold to consider a motion event an scroll",
472                                                       "Amount of pixels to consider a motion event an scroll, if it is less"
473                                                       "it is a click detected incorrectly by the touch screen.",
474                                                       0, G_MAXUINT, 25,
475                                                       G_PARAM_READWRITE |
476                                                       G_PARAM_CONSTRUCT));
477
478   g_object_class_install_property (object_class,
479                                    PROP_SCROLLBAR_FADE_DELAY,
480                                    g_param_spec_uint ("scrollbar_fade_delay",
481                                                       "Time before starting to fade the scrollbar",
482                                                       "Time the scrollbar is going to be visible if the widget is not in"
483                                                       "action in miliseconds",
484                                                       0, G_MAXUINT, 1000,
485                                                       G_PARAM_READWRITE |
486                                                       G_PARAM_CONSTRUCT));
487
488   g_object_class_install_property (object_class,
489                                    PROP_BOUNCE_STEPS,
490                                    g_param_spec_uint ("bounce_steps",
491                                                       "Bounce steps",
492                                                       "Number of steps that is going to be used to bounce when hitting the"
493                                                       "edge, the rubberband effect depends on it",
494                                                       0, G_MAXUINT, 3,
495                                                       G_PARAM_READWRITE |
496                                                       G_PARAM_CONSTRUCT));
497
498   g_object_class_install_property (object_class,
499                                    PROP_FORCE,
500                                    g_param_spec_uint ("force",
501                                                       "Multiplier of the calculated speed",
502                                                       "Force applied to the movement, multiplies the calculated speed of the"
503                                                       "user movement the cursor in the screen",
504                                                       0, G_MAXUINT, 50,
505                                                       G_PARAM_READWRITE |
506                                                       G_PARAM_CONSTRUCT));
507
508   g_object_class_install_property (object_class,
509                                    PROP_DIRECTION_ERROR_MARGIN,
510                                    g_param_spec_uint ("direction_error_margin",
511                                                       "Margin in the direction detection",
512                                                       "After detecting the direction of the movement (horizontal or"
513                                                       "vertical), we can add this margin of error to allow the movement in"
514                                                       "the other direction even apparently it is not",
515                                                       0, G_MAXUINT, 10,
516                                                       G_PARAM_READWRITE |
517                                                       G_PARAM_CONSTRUCT));
518
519   g_object_class_install_property (object_class,
520                                    PROP_VOVERSHOOT_MAX,
521                                    g_param_spec_int ("vovershoot_max",
522                                                      "Vertical overshoot distance",
523                                                      "Space we allow the widget to pass over its vertical limits when"
524                                                      "hitting the edges, set 0 in order to deactivate overshooting.",
525                                                      0, G_MAXINT, 150,
526                                                      G_PARAM_READWRITE |
527                                                      G_PARAM_CONSTRUCT));
528
529   g_object_class_install_property (object_class,
530                                    PROP_HOVERSHOOT_MAX,
531                                    g_param_spec_int ("hovershoot_max",
532                                                      "Horizontal overshoot distance",
533                                                      "Space we allow the widget to pass over its horizontal limits when"
534                                                      "hitting the edges, set 0 in order to deactivate overshooting.",
535                                                      0, G_MAXINT, 150,
536                                                      G_PARAM_READWRITE |
537                                                      G_PARAM_CONSTRUCT));
538
539   g_object_class_install_property (object_class,
540                                    PROP_SCROLL_TIME,
541                                    g_param_spec_double ("scroll_time",
542                                                         "Time to scroll to a position",
543                                                         "The time to scroll to a position when calling the hildon_pannable_scroll_to function",
544                                                         0.0, 20.0, 1.0,
545                                                         G_PARAM_READWRITE |
546                                                         G_PARAM_CONSTRUCT));
547
548   g_object_class_install_property (object_class,
549                                    PROP_INITIAL_HINT,
550                                    g_param_spec_boolean ("initial-hint",
551                                                          "Initial hint",
552                                                          "Whether to hint the user about the pannability of the container.",
553                                                          TRUE,
554                                                          G_PARAM_READWRITE |
555                                                          G_PARAM_CONSTRUCT));
556
557   g_object_class_install_property (object_class,
558                                    PROP_LOW_FRICTION_MODE,
559                                    g_param_spec_boolean ("low-friction-mode",
560                                                          "Do not decelerate the initial velocity",
561                                                          "Avoid decelerating the panning movement, like no friction, the widget"
562                                                          "will stop in the edges or if the user clicks.",
563                                                          FALSE,
564                                                          G_PARAM_READWRITE |
565                                                          G_PARAM_CONSTRUCT));
566
567   g_object_class_install_property (object_class,
568                                    PROP_SIZE_REQUEST_POLICY,
569                                    g_param_spec_enum ("size-request-policy",
570                                                       "Size Requisition policy",
571                                                       "Controls the size request policy of the widget",
572                                                       HILDON_TYPE_SIZE_REQUEST_POLICY,
573                                                       HILDON_SIZE_REQUEST_MINIMUM,
574                                                       G_PARAM_READWRITE|
575                                                       G_PARAM_CONSTRUCT));
576
577   g_object_class_install_property (object_class,
578                                    PROP_HADJUSTMENT,
579                                    g_param_spec_object ("hadjustment",
580                                                         "Horizontal Adjustment",
581                                                         "The GtkAdjustment for the horizontal position",
582                                                         GTK_TYPE_ADJUSTMENT,
583                                                         G_PARAM_READABLE));
584   g_object_class_install_property (object_class,
585                                    PROP_VADJUSTMENT,
586                                    g_param_spec_object ("vadjustment",
587                                                         "Vertical Adjustment",
588                                                         "The GtkAdjustment for the vertical position",
589                                                         GTK_TYPE_ADJUSTMENT,
590                                                         G_PARAM_READABLE));
591
592   g_object_class_install_property (object_class,
593                                    PROP_CENTER_ON_CHILD_FOCUS,
594                                    g_param_spec_boolean ("center-on-child-focus",
595                                                          "Center on the child with the focus",
596                                                          "Whether to center the pannable on the child that receives the focus.",
597                                                          FALSE,
598                                                          G_PARAM_READWRITE |
599                                                          G_PARAM_CONSTRUCT));
600
601
602   gtk_widget_class_install_style_property (widget_class,
603                                            g_param_spec_uint
604                                            ("indicator-width",
605                                             "Width of the scroll indicators",
606                                             "Pixel width used to draw the scroll indicators.",
607                                             0, G_MAXUINT, 8,
608                                             G_PARAM_READWRITE));
609
610  /**
611    * HildonPannableArea::horizontal-movement:
612    * @hildonpannable: the object which received the signal
613    * @direction: the direction of the movement #HILDON_MOVEMENT_LEFT or #HILDON_MOVEMENT_RIGHT
614    * @initial_x: the x coordinate of the point where the user clicked to start the movement
615    * @initial_y: the y coordinate of the point where the user clicked to start the movement
616    *
617    * The horizontal-movement signal is emitted when the pannable area
618    * detects a horizontal movement. The detection does not mean the
619    * widget is going to move (i.e. maybe the children are smaller
620    * horizontally than the screen).
621    *
622    * Since: 2.2
623    */
624   pannable_area_signals[HORIZONTAL_MOVEMENT] =
625     g_signal_new ("horizontal_movement",
626                   G_TYPE_FROM_CLASS (object_class),
627                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
628                   G_STRUCT_OFFSET (HildonPannableAreaClass, horizontal_movement),
629                   NULL, NULL,
630                   _hildon_marshal_VOID__INT_DOUBLE_DOUBLE,
631                   G_TYPE_NONE, 3,
632                   G_TYPE_INT,
633                   G_TYPE_DOUBLE,
634                   G_TYPE_DOUBLE);
635
636   /**
637    * HildonPannableArea::vertical-movement:
638    * @hildonpannable: the object which received the signal
639    * @direction: the direction of the movement #HILDON_MOVEMENT_UP or #HILDON_MOVEMENT_DOWN
640    * @initial_x: the x coordinate of the point where the user clicked to start the movement
641    * @initial_y: the y coordinate of the point where the user clicked to start the movement
642    *
643    * The vertical-movement signal is emitted when the pannable area
644    * detects a vertical movement. The detection does not mean the
645    * widget is going to move (i.e. maybe the children are smaller
646    * vertically than the screen).
647    *
648    * Since: 2.2
649    */
650   pannable_area_signals[VERTICAL_MOVEMENT] =
651     g_signal_new ("vertical_movement",
652                   G_TYPE_FROM_CLASS (object_class),
653                   G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
654                   G_STRUCT_OFFSET (HildonPannableAreaClass, vertical_movement),
655                   NULL, NULL,
656                   _hildon_marshal_VOID__INT_DOUBLE_DOUBLE,
657                   G_TYPE_NONE, 3,
658                   G_TYPE_INT,
659                   G_TYPE_DOUBLE,
660                   G_TYPE_DOUBLE);
661
662  /**
663    * HildonPannableArea::panning-started:
664    * @hildonpannable: the pannable area object that is going to start
665    * the panning
666    *
667    * This signal is emitted before the panning starts. Applications
668    * can return %TRUE to avoid the panning. The main difference with
669    * the vertical-movement and horizontal-movement signals is those
670    * gesture signals are launched no matter if the widget is going to
671    * move, this signal means the widget is going to start moving. It
672    * could even happen that the widget moves and there was no gesture
673    * (i.e. click meanwhile the pannable is overshooting).
674    *
675    * Returns: %TRUE to stop the panning launch. %FALSE to continue
676    * with it.
677    *
678    * Since: 2.2
679    */
680   pannable_area_signals[PANNING_STARTED] =
681     g_signal_new ("panning-started",
682                   G_TYPE_FROM_CLASS (object_class),
683                   0,
684                   0,
685                   NULL, NULL,
686                   _hildon_marshal_BOOLEAN__VOID,
687                   G_TYPE_BOOLEAN, 0);
688
689  /**
690    * HildonPannableArea::panning-finished:
691    * @hildonpannable: the pannable area object that finished the
692    * panning
693    *
694    * This signal is emitted after the kinetic panning has
695    * finished.
696    *
697    * Since: 2.2
698    */
699   pannable_area_signals[PANNING_FINISHED] =
700     g_signal_new ("panning-finished",
701                   G_TYPE_FROM_CLASS (object_class),
702                   0,
703                   0,
704                   NULL, NULL,
705                   _hildon_marshal_VOID__VOID,
706                   G_TYPE_NONE, 0);
707
708 }
709
710 static void
711 hildon_pannable_area_init (HildonPannableArea * area)
712 {
713   HildonPannableAreaPrivate *priv = PANNABLE_AREA_PRIVATE (area);
714
715   GTK_WIDGET_UNSET_FLAGS (area, GTK_NO_WINDOW);
716
717   area->priv = priv;
718
719   priv->moved = FALSE;
720   priv->button_pressed = FALSE;
721   priv->last_time = 0;
722   priv->last_press_time = 0;
723   priv->last_type = 0;
724   priv->vscroll_visible = TRUE;
725   priv->hscroll_visible = TRUE;
726   priv->indicator_width = 6;
727   priv->overshot_dist_x = 0;
728   priv->overshot_dist_y = 0;
729   priv->overshooting_y = 0;
730   priv->overshooting_x = 0;
731   priv->accel_vel_x = 0;
732   priv->accel_vel_y = 0;
733   priv->idle_id = 0;
734   priv->vel_x = 0;
735   priv->vel_y = 0;
736   priv->old_vel_x = 0;
737   priv->old_vel_y = 0;
738   priv->scroll_indicator_alpha = 0.0;
739   priv->scroll_indicator_timeout = 0;
740   priv->motion_event_scroll_timeout = 0;
741   priv->scroll_indicator_event_interrupt = 0;
742   priv->scroll_delay_counter = 0;
743   priv->scrollbar_fade_delay = 0;
744   priv->scroll_to_x = -1;
745   priv->scroll_to_y = -1;
746   priv->first_drag = TRUE;
747   priv->initial_effect = TRUE;
748   priv->child_width = 0;
749   priv->child_height = 0;
750   priv->last_in = TRUE;
751   priv->x_offset = 0;
752   priv->y_offset = 0;
753   priv->center_on_child_focus_pending = FALSE;
754
755   gtk_style_lookup_color (GTK_WIDGET (area)->style,
756                           "SecondaryTextColor", &priv->scroll_color);
757
758   priv->hadjust =
759     GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
760   priv->vadjust =
761     GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
762
763   g_object_ref_sink (G_OBJECT (priv->hadjust));
764   g_object_ref_sink (G_OBJECT (priv->vadjust));
765
766   g_signal_connect_swapped (priv->hadjust, "value-changed",
767                             G_CALLBACK (hildon_pannable_area_adjust_value_changed), area);
768   g_signal_connect_swapped (priv->vadjust, "value-changed",
769                             G_CALLBACK (hildon_pannable_area_adjust_value_changed), area);
770   g_signal_connect_swapped (priv->hadjust, "changed",
771                             G_CALLBACK (hildon_pannable_area_adjust_changed), area);
772   g_signal_connect_swapped (priv->vadjust, "changed",
773                             G_CALLBACK (hildon_pannable_area_adjust_changed), area);
774   g_signal_connect (area, "grab-notify",
775                     G_CALLBACK (hildon_pannable_area_grab_notify), NULL);
776 }
777
778 static void
779 hildon_pannable_area_get_property (GObject * object,
780                                    guint property_id,
781                                    GValue * value,
782                                    GParamSpec * pspec)
783 {
784   HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (object)->priv;
785
786   switch (property_id) {
787   case PROP_ENABLED:
788     g_value_set_boolean (value, priv->enabled);
789     break;
790   case PROP_MODE:
791     g_value_set_enum (value, priv->mode);
792     break;
793   case PROP_MOVEMENT_MODE:
794     g_value_set_flags (value, priv->mov_mode);
795     break;
796   case PROP_VELOCITY_MIN:
797     g_value_set_double (value, priv->vmin);
798     break;
799   case PROP_VELOCITY_MAX:
800     g_value_set_double (value, priv->vmax);
801     break;
802   case PROP_VEL_MAX_OVERSHOOTING:
803     g_value_set_double (value, priv->vmax_overshooting);
804     break;
805   case PROP_VELOCITY_FAST_FACTOR:
806     g_value_set_double (value, priv->vfast_factor);
807     break;
808   case PROP_DECELERATION:
809     g_value_set_double (value, priv->decel);
810     break;
811   case PROP_DRAG_INERTIA:
812     g_value_set_double (value, priv->drag_inertia);
813     break;
814   case PROP_SPS:
815     g_value_set_uint (value, priv->sps);
816     break;
817   case PROP_PANNING_THRESHOLD:
818     g_value_set_uint (value, priv->panning_threshold);
819     break;
820   case PROP_SCROLLBAR_FADE_DELAY:
821     /* convert to miliseconds */
822     g_value_set_uint (value, priv->scrollbar_fade_delay * SCROLL_FADE_TIMEOUT);
823     break;
824   case PROP_BOUNCE_STEPS:
825     g_value_set_uint (value, priv->bounce_steps);
826     break;
827   case PROP_FORCE:
828     g_value_set_uint (value, priv->force);
829     break;
830   case PROP_DIRECTION_ERROR_MARGIN:
831     g_value_set_uint (value, priv->direction_error_margin);
832     break;
833   case PROP_VSCROLLBAR_POLICY:
834     g_value_set_enum (value, priv->vscrollbar_policy);
835     break;
836   case PROP_HSCROLLBAR_POLICY:
837     g_value_set_enum (value, priv->hscrollbar_policy);
838     break;
839   case PROP_VOVERSHOOT_MAX:
840     g_value_set_int (value, priv->vovershoot_max);
841     break;
842   case PROP_HOVERSHOOT_MAX:
843     g_value_set_int (value, priv->hovershoot_max);
844     break;
845   case PROP_SCROLL_TIME:
846     g_value_set_double (value, priv->scroll_time);
847     break;
848   case PROP_INITIAL_HINT:
849     g_value_set_boolean (value, priv->initial_hint);
850     break;
851   case PROP_LOW_FRICTION_MODE:
852     g_value_set_boolean (value, priv->low_friction_mode);
853     break;
854   case PROP_SIZE_REQUEST_POLICY:
855     g_value_set_enum (value, priv->size_request_policy);
856     break;
857   case PROP_HADJUSTMENT:
858     g_value_set_object (value,
859                         hildon_pannable_area_get_hadjustment
860                         (HILDON_PANNABLE_AREA (object)));
861     break;
862   case PROP_VADJUSTMENT:
863     g_value_set_object (value,
864                         hildon_pannable_area_get_vadjustment
865                         (HILDON_PANNABLE_AREA (object)));
866     break;
867   case PROP_CENTER_ON_CHILD_FOCUS:
868     g_value_set_boolean (value, priv->center_on_child_focus);
869     break;
870   default:
871     G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
872   }
873 }
874
875 static void
876 hildon_pannable_area_set_property (GObject * object,
877                                    guint property_id,
878                                    const GValue * value,
879                                    GParamSpec * pspec)
880 {
881   HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (object)->priv;
882   gboolean enabled;
883
884   switch (property_id) {
885   case PROP_ENABLED:
886     enabled = g_value_get_boolean (value);
887
888     if ((priv->enabled != enabled) && (GTK_WIDGET_REALIZED (object))) {
889       if (enabled)
890         gdk_window_raise (priv->event_window);
891       else
892         gdk_window_lower (priv->event_window);
893     }
894
895     priv->enabled = enabled;
896     break;
897   case PROP_MODE:
898     priv->mode = g_value_get_enum (value);
899     break;
900   case PROP_MOVEMENT_MODE:
901     priv->mov_mode = g_value_get_flags (value);
902     break;
903   case PROP_VELOCITY_MIN:
904     priv->vmin = g_value_get_double (value);
905     break;
906   case PROP_VELOCITY_MAX:
907     priv->vmax = g_value_get_double (value);
908     break;
909   case PROP_VEL_MAX_OVERSHOOTING:
910     priv->vmax_overshooting = g_value_get_double (value);
911     break;
912   case PROP_VELOCITY_FAST_FACTOR:
913     priv->vfast_factor = g_value_get_double (value);
914     break;
915   case PROP_DECELERATION:
916     priv->decel = g_value_get_double (value);
917     hildon_pannable_calculate_vel_factor (HILDON_PANNABLE_AREA (object));
918     break;
919   case PROP_DRAG_INERTIA:
920     priv->drag_inertia = g_value_get_double (value);
921     break;
922   case PROP_SPS:
923     priv->sps = g_value_get_uint (value);
924     break;
925   case PROP_PANNING_THRESHOLD:
926     {
927       GtkSettings *settings = gtk_settings_get_default ();
928       GtkSettingsValue svalue = { NULL, { 0, }, };
929
930       priv->panning_threshold = g_value_get_uint (value);
931
932       /* insure gtk dnd is the same we are using, not allowed
933          different thresholds in the same application */
934       svalue.origin = "panning_threshold";
935       g_value_init (&svalue.value, G_TYPE_LONG);
936       g_value_set_long (&svalue.value, priv->panning_threshold);
937       gtk_settings_set_property_value (settings, "gtk-dnd-drag-threshold", &svalue);
938       g_value_unset (&svalue.value);
939     }
940     break;
941   case PROP_SCROLLBAR_FADE_DELAY:
942     /* convert to miliseconds */
943     priv->scrollbar_fade_delay = g_value_get_uint (value)/(SCROLL_FADE_TIMEOUT);
944     break;
945   case PROP_BOUNCE_STEPS:
946     priv->bounce_steps = g_value_get_uint (value);
947     break;
948   case PROP_FORCE:
949     priv->force = g_value_get_uint (value);
950     break;
951   case PROP_DIRECTION_ERROR_MARGIN:
952     priv->direction_error_margin = g_value_get_uint (value);
953     break;
954   case PROP_VSCROLLBAR_POLICY:
955     priv->vscrollbar_policy = g_value_get_enum (value);
956
957     gtk_widget_queue_resize (GTK_WIDGET (object));
958     break;
959   case PROP_HSCROLLBAR_POLICY:
960     priv->hscrollbar_policy = g_value_get_enum (value);
961
962     gtk_widget_queue_resize (GTK_WIDGET (object));
963     break;
964   case PROP_VOVERSHOOT_MAX:
965     priv->vovershoot_max = g_value_get_int (value);
966     break;
967   case PROP_HOVERSHOOT_MAX:
968     priv->hovershoot_max = g_value_get_int (value);
969     break;
970   case PROP_SCROLL_TIME:
971     priv->scroll_time = g_value_get_double (value);
972
973     hildon_pannable_calculate_vel_factor (HILDON_PANNABLE_AREA (object));
974     break;
975   case PROP_INITIAL_HINT:
976     priv->initial_hint = g_value_get_boolean (value);
977     break;
978   case PROP_LOW_FRICTION_MODE:
979     priv->low_friction_mode = g_value_get_boolean (value);
980     break;
981   case PROP_SIZE_REQUEST_POLICY:
982     hildon_pannable_area_set_size_request_policy (HILDON_PANNABLE_AREA (object),
983                                                   g_value_get_enum (value));
984     break;
985   case PROP_CENTER_ON_CHILD_FOCUS:
986     priv->center_on_child_focus = g_value_get_boolean (value);
987     break;
988
989   default:
990     G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
991   }
992 }
993
994 static void
995 hildon_pannable_area_dispose (GObject * object)
996 {
997   HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (object)->priv;
998   GtkWidget *child = gtk_bin_get_child (GTK_BIN (object));
999
1000   hildon_pannable_area_remove_timeouts (GTK_WIDGET (object));
1001
1002   if (child) {
1003     g_signal_handlers_disconnect_by_func (child,
1004                                           hildon_pannable_area_child_mapped,
1005                                           object);
1006   }
1007
1008   g_signal_handlers_disconnect_by_func (object,
1009                                         hildon_pannable_area_grab_notify,
1010                                         NULL);
1011
1012   if (priv->hadjust) {
1013     g_signal_handlers_disconnect_by_func (priv->hadjust,
1014                                           hildon_pannable_area_adjust_value_changed,
1015                                           object);
1016     g_signal_handlers_disconnect_by_func (priv->hadjust,
1017                                           hildon_pannable_area_adjust_changed,
1018                                           object);
1019     g_object_unref (priv->hadjust);
1020     priv->hadjust = NULL;
1021   }
1022
1023   if (priv->vadjust) {
1024     g_signal_handlers_disconnect_by_func (priv->vadjust,
1025                                           hildon_pannable_area_adjust_value_changed,
1026                                           object);
1027     g_signal_handlers_disconnect_by_func (priv->vadjust,
1028                                           hildon_pannable_area_adjust_changed,
1029                                           object);
1030     g_object_unref (priv->vadjust);
1031     priv->vadjust = NULL;
1032   }
1033
1034   if (G_OBJECT_CLASS (hildon_pannable_area_parent_class)->dispose)
1035     G_OBJECT_CLASS (hildon_pannable_area_parent_class)->dispose (object);
1036 }
1037
1038 static void
1039 hildon_pannable_area_realize (GtkWidget * widget)
1040 {
1041   GdkWindowAttr attributes;
1042   gint attributes_mask;
1043   gint border_width;
1044   HildonPannableAreaPrivate *priv;
1045
1046   priv = HILDON_PANNABLE_AREA (widget)->priv;
1047
1048   GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
1049
1050   border_width = GTK_CONTAINER (widget)->border_width;
1051
1052   attributes.x = widget->allocation.x + border_width;
1053   attributes.y = widget->allocation.y + border_width;
1054   attributes.width = MAX (widget->allocation.width - 2 * border_width, 0);
1055   attributes.height = MAX (widget->allocation.height - 2 * border_width, 0);
1056   attributes.window_type = GDK_WINDOW_CHILD;
1057
1058   /* avoid using the hildon_window */
1059   attributes.visual = gtk_widget_get_visual (widget);
1060   attributes.colormap = gtk_widget_get_colormap (widget);
1061   attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK;
1062   attributes.wclass = GDK_INPUT_OUTPUT;
1063
1064   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1065
1066   widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
1067                                    &attributes, attributes_mask);
1068   gdk_window_set_user_data (widget->window, widget);
1069
1070   /* create the events window */
1071   attributes.x = 0;
1072   attributes.y = 0;
1073   attributes.event_mask = gtk_widget_get_events (widget)
1074     | GDK_BUTTON_MOTION_MASK
1075     | GDK_BUTTON_PRESS_MASK
1076     | GDK_BUTTON_RELEASE_MASK
1077     | GDK_SCROLL_MASK
1078     | GDK_POINTER_MOTION_HINT_MASK
1079     | GDK_EXPOSURE_MASK | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK;
1080   attributes.wclass = GDK_INPUT_ONLY;
1081
1082   attributes_mask = GDK_WA_X | GDK_WA_Y;
1083
1084   priv->event_window = gdk_window_new (widget->window,
1085                                        &attributes, attributes_mask);
1086   gdk_window_set_user_data (priv->event_window, widget);
1087
1088   widget->style = gtk_style_attach (widget->style, widget->window);
1089   gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
1090
1091   priv->scrollbars_gc = gdk_gc_new (GDK_DRAWABLE (widget->window));
1092   gdk_gc_copy (priv->scrollbars_gc, widget->style->fg_gc[GTK_STATE_INSENSITIVE]);
1093 }
1094
1095
1096 static void
1097 hildon_pannable_area_remove_timeouts (GtkWidget * widget)
1098 {
1099   HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1100
1101   if (priv->idle_id) {
1102     g_signal_emit (widget, pannable_area_signals[PANNING_FINISHED], 0);
1103     g_source_remove (priv->idle_id);
1104     priv->idle_id = 0;
1105   }
1106
1107   if (priv->scroll_indicator_timeout){
1108     g_source_remove (priv->scroll_indicator_timeout);
1109     priv->scroll_indicator_timeout = 0;
1110   }
1111
1112   if (priv->motion_event_scroll_timeout){
1113     g_source_remove (priv->motion_event_scroll_timeout);
1114     priv->motion_event_scroll_timeout = 0;
1115   }
1116 }
1117
1118 static void
1119 hildon_pannable_area_unrealize (GtkWidget * widget)
1120 {
1121   HildonPannableAreaPrivate *priv;
1122
1123   priv = HILDON_PANNABLE_AREA (widget)->priv;
1124
1125   hildon_pannable_area_remove_timeouts (widget);
1126
1127   if (priv->event_window != NULL) {
1128     gdk_window_set_user_data (priv->event_window, NULL);
1129     gdk_window_destroy (priv->event_window);
1130     priv->event_window = NULL;
1131   }
1132
1133   gdk_gc_unref (priv->scrollbars_gc);
1134
1135   if (GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->unrealize)
1136     (*GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->unrealize)(widget);
1137 }
1138
1139 static void
1140 hildon_pannable_area_size_request (GtkWidget * widget,
1141                                    GtkRequisition * requisition)
1142 {
1143   GtkRequisition child_requisition = {0};
1144   HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1145   GtkWidget *child = gtk_bin_get_child (GTK_BIN (widget));
1146
1147   if (child && GTK_WIDGET_VISIBLE (child))
1148     {
1149       gtk_widget_size_request (child, &child_requisition);
1150     }
1151
1152   if (priv->hscrollbar_policy == GTK_POLICY_NEVER) {
1153     requisition->width = child_requisition.width;
1154   } else {
1155     switch (priv->size_request_policy) {
1156       case HILDON_SIZE_REQUEST_CHILDREN:
1157         requisition->width = MIN (PANNABLE_MAX_WIDTH,
1158                                   child_requisition.width);
1159         break;
1160       case HILDON_SIZE_REQUEST_MINIMUM:
1161       default:
1162         requisition->width = priv->indicator_width;
1163       }
1164   }
1165
1166   if (priv->vscrollbar_policy == GTK_POLICY_NEVER) {
1167     requisition->height = child_requisition.height;
1168   } else {
1169     switch (priv->size_request_policy) {
1170       case HILDON_SIZE_REQUEST_CHILDREN:
1171         requisition->height = MIN (PANNABLE_MAX_HEIGHT,
1172                                    child_requisition.height);
1173         break;
1174       case HILDON_SIZE_REQUEST_MINIMUM:
1175       default:
1176         requisition->height = priv->indicator_width;
1177       }
1178   }
1179
1180   requisition->width += 2 * GTK_CONTAINER (widget)->border_width;
1181   requisition->height += 2 * GTK_CONTAINER (widget)->border_width;
1182 }
1183
1184 static void
1185 hildon_pannable_area_child_allocate_calculate (GtkWidget * widget,
1186                                                GtkAllocation * allocation,
1187                                                GtkAllocation * child_allocation)
1188 {
1189   gint border_width;
1190   HildonPannableAreaPrivate *priv;
1191
1192   border_width = GTK_CONTAINER (widget)->border_width;
1193
1194   priv = HILDON_PANNABLE_AREA (widget)->priv;
1195
1196   child_allocation->x = 0;
1197   child_allocation->y = 0;
1198   child_allocation->width = MAX (allocation->width - 2 * border_width -
1199                                  (priv->vscroll_visible ? priv->vscroll_rect.width : 0), 0);
1200   child_allocation->height = MAX (allocation->height - 2 * border_width -
1201                                   (priv->hscroll_visible ? priv->hscroll_rect.height : 0), 0);
1202
1203   if (priv->overshot_dist_y > 0) {
1204     child_allocation->y = MIN (child_allocation->y + priv->overshot_dist_y,
1205                                child_allocation->height);
1206     child_allocation->height = MAX (child_allocation->height - priv->overshot_dist_y, 0);
1207   } else if (priv->overshot_dist_y < 0) {
1208     child_allocation->height = MAX (child_allocation->height + priv->overshot_dist_y, 0);
1209   }
1210
1211   if (priv->overshot_dist_x > 0) {
1212     child_allocation->x = MIN (child_allocation->x + priv->overshot_dist_x,
1213                                child_allocation->width);
1214     child_allocation->width = MAX (child_allocation->width - priv->overshot_dist_x, 0);
1215   } else if (priv->overshot_dist_x < 0) {
1216     child_allocation->width = MAX (child_allocation->width + priv->overshot_dist_x, 0);
1217   }
1218 }
1219
1220 static void
1221 hildon_pannable_area_size_allocate (GtkWidget * widget,
1222                                     GtkAllocation * allocation)
1223 {
1224   GtkAllocation child_allocation;
1225   HildonPannableAreaPrivate *priv;
1226   GtkWidget *child = gtk_bin_get_child (GTK_BIN (widget));
1227   gint border_width;
1228   gdouble hv, vv;
1229
1230   border_width = GTK_CONTAINER (widget)->border_width;
1231
1232   widget->allocation = *allocation;
1233
1234   priv = HILDON_PANNABLE_AREA (widget)->priv;
1235
1236   if (GTK_WIDGET_REALIZED (widget)) {
1237       gdk_window_move_resize (widget->window,
1238                               allocation->x + border_width,
1239                               allocation->y  + border_width,
1240                               allocation->width  - border_width * 2,
1241                               allocation->height - border_width * 2);
1242       gdk_window_move_resize (priv->event_window,
1243                               0,
1244                               0,
1245                               allocation->width  - border_width * 2,
1246                               allocation->height - border_width * 2);
1247   }
1248
1249   if (child && GTK_WIDGET_VISIBLE (child)) {
1250
1251     hildon_pannable_area_check_scrollbars (HILDON_PANNABLE_AREA (widget));
1252
1253     hildon_pannable_area_child_allocate_calculate (widget,
1254                                                    allocation,
1255                                                    &child_allocation);
1256
1257     gtk_widget_size_allocate (child, &child_allocation);
1258
1259     if (hildon_pannable_area_check_scrollbars (HILDON_PANNABLE_AREA (widget))) {
1260       hildon_pannable_area_child_allocate_calculate (widget,
1261                                                      allocation,
1262                                                      &child_allocation);
1263
1264       gtk_widget_size_allocate (child, &child_allocation);
1265     }
1266
1267     if (priv->vadjust->page_size >= 0) {
1268       priv->accel_vel_y = MIN (priv->vmax,
1269                                priv->vadjust->upper/priv->vadjust->page_size*ACCEL_FACTOR);
1270       priv->accel_vel_x = MIN (priv->vmax,
1271                                priv->hadjust->upper/priv->hadjust->page_size*ACCEL_FACTOR);
1272     }
1273
1274     hv = priv->hadjust->value;
1275     vv = priv->vadjust->value;
1276
1277     /* we have to do this after child size_allocate because page_size is
1278      * changed when we allocate the size of the children */
1279     if (priv->overshot_dist_y < 0) {
1280       priv->vadjust->value = priv->vadjust->upper - priv->vadjust->page_size;
1281     }
1282
1283     if (priv->overshot_dist_x < 0) {
1284       priv->hadjust->value = priv->hadjust->upper - priv->hadjust->page_size;
1285     }
1286
1287     if (hv != priv->hadjust->value)
1288       gtk_adjustment_value_changed (priv->hadjust);
1289
1290     if (vv != priv->vadjust->value)
1291       gtk_adjustment_value_changed (priv->vadjust);
1292
1293   } else {
1294     hildon_pannable_area_check_scrollbars (HILDON_PANNABLE_AREA (widget));
1295   }
1296 }
1297
1298 static void
1299 hildon_pannable_area_style_set (GtkWidget * widget,
1300                                 GtkStyle * previous_style)
1301 {
1302   HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1303
1304   GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->
1305     style_set (widget, previous_style);
1306
1307   gtk_style_lookup_color (widget->style, "SecondaryTextColor", &priv->scroll_color);
1308   gtk_widget_style_get (widget, "indicator-width", &priv->indicator_width, NULL);
1309 }
1310
1311 static void
1312 hildon_pannable_area_map (GtkWidget * widget)
1313 {
1314   HildonPannableAreaPrivate *priv;
1315
1316   priv = HILDON_PANNABLE_AREA (widget)->priv;
1317
1318   gdk_window_show (widget->window);
1319
1320   if (priv->event_window != NULL && !priv->enabled)
1321     gdk_window_show (priv->event_window);
1322
1323   (*GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->map) (widget);
1324
1325   if (priv->event_window != NULL && priv->enabled)
1326     gdk_window_show (priv->event_window);
1327 }
1328
1329 static void
1330 hildon_pannable_area_unmap (GtkWidget * widget)
1331 {
1332   HildonPannableAreaPrivate *priv;
1333
1334   priv = HILDON_PANNABLE_AREA (widget)->priv;
1335
1336   if (priv->event_window != NULL)
1337     gdk_window_hide (priv->event_window);
1338
1339   gdk_window_hide (widget->window);
1340
1341   (*GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->unmap) (widget);
1342 }
1343
1344 static void
1345 hildon_pannable_area_grab_notify (GtkWidget *widget,
1346                                   gboolean was_grabbed,
1347                                   gpointer user_data)
1348 {
1349   /* an internal widget has grabbed the focus and now has returned it,
1350      we have to do some release actions */
1351   if (was_grabbed) {
1352     HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1353
1354     priv->scroll_indicator_event_interrupt = 0;
1355
1356     if ((!priv->scroll_indicator_timeout)&&(priv->scroll_indicator_alpha)>0.1) {
1357       priv->scroll_delay_counter = priv->scrollbar_fade_delay;
1358
1359       hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget),
1360                                                 priv->scroll_indicator_alpha);
1361     }
1362
1363     priv->last_type = 3;
1364     priv->moved = FALSE;
1365   }
1366 }
1367
1368 #if USE_CAIRO_SCROLLBARS == 1
1369
1370 static void
1371 rgb_from_gdkcolor (GdkColor *color, gdouble *r, gdouble *g, gdouble *b)
1372 {
1373   *r = (color->red >> 8) / 255.0;
1374   *g = (color->green >> 8) / 255.0;
1375   *b = (color->blue >> 8) / 255.0;
1376 }
1377
1378 static void
1379 hildon_pannable_draw_vscroll (GtkWidget * widget,
1380                               GdkColor *back_color,
1381                               GdkColor *scroll_color)
1382 {
1383   HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1384   gfloat y, height;
1385   cairo_t *cr;
1386   cairo_pattern_t *pattern;
1387   gdouble r, g, b;
1388   gint radius = (priv->vscroll_rect.width/2) - 1;
1389
1390   cr = gdk_cairo_create(widget->window);
1391
1392   /* Draw the background */
1393   rgb_from_gdkcolor (back_color, &r, &g, &b);
1394   cairo_set_source_rgb (cr, r, g, b);
1395   cairo_rectangle(cr, priv->vscroll_rect.x, priv->vscroll_rect.y,
1396                   priv->vscroll_rect.width,
1397                   priv->vscroll_rect.height);
1398   cairo_fill_preserve (cr);
1399   cairo_clip (cr);
1400
1401   /* Calculate the scroll bar height and position */
1402   y = ((priv->vadjust->value - priv->vadjust->lower) / (priv->vadjust->upper - priv->vadjust->lower)) *
1403     (widget->allocation.height -
1404      (priv->hscroll_visible ? priv->indicator_width : 0));
1405   height = ((((priv->vadjust->value - priv->vadjust->lower) +
1406               priv->vadjust->page_size) /
1407              (priv->vadjust->upper - priv->vadjust->lower)) *
1408             (widget->allocation.height -
1409              (priv->hscroll_visible ? priv->indicator_width : 0))) - y;
1410
1411   /* Set a minimum height */
1412   height = MAX (SCROLL_BAR_MIN_SIZE, height);
1413
1414   /* Check the max y position */
1415   y = MIN (y, widget->allocation.height -
1416            (priv->hscroll_visible ? priv->hscroll_rect.height : 0) -
1417            height);
1418
1419   /* Draw the scrollbar */
1420   rgb_from_gdkcolor (scroll_color, &r, &g, &b);
1421
1422   pattern = cairo_pattern_create_linear(radius+1, y, radius+1,y + height);
1423   cairo_pattern_add_color_stop_rgb(pattern, 0, r, g, b);
1424   cairo_pattern_add_color_stop_rgb(pattern, 1, r/2, g/2, b/2);
1425   cairo_set_source(cr, pattern);
1426   cairo_fill(cr);
1427   cairo_pattern_destroy(pattern);
1428
1429   cairo_arc(cr, priv->vscroll_rect.x + radius + 1, y + radius + 1, radius, G_PI, 0);
1430   cairo_line_to(cr, priv->vscroll_rect.x + (radius * 2) + 1, y + height - radius);
1431   cairo_arc(cr, priv->vscroll_rect.x + radius + 1, y + height - radius, radius, 0, G_PI);
1432   cairo_line_to(cr, priv->vscroll_rect.x + 1, y + height - radius);
1433   cairo_clip (cr);
1434
1435   cairo_paint_with_alpha(cr, priv->scroll_indicator_alpha);
1436
1437   cairo_destroy(cr);
1438 }
1439
1440 static void
1441 hildon_pannable_draw_hscroll (GtkWidget * widget,
1442                               GdkColor *back_color,
1443                               GdkColor *scroll_color)
1444 {
1445   HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1446   gfloat x, width;
1447   cairo_t *cr;
1448   cairo_pattern_t *pattern;
1449   gdouble r, g, b;
1450   gint radius = (priv->hscroll_rect.height/2) - 1;
1451
1452   cr = gdk_cairo_create(widget->window);
1453
1454   /* Draw the background */
1455   rgb_from_gdkcolor (back_color, &r, &g, &b);
1456   cairo_set_source_rgb (cr, r, g, b);
1457   cairo_rectangle(cr, priv->hscroll_rect.x, priv->hscroll_rect.y,
1458                   priv->hscroll_rect.width,
1459                   priv->hscroll_rect.height);
1460   cairo_fill_preserve (cr);
1461   cairo_clip (cr);
1462
1463   /* calculate the scrollbar width and position */
1464   x = ((priv->hadjust->value - priv->hadjust->lower) / (priv->hadjust->upper - priv->hadjust->lower)) *
1465     (widget->allocation.width - (priv->vscroll_visible ? priv->indicator_width : 0));
1466   width =((((priv->hadjust->value - priv->hadjust->lower) +
1467             priv->hadjust->page_size) / (priv->hadjust->upper - priv->hadjust->lower)) *
1468           (widget->allocation.width -
1469            (priv->vscroll_visible ? priv->indicator_width : 0))) - x;
1470
1471   /* Set a minimum width */
1472   width = MAX (SCROLL_BAR_MIN_SIZE, width);
1473
1474   /* Check the max x position */
1475   x = MIN (x, widget->allocation.width -
1476            (priv->vscroll_visible ? priv->vscroll_rect.width : 0) -
1477            width);
1478
1479   /* Draw the scrollbar */
1480   rgb_from_gdkcolor (scroll_color, &r, &g, &b);
1481
1482   pattern = cairo_pattern_create_linear(x, radius+1, x+width, radius+1);
1483   cairo_pattern_add_color_stop_rgb(pattern, 0, r, g, b);
1484   cairo_pattern_add_color_stop_rgb(pattern, 1, r/2, g/2, b/2);
1485   cairo_set_source(cr, pattern);
1486   cairo_fill(cr);
1487   cairo_pattern_destroy(pattern);
1488
1489   cairo_arc_negative(cr, x + radius + 1, priv->hscroll_rect.y + radius + 1, radius, 3*G_PI_2, G_PI_2);
1490   cairo_line_to(cr, x + width - radius, priv->hscroll_rect.y + (radius * 2) + 1);
1491   cairo_arc_negative(cr, x + width - radius, priv->hscroll_rect.y + radius + 1, radius, G_PI_2, 3*G_PI_2);
1492   cairo_line_to(cr, x + width - radius, priv->hscroll_rect.y + 1);
1493   cairo_clip (cr);
1494
1495   cairo_paint_with_alpha(cr, priv->scroll_indicator_alpha);
1496
1497   cairo_destroy(cr);
1498 }
1499
1500 #else /* USE_CAIRO_SCROLLBARS */
1501
1502 static void
1503 tranparency_color (GdkColor *color,
1504                    GdkColor colora,
1505                    GdkColor colorb,
1506                    gdouble transparency)
1507 {
1508   gdouble diff;
1509
1510   diff = colora.red - colorb.red;
1511   color->red = colora.red-diff*transparency;
1512
1513   diff = colora.green - colorb.green;
1514   color->green = colora.green-diff*transparency;
1515
1516   diff = colora.blue - colorb.blue;
1517   color->blue = colora.blue-diff*transparency;
1518 }
1519
1520 static void
1521 hildon_pannable_draw_vscroll (GtkWidget *widget,
1522                               GdkColor *back_color,
1523                               GdkColor *scroll_color)
1524 {
1525   HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1526   gfloat y, height;
1527   GdkColor transp_color;
1528   GdkGC *gc = priv->scrollbars_gc;
1529
1530   gdk_draw_rectangle (widget->window,
1531                       widget->style->bg_gc[GTK_STATE_NORMAL],
1532                       TRUE,
1533                        priv->vscroll_rect.x, priv->vscroll_rect.y,
1534                       priv->vscroll_rect.width,
1535                       priv->vscroll_rect.height);
1536
1537   y = ((priv->vadjust->value - priv->vadjust->lower) / (priv->vadjust->upper - priv->vadjust->lower)) *
1538     (widget->allocation.height - (priv->hscroll_visible ? priv->indicator_width : 0));
1539   height = ((((priv->vadjust->value - priv->vadjust->lower) + priv->vadjust->page_size) /
1540              (priv->vadjust->upper - priv->vadjust->lower)) *
1541             (widget->allocation.height -
1542              (priv->hscroll_visible ? priv->indicator_width : 0))) - y;
1543
1544   /* Set a minimum height */
1545   height = MAX (SCROLL_BAR_MIN_SIZE, height);
1546
1547   /* Check the max y position */
1548   y = MIN (y, widget->allocation.height -
1549            (priv->hscroll_visible ? priv->hscroll_rect.height : 0) -
1550            height);
1551
1552   if (priv->scroll_indicator_alpha == 1.0) {
1553     transp_color = priv->scroll_color;
1554   } else if (priv->scroll_indicator_alpha < 1.0) {
1555     tranparency_color (&transp_color, *back_color, *scroll_color,
1556                        priv->scroll_indicator_alpha);
1557   }
1558   gdk_gc_set_rgb_fg_color (gc, &transp_color);
1559
1560   gdk_draw_rectangle (widget->window, gc,
1561                       TRUE, priv->vscroll_rect.x, y,
1562                       priv->vscroll_rect.width, height);
1563 }
1564
1565 static void
1566 hildon_pannable_draw_hscroll (GtkWidget *widget,
1567                               GdkColor *back_color,
1568                               GdkColor *scroll_color)
1569 {
1570   HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1571   gfloat x, width;
1572   GdkColor transp_color;
1573   GdkGC *gc = priv->scrollbars_gc;
1574
1575   gdk_draw_rectangle (widget->window,
1576                       widget->style->bg_gc[GTK_STATE_INSENSITIVE],
1577                       TRUE,
1578                       priv->hscroll_rect.x, priv->hscroll_rect.y,
1579                       priv->hscroll_rect.width,
1580                       priv->hscroll_rect.height);
1581
1582   /* calculate the scrollbar width and position */
1583   x = ((priv->hadjust->value - priv->hadjust->lower) / (priv->hadjust->upper - priv->hadjust->lower)) *
1584     (widget->allocation.width - (priv->vscroll_visible ? priv->indicator_width : 0));
1585   width =((((priv->hadjust->value - priv->hadjust->lower) +
1586             priv->hadjust->page_size) / (priv->hadjust->upper - priv->hadjust->lower)) *
1587           (widget->allocation.width -
1588            (priv->vscroll_visible ? priv->indicator_width : 0))) - x;
1589
1590   /* Set a minimum width */
1591   width = MAX (SCROLL_BAR_MIN_SIZE, width);
1592
1593   /* Check the max x position */
1594   x = MIN (x, widget->allocation.width -
1595            (priv->vscroll_visible ? priv->vscroll_rect.width : 0) -
1596            width);
1597
1598   if (priv->scroll_indicator_alpha == 1.0) {
1599     transp_color = priv->scroll_color;
1600   } else if (priv->scroll_indicator_alpha < 1.0) {
1601     tranparency_color (&transp_color, *back_color, *scroll_color,
1602                        priv->scroll_indicator_alpha);
1603   }
1604   gdk_gc_set_rgb_fg_color (gc, &transp_color);
1605
1606   gdk_draw_rectangle (widget->window, gc,
1607                       TRUE, x, priv->hscroll_rect.y, width,
1608                       priv->hscroll_rect.height);
1609 }
1610
1611 #endif /* USE_CAIRO_SCROLLBARS */
1612
1613 static void
1614 hildon_pannable_area_initial_effect (GtkWidget * widget)
1615 {
1616   HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1617
1618   if (priv->initial_hint) {
1619     if (priv->vscroll_visible || priv->hscroll_visible) {
1620
1621       priv->scroll_indicator_event_interrupt = 0;
1622       priv->scroll_delay_counter = priv->scrollbar_fade_delay;
1623
1624       hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget), 1.0);
1625     }
1626   }
1627 }
1628
1629 static void
1630 hildon_pannable_area_launch_fade_timeout (HildonPannableArea * area,
1631                                           gdouble alpha)
1632 {
1633   HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (area)->priv;
1634
1635   priv->scroll_indicator_alpha = alpha;
1636
1637   if (!priv->scroll_indicator_timeout)
1638     priv->scroll_indicator_timeout =
1639       gdk_threads_add_timeout_full (G_PRIORITY_HIGH_IDLE + 20,
1640                                     SCROLL_FADE_TIMEOUT,
1641                                     (GSourceFunc) hildon_pannable_area_scroll_indicator_fade,
1642                                     area,
1643                                     NULL);
1644 }
1645
1646 static void
1647 hildon_pannable_area_adjust_changed (HildonPannableArea * area,
1648                                      gpointer data)
1649 {
1650   if (GTK_WIDGET_REALIZED (area))
1651     hildon_pannable_area_refresh (area);
1652 }
1653
1654 static void
1655 hildon_pannable_area_adjust_value_changed (HildonPannableArea * area,
1656                                            gpointer data)
1657 {
1658   HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (area)->priv;
1659   gint xdiff, ydiff;
1660   gint x = priv->x_offset;
1661   gint y = priv->y_offset;
1662
1663   priv->x_offset = priv->hadjust->value;
1664   xdiff = x - priv->x_offset;
1665   priv->y_offset = priv->vadjust->value;
1666   ydiff = y - priv->y_offset;
1667
1668   if ((xdiff || ydiff) && GTK_WIDGET_DRAWABLE (area)) {
1669     hildon_pannable_area_redraw (area);
1670
1671     if ((priv->vscroll_visible) || (priv->hscroll_visible)) {
1672       priv->scroll_indicator_event_interrupt = 0;
1673       priv->scroll_delay_counter = priv->scrollbar_fade_delay;
1674
1675       hildon_pannable_area_launch_fade_timeout (area, 1.0);
1676     }
1677   }
1678 }
1679
1680 static void
1681 hildon_pannable_area_redraw (HildonPannableArea * area)
1682 {
1683   HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (area)->priv;
1684
1685   /* Redraw scroll indicators */
1686   if (GTK_WIDGET_DRAWABLE (area)) {
1687       if (priv->hscroll_visible) {
1688         gdk_window_invalidate_rect (GTK_WIDGET (area)->window,
1689                                     &priv->hscroll_rect, FALSE);
1690       }
1691
1692       if (priv->vscroll_visible) {
1693         gdk_window_invalidate_rect (GTK_WIDGET (area)->window,
1694                                     &priv->vscroll_rect, FALSE);
1695       }
1696   }
1697 }
1698
1699 static gboolean
1700 hildon_pannable_area_scroll_indicator_fade(HildonPannableArea * area)
1701 {
1702   HildonPannableAreaPrivate *priv = area->priv;
1703
1704   /* if moving do not fade out */
1705   if (((ABS (priv->vel_y)>priv->vmin)||
1706        (ABS (priv->vel_x)>priv->vmin))&&(!priv->button_pressed)) {
1707
1708     return TRUE;
1709   }
1710
1711   if (priv->scroll_indicator_event_interrupt) {
1712     /* Stop a fade out, and fade back in */
1713     if (priv->scroll_indicator_alpha > 0.9) {
1714       priv->scroll_indicator_alpha = 1.0;
1715       priv->scroll_indicator_timeout = 0;
1716
1717       return FALSE;
1718     } else {
1719       priv->scroll_indicator_alpha += 0.2;
1720       hildon_pannable_area_redraw (area);
1721
1722       return TRUE;
1723     }
1724   }
1725
1726   if ((priv->scroll_indicator_alpha > 0.9) &&
1727       (priv->scroll_delay_counter > 0)) {
1728     priv->scroll_delay_counter--;
1729
1730     return TRUE;
1731   }
1732
1733   if (!priv->scroll_indicator_event_interrupt) {
1734     /* Continue fade out */
1735     if (priv->scroll_indicator_alpha < 0.1) {
1736       priv->scroll_indicator_timeout = 0;
1737       priv->scroll_indicator_alpha = 0.0;
1738
1739       return FALSE;
1740     } else {
1741       priv->scroll_indicator_alpha -= 0.2;
1742       hildon_pannable_area_redraw (area);
1743
1744       return TRUE;
1745     }
1746   }
1747
1748   return TRUE;
1749 }
1750
1751 static gboolean
1752 hildon_pannable_area_expose_event (GtkWidget * widget,
1753                                    GdkEventExpose * event)
1754 {
1755
1756   HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1757 #if USE_CAIRO_SCROLLBARS == 1
1758   GdkColor back_color = widget->style->bg[GTK_STATE_NORMAL];
1759   GdkColor scroll_color = widget->style->base[GTK_STATE_SELECTED];
1760 #else /* USE_CAIRO_SCROLLBARS */
1761   GdkColor back_color = widget->style->bg[GTK_STATE_NORMAL];
1762   GdkColor scroll_color = priv->scroll_color;
1763 #endif
1764
1765   if (G_UNLIKELY (priv->initial_effect)) {
1766     hildon_pannable_area_initial_effect (widget);
1767
1768     priv->initial_effect = FALSE;
1769   }
1770
1771   if (gtk_bin_get_child (GTK_BIN (widget))) {
1772
1773     if (priv->scroll_indicator_alpha > 0.1) {
1774       if (priv->vscroll_visible) {
1775         hildon_pannable_draw_vscroll (widget, &back_color, &scroll_color);
1776       }
1777       if (priv->hscroll_visible) {
1778         hildon_pannable_draw_hscroll (widget, &back_color, &scroll_color);
1779       }
1780     }
1781
1782     /* draw overshooting rectangles */
1783     if (priv->overshot_dist_y > 0) {
1784       gint overshot_height;
1785
1786       overshot_height = MIN (priv->overshot_dist_y, widget->allocation.height -
1787                              (priv->hscroll_visible ? priv->hscroll_rect.height : 0));
1788
1789       gdk_draw_rectangle (widget->window,
1790                           widget->style->bg_gc[GTK_STATE_NORMAL],
1791                           TRUE,
1792                           0,
1793                           0,
1794                           widget->allocation.width -
1795                           (priv->vscroll_visible ? priv->vscroll_rect.width : 0),
1796                           overshot_height);
1797     } else if (priv->overshot_dist_y < 0) {
1798       gint overshot_height;
1799       gint overshot_y;
1800
1801       overshot_height =
1802         MAX (priv->overshot_dist_y,
1803              -(widget->allocation.height -
1804                (priv->hscroll_visible ? priv->hscroll_rect.height : 0)));
1805
1806       overshot_y = MAX (widget->allocation.height +
1807                         overshot_height -
1808                         (priv->hscroll_visible ? priv->hscroll_rect.height : 0), 0);
1809
1810       gdk_draw_rectangle (widget->window,
1811                           widget->style->bg_gc[GTK_STATE_NORMAL],
1812                           TRUE,
1813                           0,
1814                           overshot_y,
1815                           widget->allocation.width -
1816                           priv->vscroll_rect.width,
1817                           -overshot_height);
1818     }
1819
1820     if (priv->overshot_dist_x > 0) {
1821       gint overshot_width;
1822
1823       overshot_width = MIN (priv->overshot_dist_x, widget->allocation.width -
1824                              (priv->vscroll_visible ? priv->vscroll_rect.width : 0));
1825
1826       gdk_draw_rectangle (widget->window,
1827                           widget->style->bg_gc[GTK_STATE_NORMAL],
1828                           TRUE,
1829                           0,
1830                           0,
1831                           overshot_width,
1832                           widget->allocation.height -
1833                           (priv->hscroll_visible ? priv->hscroll_rect.height : 0));
1834     } else if (priv->overshot_dist_x < 0) {
1835       gint overshot_width;
1836       gint overshot_x;
1837
1838       overshot_width =
1839         MAX (priv->overshot_dist_x,
1840              -(widget->allocation.width -
1841                (priv->vscroll_visible ? priv->vscroll_rect.width : 0)));
1842
1843       overshot_x = MAX (widget->allocation.width +
1844                         overshot_width -
1845                         (priv->vscroll_visible ? priv->vscroll_rect.width : 0), 0);
1846
1847       gdk_draw_rectangle (widget->window,
1848                           widget->style->bg_gc[GTK_STATE_NORMAL],
1849                           TRUE,
1850                           overshot_x,
1851                           0,
1852                           -overshot_width,
1853                           widget->allocation.height -
1854                           priv->hscroll_rect.height);
1855     }
1856
1857   }
1858
1859   return GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->expose_event (widget, event);
1860 }
1861
1862 static GdkWindow *
1863 hildon_pannable_area_get_topmost (GdkWindow * window,
1864                                   gint x, gint y,
1865                                   gint * tx, gint * ty,
1866                                   GdkEventMask mask)
1867 {
1868   /* Find the GdkWindow at the given point, by recursing from a given
1869    * parent GdkWindow. Optionally return the co-ordinates transformed
1870    * relative to the child window.
1871    */
1872   gint width, height;
1873   GList *c, *children;
1874   GdkWindow *selected_window = NULL;
1875
1876   gdk_drawable_get_size (GDK_DRAWABLE (window), &width, &height);
1877   if ((x < 0) || (x >= width) || (y < 0) || (y >= height))
1878     return NULL;
1879
1880   children = gdk_window_peek_children (window);
1881
1882   if (!children) {
1883     if (tx)
1884       *tx = x;
1885     if (ty)
1886       *ty = y;
1887     selected_window = window;
1888   }
1889
1890   for (c = children; c; c = c->next) {
1891     GdkWindow *child = (GdkWindow *) c->data;
1892     gint wx, wy;
1893
1894     gdk_drawable_get_size (GDK_DRAWABLE (child), &width, &height);
1895     gdk_window_get_position (child, &wx, &wy);
1896
1897     if ((x >= wx) && (x < (wx + width)) && (y >= wy) && (y < (wy + height)) &&
1898         (gdk_window_is_visible (child))) {
1899
1900       if (gdk_window_peek_children (child)) {
1901         selected_window = hildon_pannable_area_get_topmost (child, x-wx, y-wy,
1902                                                             tx, ty, mask);
1903         if (!selected_window) {
1904           if (tx)
1905             *tx = x-wx;
1906           if (ty)
1907             *ty = y-wy;
1908           selected_window = child;
1909         }
1910       } else {
1911         if ((gdk_window_get_events (child)&mask)) {
1912           if (tx)
1913             *tx = x-wx;
1914           if (ty)
1915             *ty = y-wy;
1916           selected_window = child;
1917         }
1918       }
1919     }
1920   }
1921
1922   return selected_window;
1923 }
1924
1925 static void
1926 synth_crossing (GdkWindow * child,
1927                 gint x, gint y,
1928                 gint x_root, gint y_root,
1929                 guint32 time, gboolean in)
1930 {
1931   GdkEventCrossing *crossing_event;
1932   GdkEventType type = in ? GDK_ENTER_NOTIFY : GDK_LEAVE_NOTIFY;
1933
1934   /* Send synthetic enter event */
1935   crossing_event = (GdkEventCrossing *) gdk_event_new (type);
1936   ((GdkEventAny *) crossing_event)->type = type;
1937   ((GdkEventAny *) crossing_event)->window = g_object_ref (child);
1938   ((GdkEventAny *) crossing_event)->send_event = FALSE;
1939   crossing_event->subwindow = g_object_ref (child);
1940   crossing_event->time = time;
1941   crossing_event->x = x;
1942   crossing_event->y = y;
1943   crossing_event->x_root = x_root;
1944   crossing_event->y_root = y_root;
1945   crossing_event->mode = GDK_CROSSING_NORMAL;
1946   crossing_event->detail = GDK_NOTIFY_UNKNOWN;
1947   crossing_event->focus = FALSE;
1948   crossing_event->state = 0;
1949   gdk_event_put ((GdkEvent *) crossing_event);
1950   gdk_event_free ((GdkEvent *) crossing_event);
1951 }
1952
1953 static gboolean
1954 hildon_pannable_area_button_press_cb (GtkWidget * widget,
1955                                       GdkEventButton * event)
1956 {
1957   gint x, y;
1958   HildonPannableArea *area = HILDON_PANNABLE_AREA (widget);
1959   HildonPannableAreaPrivate *priv = area->priv;
1960
1961   if ((!priv->enabled) || (event->button != 1) ||
1962       ((event->time == priv->last_time) &&
1963        (priv->last_type == 1)) || (gtk_bin_get_child (GTK_BIN (widget)) == NULL))
1964     return TRUE;
1965
1966   priv->scroll_indicator_event_interrupt = 1;
1967
1968   hildon_pannable_area_launch_fade_timeout (area,
1969                                             priv->scroll_indicator_alpha);
1970
1971   priv->last_time = event->time;
1972   priv->last_press_time = event->time;
1973   priv->last_type = 1;
1974
1975   priv->scroll_to_x = -1;
1976   priv->scroll_to_y = -1;
1977
1978   if (priv->button_pressed && priv->child) {
1979     /* Widget stole focus on last click, send crossing-out event */
1980     synth_crossing (priv->child, 0, 0, event->x_root, event->y_root,
1981                     event->time, FALSE);
1982   }
1983
1984   priv->x = event->x;
1985   priv->y = event->y;
1986   priv->ix = priv->x;
1987   priv->iy = priv->y;
1988
1989   /* Don't allow a click if we're still moving fast */
1990   if ((ABS (priv->vel_x) <= (priv->vmax * priv->vfast_factor)) &&
1991       (ABS (priv->vel_y) <= (priv->vmax * priv->vfast_factor)))
1992     priv->child =
1993       hildon_pannable_area_get_topmost (gtk_bin_get_child (GTK_BIN (widget))->window,
1994                                         event->x, event->y, &x, &y, GDK_BUTTON_PRESS_MASK);
1995   else
1996     priv->child = NULL;
1997
1998   priv->button_pressed = TRUE;
1999
2000   /* Stop scrolling on mouse-down (so you can flick, then hold to stop) */
2001   priv->old_vel_x = priv->vel_x;
2002   priv->old_vel_y = priv->vel_y;
2003   priv->vel_x = 0;
2004   priv->vel_y = 0;
2005   if (priv->idle_id) {
2006     g_source_remove (priv->idle_id);
2007     priv->idle_id = 0;
2008     g_signal_emit (area, pannable_area_signals[PANNING_FINISHED], 0);
2009   }
2010
2011   if (priv->child) {
2012
2013     gdk_drawable_get_size (priv->child, &priv->child_width,
2014                            &priv->child_height);
2015     priv->last_in = TRUE;
2016
2017     g_object_add_weak_pointer ((GObject *) priv->child,
2018                                (gpointer) & priv->child);
2019
2020     event = (GdkEventButton *) gdk_event_copy ((GdkEvent *) event);
2021     /* remove the reference we added with the copy */
2022     g_object_unref (priv->event_window);
2023     event->x = x;
2024     event->y = y;
2025     priv->cx = x;
2026     priv->cy = y;
2027
2028     synth_crossing (priv->child, x, y, event->x_root,
2029                     event->y_root, event->time, TRUE);
2030
2031     /* Send synthetic click (button press/release) event */
2032     ((GdkEventAny *) event)->window = g_object_ref (priv->child);
2033
2034     gdk_event_put ((GdkEvent *) event);
2035     gdk_event_free ((GdkEvent *) event);
2036   } else
2037     priv->child = NULL;
2038
2039   return TRUE;
2040 }
2041
2042 static gboolean
2043 hildon_pannable_area_check_scrollbars (HildonPannableArea * area)
2044 {
2045   HildonPannableAreaPrivate *priv = area->priv;
2046   gboolean prev_hscroll_visible, prev_vscroll_visible;
2047
2048   prev_hscroll_visible = priv->hscroll_visible;
2049   prev_vscroll_visible = priv->vscroll_visible;
2050
2051   if (!gtk_bin_get_child (GTK_BIN (area))) {
2052     priv->vscroll_visible = FALSE;
2053     priv->hscroll_visible = FALSE;
2054   } else {
2055     switch (priv->hscrollbar_policy) {
2056     case GTK_POLICY_ALWAYS:
2057       priv->hscroll_visible = TRUE;
2058       break;
2059     case GTK_POLICY_NEVER:
2060       priv->hscroll_visible = FALSE;
2061       break;
2062     default:
2063       priv->hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
2064                                priv->hadjust->page_size);
2065     }
2066
2067     switch (priv->vscrollbar_policy) {
2068     case GTK_POLICY_ALWAYS:
2069       priv->vscroll_visible = TRUE;
2070       break;
2071     case GTK_POLICY_NEVER:
2072       priv->vscroll_visible = FALSE;
2073       break;
2074     default:
2075       priv->vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
2076                                priv->vadjust->page_size);
2077     }
2078
2079     /* Store the vscroll/hscroll areas for redrawing */
2080     if (priv->vscroll_visible) {
2081       GtkAllocation *allocation = &GTK_WIDGET (area)->allocation;
2082       priv->vscroll_rect.x = allocation->width - priv->indicator_width;
2083       priv->vscroll_rect.y = 0;
2084       priv->vscroll_rect.width = priv->indicator_width;
2085       priv->vscroll_rect.height = allocation->height -
2086         (priv->hscroll_visible ? priv->indicator_width : 0);
2087     }
2088     if (priv->hscroll_visible) {
2089       GtkAllocation *allocation = &GTK_WIDGET (area)->allocation;
2090       priv->hscroll_rect.y = allocation->height - priv->indicator_width;
2091       priv->hscroll_rect.x = 0;
2092       priv->hscroll_rect.height = priv->indicator_width;
2093       priv->hscroll_rect.width = allocation->width -
2094         (priv->vscroll_visible ? priv->indicator_width : 0);
2095     }
2096   }
2097
2098   return ((priv->hscroll_visible != prev_hscroll_visible) ||
2099           (priv->vscroll_visible != prev_vscroll_visible));
2100 }
2101
2102 static void
2103 hildon_pannable_area_refresh (HildonPannableArea * area)
2104 {
2105   if (GTK_WIDGET_DRAWABLE (area) &&
2106       hildon_pannable_area_check_scrollbars (area)) {
2107     HildonPannableAreaPrivate *priv = area->priv;
2108
2109     gtk_widget_queue_resize (GTK_WIDGET (area));
2110
2111     if ((priv->vscroll_visible) || (priv->hscroll_visible)) {
2112       priv->scroll_indicator_event_interrupt = 0;
2113       priv->scroll_delay_counter = area->priv->scrollbar_fade_delay;
2114
2115       hildon_pannable_area_launch_fade_timeout (area, 1.0);
2116     }
2117   } else {
2118     hildon_pannable_area_redraw (area);
2119   }
2120 }
2121
2122 /* Scroll by a particular amount (in pixels). Optionally, return if
2123  * the scroll on a particular axis was successful.
2124  */
2125 static void
2126 hildon_pannable_axis_scroll (HildonPannableArea *area,
2127                              GtkAdjustment *adjust,
2128                              gdouble *vel,
2129                              gdouble inc,
2130                              gint *overshooting,
2131                              gint *overshot_dist,
2132                              gdouble *scroll_to,
2133                              gint overshoot_max,
2134                              gboolean *s)
2135 {
2136   gdouble dist;
2137   HildonPannableAreaPrivate *priv = area->priv;
2138
2139   dist = gtk_adjustment_get_value (adjust) - inc;
2140
2141   /* Overshooting
2142    * We use overshot_dist to define the distance of the current overshoot,
2143    * and overshooting to define the direction/whether or not we are overshot
2144    */
2145   if (!(*overshooting)) {
2146
2147     /* Initiation of the overshoot happens when the finger is released
2148      * and the current position of the pannable contents are out of range
2149      */
2150     if (dist < adjust->lower) {
2151       if (s) *s = FALSE;
2152
2153       dist = adjust->lower;
2154
2155       if (overshoot_max!=0) {
2156         *overshooting = 1;
2157         *scroll_to = -1;
2158         *overshot_dist = CLAMP (*overshot_dist + *vel, 0, overshoot_max);
2159         *vel = MIN (priv->vmax_overshooting, *vel);
2160         gtk_widget_queue_resize (GTK_WIDGET (area));
2161       } else {
2162         *vel = 0.0;
2163         *scroll_to = -1;
2164       }
2165     } else if (dist > adjust->upper - adjust->page_size) {
2166       if (s) *s = FALSE;
2167
2168       dist = adjust->upper - adjust->page_size;
2169
2170       if (overshoot_max!=0) {
2171         *overshooting = 1;
2172         *scroll_to = -1;
2173         *overshot_dist = CLAMP (*overshot_dist + *vel, -overshoot_max, 0);
2174         *vel = MAX (-priv->vmax_overshooting, *vel);
2175         gtk_widget_queue_resize (GTK_WIDGET (area));
2176       } else {
2177         *vel = 0.0;
2178         *scroll_to = -1;
2179       }
2180     } else {
2181       if ((*scroll_to) != -1) {
2182         if (((inc < 0)&&(*scroll_to <= dist))||
2183             ((inc > 0)&&(*scroll_to >= dist))) {
2184           dist = *scroll_to;
2185           *scroll_to = -1;
2186           *vel = 0;
2187         }
2188       }
2189     }
2190
2191     adjust->value = dist;
2192   } else {
2193     if (!priv->button_pressed) {
2194
2195       /* When the overshoot has started we continue for
2196        * PROP_BOUNCE_STEPS more steps into the overshoot before we
2197        * reverse direction. The deceleration factor is calculated
2198        * based on the percentage distance from the first item with
2199        * each iteration, therefore always returning us to the
2200        * top/bottom most element
2201        */
2202       if (*overshot_dist > 0) {
2203
2204         if ((*overshooting < priv->bounce_steps) && (*vel > 0)) {
2205           (*overshooting)++;
2206           *vel = (((gdouble)*overshot_dist)/overshoot_max) * (*vel);
2207         } else if ((*overshooting >= priv->bounce_steps) && (*vel > 0)) {
2208           *vel *= -1;
2209         } else if ((*overshooting > 1) && (*vel < 0)) {
2210           /* we add the MIN in order to avoid very small speeds */
2211           *vel = MIN (((((gdouble)*overshot_dist)*0.8) * -1), -10.0);
2212         }
2213
2214         *overshot_dist = CLAMP (*overshot_dist + *vel, 0, overshoot_max);
2215
2216         gtk_widget_queue_resize (GTK_WIDGET (area));
2217
2218       } else if (*overshot_dist < 0) {
2219
2220         if ((*overshooting < priv->bounce_steps) && (*vel < 0)) {
2221           (*overshooting)++;
2222           *vel = (((gdouble)*overshot_dist)/overshoot_max) * (*vel) * -1;
2223         } else if ((*overshooting >= priv->bounce_steps) && (*vel < 0)) {
2224           *vel *= -1;
2225         } else if ((*overshooting > 1) && (*vel > 0)) {
2226           /* we add the MAX in order to avoid very small speeds */
2227           *vel = MAX (((((gdouble)*overshot_dist)*0.8) * -1), 10.0);
2228         }
2229
2230         *overshot_dist = CLAMP (*overshot_dist + (*vel), -overshoot_max, 0);
2231
2232         gtk_widget_queue_resize (GTK_WIDGET (area));
2233
2234       } else {
2235         *overshooting = 0;
2236         *vel = 0;
2237         gtk_widget_queue_resize (GTK_WIDGET (area));
2238       }
2239     } else {
2240
2241       gint overshot_dist_old = *overshot_dist;
2242
2243       if (*overshot_dist > 0) {
2244         *overshot_dist = CLAMP ((*overshot_dist) + inc, 0, overshoot_max);
2245       } else if (*overshot_dist < 0) {
2246         *overshot_dist = CLAMP ((*overshot_dist) + inc, -1 * overshoot_max, 0);
2247       } else {
2248         *overshooting = 0;
2249         adjust->value = CLAMP (dist,
2250                                adjust->lower,
2251                                adjust->upper -
2252                                adjust->page_size);
2253       }
2254
2255       if (*overshot_dist != overshot_dist_old)
2256         gtk_widget_queue_resize (GTK_WIDGET (area));
2257     }
2258   }
2259 }
2260
2261 static void
2262 hildon_pannable_area_scroll (HildonPannableArea *area,
2263                              gdouble x, gdouble y)
2264 {
2265   gboolean sx, sy;
2266   HildonPannableAreaPrivate *priv = area->priv;
2267   gboolean hscroll_visible, vscroll_visible;
2268   gdouble hv, vv;
2269
2270   if (gtk_bin_get_child (GTK_BIN (area)) == NULL)
2271     return;
2272
2273   vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
2274              priv->vadjust->page_size);
2275   hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
2276              priv->hadjust->page_size);
2277
2278   sx = TRUE;
2279   sy = TRUE;
2280
2281   hv = priv->hadjust->value;
2282   vv = priv->vadjust->value;
2283
2284   if (vscroll_visible) {
2285     hildon_pannable_axis_scroll (area, priv->vadjust, &priv->vel_y, y,
2286                                  &priv->overshooting_y, &priv->overshot_dist_y,
2287                                  &priv->scroll_to_y, priv->vovershoot_max, &sy);
2288   } else {
2289     priv->vel_y = 0.0;
2290     priv->scroll_to_y = -1;
2291   }
2292
2293   if (hscroll_visible) {
2294     hildon_pannable_axis_scroll (area, priv->hadjust, &priv->vel_x, x,
2295                                  &priv->overshooting_x, &priv->overshot_dist_x,
2296                                  &priv->scroll_to_x, priv->hovershoot_max, &sx);
2297   } else {
2298     priv->vel_x = 0.0;
2299     priv->scroll_to_x = -1;
2300   }
2301
2302   if (hv != priv->hadjust->value)
2303     gtk_adjustment_value_changed (priv->hadjust);
2304
2305   if (vv != priv->vadjust->value)
2306     gtk_adjustment_value_changed (priv->vadjust);
2307
2308   /* If the scroll on a particular axis wasn't succesful, reset the
2309    * initial scroll position to the new mouse co-ordinate. This means
2310    * when you get to the top of the page, dragging down works immediately.
2311    */
2312   if (priv->mode == HILDON_PANNABLE_AREA_MODE_ACCEL) {
2313       if (!sx) {
2314         priv->x = priv->ex;
2315       }
2316
2317       if (!sy) {
2318         priv->y = priv->ey;
2319       }
2320     }
2321 }
2322
2323 static gboolean
2324 hildon_pannable_area_timeout (HildonPannableArea * area)
2325 {
2326   HildonPannableAreaPrivate *priv = area->priv;
2327
2328   if ((!priv->enabled) || (priv->mode == HILDON_PANNABLE_AREA_MODE_PUSH)) {
2329     priv->idle_id = 0;
2330     g_signal_emit (area, pannable_area_signals[PANNING_FINISHED], 0);
2331
2332     return FALSE;
2333   }
2334
2335   hildon_pannable_area_scroll (area, priv->vel_x, priv->vel_y);
2336
2337   gdk_window_process_updates (GTK_WIDGET (area)->window, FALSE);
2338
2339   if (!priv->button_pressed) {
2340     /* Decelerate gradually when pointer is raised */
2341     if ((!priv->overshot_dist_y) &&
2342         (!priv->overshot_dist_x)) {
2343
2344       /* in case we move to a specific point do not decelerate when arriving */
2345       if ((priv->scroll_to_x != -1)||(priv->scroll_to_y != -1)) {
2346
2347         if (ABS (priv->vel_x) >= 1.5) {
2348           priv->vel_x *= priv->decel;
2349         }
2350
2351         if (ABS (priv->vel_y) >= 1.5) {
2352           priv->vel_y *= priv->decel;
2353         }
2354
2355       } else {
2356         if ((!priv->low_friction_mode) ||
2357             ((priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ) &&
2358              (ABS (priv->vel_x) < 0.8*priv->vmax)))
2359           priv->vel_x *= priv->decel;
2360
2361         if ((!priv->low_friction_mode) ||
2362             ((priv->mov_mode&HILDON_MOVEMENT_MODE_VERT) &&
2363              (ABS (priv->vel_y) < 0.8*priv->vmax)))
2364           priv->vel_y *= priv->decel;
2365
2366         if ((ABS (priv->vel_x) < 1.0) && (ABS (priv->vel_y) < 1.0)) {
2367           priv->vel_x = 0;
2368           priv->vel_y = 0;
2369           priv->idle_id = 0;
2370
2371           g_signal_emit (area, pannable_area_signals[PANNING_FINISHED], 0);
2372
2373           return FALSE;
2374         }
2375       }
2376     }
2377   } else if (priv->mode == HILDON_PANNABLE_AREA_MODE_AUTO) {
2378     priv->idle_id = 0;
2379
2380     return FALSE;
2381   }
2382
2383   return TRUE;
2384 }
2385
2386 static void
2387 hildon_pannable_area_calculate_velocity (gdouble *vel,
2388                                          gdouble delta,
2389                                          gdouble dist,
2390                                          gdouble vmax,
2391                                          gdouble drag_inertia,
2392                                          gdouble force,
2393                                          guint sps)
2394 {
2395   gdouble rawvel;
2396
2397   if (ABS (dist) >= RATIO_TOLERANCE) {
2398     rawvel = (dist / ABS (delta)) * force;
2399     *vel = *vel * (1 - drag_inertia) +
2400       rawvel * drag_inertia;
2401     *vel = *vel > 0 ? MIN (*vel, vmax)
2402       : MAX (*vel, -1 * vmax);
2403   }
2404 }
2405
2406 static gboolean
2407 hildon_pannable_area_motion_event_scroll_timeout (HildonPannableArea *area)
2408 {
2409   HildonPannableAreaPrivate *priv = area->priv;
2410
2411   if ((priv->motion_x != 0)||(priv->motion_y != 0))
2412     hildon_pannable_area_scroll (area, priv->motion_x, priv->motion_y);
2413
2414   priv->motion_event_scroll_timeout = 0;
2415
2416   return FALSE;
2417 }
2418
2419 static void
2420 hildon_pannable_area_motion_event_scroll (HildonPannableArea *area,
2421                                           gdouble x, gdouble y)
2422 {
2423   HildonPannableAreaPrivate *priv = area->priv;
2424
2425   if (priv->motion_event_scroll_timeout) {
2426
2427     priv->motion_x += x;
2428     priv->motion_y += y;
2429
2430   } else {
2431
2432   /* we do not delay the first event but the next ones */
2433     hildon_pannable_area_scroll (area, x, y);
2434
2435     priv->motion_x = 0;
2436     priv->motion_y = 0;
2437
2438     priv->motion_event_scroll_timeout = gdk_threads_add_timeout_full
2439       (G_PRIORITY_HIGH_IDLE + 20,
2440        (gint) (1000.0 / (gdouble) MOTION_EVENTS_PER_SECOND),
2441        (GSourceFunc) hildon_pannable_area_motion_event_scroll_timeout, area, NULL);
2442   }
2443 }
2444
2445 static void
2446 hildon_pannable_area_check_move (HildonPannableArea *area,
2447                                  GdkEventMotion * event,
2448                                  gdouble *x,
2449                                  gdouble *y)
2450 {
2451   HildonPannableAreaPrivate *priv = area->priv;
2452
2453   if (priv->first_drag && (!priv->moved) &&
2454       ((ABS (*x) > (priv->panning_threshold))
2455        || (ABS (*y) > (priv->panning_threshold)))) {
2456     priv->moved = TRUE;
2457     *x = 0;
2458     *y = 0;
2459
2460     if (priv->first_drag) {
2461         gboolean vscroll_visible;
2462         gboolean hscroll_visible;
2463
2464       if (ABS (priv->iy - event->y) >=
2465           ABS (priv->ix - event->x)) {
2466
2467         g_signal_emit (area,
2468                        pannable_area_signals[VERTICAL_MOVEMENT],
2469                        0, (priv->iy > event->y) ?
2470                        HILDON_MOVEMENT_UP :
2471                        HILDON_MOVEMENT_DOWN,
2472                        (gdouble)priv->ix, (gdouble)priv->iy);
2473
2474         vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
2475                    priv->vadjust->page_size);
2476
2477         if (!((vscroll_visible)&&
2478               (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT))) {
2479
2480           hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
2481                              priv->hadjust->page_size);
2482
2483           /* even in case we do not have to move we check if this
2484              could be a fake horizontal movement */
2485           if (!((hscroll_visible)&&
2486                 (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ)) ||
2487               (ABS (priv->iy - event->y) -
2488                ABS (priv->ix - event->x) >= priv->direction_error_margin))
2489             priv->moved = FALSE;
2490         }
2491       } else {
2492
2493         g_signal_emit (area,
2494                        pannable_area_signals[HORIZONTAL_MOVEMENT],
2495                        0, (priv->ix > event->x) ?
2496                        HILDON_MOVEMENT_LEFT :
2497                        HILDON_MOVEMENT_RIGHT,
2498                        (gdouble)priv->ix, (gdouble)priv->iy);
2499
2500         hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
2501                            priv->hadjust->page_size);
2502
2503         if (!((hscroll_visible)&&
2504               (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ))) {
2505
2506           vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
2507                              priv->vadjust->page_size);
2508
2509           /* even in case we do not have to move we check if this
2510              could be a fake vertical movement */
2511           if (!((vscroll_visible) &&
2512                 (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT)) ||
2513               (ABS (priv->ix - event->x) -
2514                ABS (priv->iy - event->y) >= priv->direction_error_margin))
2515             priv->moved = FALSE;
2516         }
2517       }
2518
2519       if ((priv->moved)&&(priv->child)) {
2520         gint pos_x, pos_y;
2521
2522         pos_x = priv->cx + (event->x - priv->ix);
2523         pos_y = priv->cy + (event->y - priv->iy);
2524
2525         synth_crossing (priv->child, pos_x, pos_y, event->x_root,
2526                         event->y_root, event->time, FALSE);
2527       }
2528
2529       if (priv->moved) {
2530         gboolean result_val;
2531
2532         g_signal_emit (area,
2533                        pannable_area_signals[PANNING_STARTED],
2534                        0, &result_val);
2535
2536         priv->moved = !result_val;
2537       }
2538     }
2539
2540     priv->first_drag = FALSE;
2541
2542     if ((priv->mode != HILDON_PANNABLE_AREA_MODE_PUSH) &&
2543         (priv->mode != HILDON_PANNABLE_AREA_MODE_AUTO)) {
2544
2545       if (!priv->idle_id)
2546         priv->idle_id = gdk_threads_add_timeout_full
2547           (G_PRIORITY_HIGH_IDLE + 20,
2548            (gint)(1000.0 / (gdouble) priv->sps),
2549            (GSourceFunc)
2550            hildon_pannable_area_timeout, area, NULL);
2551     }
2552   }
2553 }
2554
2555 static void
2556 hildon_pannable_area_handle_move (HildonPannableArea *area,
2557                                   GdkEventMotion * event,
2558                                   gdouble *x,
2559                                   gdouble *y)
2560 {
2561   HildonPannableAreaPrivate *priv = area->priv;
2562   gdouble delta;
2563
2564   switch (priv->mode) {
2565   case HILDON_PANNABLE_AREA_MODE_PUSH:
2566     /* Scroll by the amount of pixels the cursor has moved
2567      * since the last motion event.
2568      */
2569     hildon_pannable_area_motion_event_scroll (area, *x, *y);
2570     priv->x = event->x;
2571     priv->y = event->y;
2572     break;
2573   case HILDON_PANNABLE_AREA_MODE_ACCEL:
2574     /* Set acceleration relative to the initial click */
2575     priv->ex = event->x;
2576     priv->ey = event->y;
2577     priv->vel_x = ((*x > 0) ? 1 : -1) *
2578       (((ABS (*x) /
2579          (gdouble) GTK_WIDGET (area)->allocation.width) *
2580         (priv->vmax - priv->vmin)) + priv->vmin);
2581     priv->vel_y = ((*y > 0) ? 1 : -1) *
2582       (((ABS (*y) /
2583          (gdouble) GTK_WIDGET (area)->allocation.height) *
2584         (priv->vmax - priv->vmin)) + priv->vmin);
2585     break;
2586   case HILDON_PANNABLE_AREA_MODE_AUTO:
2587
2588     delta = event->time - priv->last_time;
2589
2590     if (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT) {
2591       gdouble dist = event->y - priv->y;
2592
2593       hildon_pannable_area_calculate_velocity (&priv->vel_y,
2594                                                delta,
2595                                                dist,
2596                                                priv->vmax,
2597                                                priv->drag_inertia,
2598                                                priv->force,
2599                                                priv->sps);
2600     } else {
2601       *y = 0;
2602       priv->vel_y = 0;
2603     }
2604
2605     if (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ) {
2606       gdouble dist = event->x - priv->x;
2607
2608       hildon_pannable_area_calculate_velocity (&priv->vel_x,
2609                                                delta,
2610                                                dist,
2611                                                priv->vmax,
2612                                                priv->drag_inertia,
2613                                                priv->force,
2614                                                priv->sps);
2615     } else {
2616       *x = 0;
2617       priv->vel_x = 0;
2618     }
2619
2620     hildon_pannable_area_motion_event_scroll (area, *x, *y);
2621
2622     if (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ)
2623       priv->x = event->x;
2624     if (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT)
2625       priv->y = event->y;
2626
2627     break;
2628   default:
2629     break;
2630   }
2631 }
2632
2633 static gboolean
2634 hildon_pannable_area_motion_notify_cb (GtkWidget * widget,
2635                                        GdkEventMotion * event)
2636 {
2637   HildonPannableArea *area = HILDON_PANNABLE_AREA (widget);
2638   HildonPannableAreaPrivate *priv = area->priv;
2639   gdouble x, y;
2640
2641   if (gtk_bin_get_child (GTK_BIN (widget)) == NULL)
2642     return TRUE;
2643
2644   if ((!priv->enabled) || (!priv->button_pressed) ||
2645       ((event->time == priv->last_time) && (priv->last_type == 2))) {
2646     gdk_window_get_pointer (widget->window, NULL, NULL, 0);
2647     return TRUE;
2648   }
2649
2650   if (priv->last_type == 1) {
2651     priv->first_drag = TRUE;
2652   }
2653
2654   x = event->x - priv->x;
2655   y = event->y - priv->y;
2656
2657   if (!priv->moved) {
2658     hildon_pannable_area_check_move (area, event, &x, &y);
2659   }
2660
2661   if (priv->moved) {
2662     hildon_pannable_area_handle_move (area, event, &x, &y);
2663   } else if (priv->child) {
2664     gboolean in;
2665     gint pos_x, pos_y;
2666
2667     pos_x = priv->cx + (event->x - priv->ix);
2668     pos_y = priv->cy + (event->y - priv->iy);
2669
2670     in = (((0 <= pos_x)&&(priv->child_width >= pos_x)) &&
2671           ((0 <= pos_y)&&(priv->child_height >= pos_y)));
2672
2673     if (((!priv->last_in)&&in)||((priv->last_in)&&(!in))) {
2674
2675       synth_crossing (priv->child, pos_x, pos_y, event->x_root,
2676                       event->y_root, event->time, in);
2677
2678       priv->last_in = in;
2679     }
2680   }
2681
2682   priv->last_time = event->time;
2683   priv->last_type = 2;
2684
2685   if (priv->child) {
2686     /* Send motion notify to child */
2687     event = (GdkEventMotion *) gdk_event_copy ((GdkEvent *) event);
2688     /* remove the reference we added with the copy */
2689     g_object_unref (priv->event_window);
2690     event->x = priv->cx + (event->x - priv->ix);
2691     event->y = priv->cy + (event->y - priv->iy);
2692     event->window = g_object_ref (priv->child);
2693     gdk_event_put ((GdkEvent *) event);
2694     gdk_event_free ((GdkEvent *) event);
2695   }
2696
2697   gdk_window_get_pointer (widget->window, NULL, NULL, 0);
2698
2699   return TRUE;
2700 }
2701
2702 static gboolean
2703 hildon_pannable_leave_notify_event (GtkWidget *widget,
2704                                     GdkEventCrossing *event)
2705 {
2706   HildonPannableArea *area = HILDON_PANNABLE_AREA (widget);
2707   HildonPannableAreaPrivate *priv = area->priv;
2708
2709   if ((priv->child)&&(priv->last_in)) {
2710     priv->last_in = FALSE;
2711
2712     synth_crossing (priv->child, 0, 0, event->x_root,
2713                     event->y_root, event->time, FALSE);
2714   }
2715
2716   return FALSE;
2717 }
2718
2719 static gboolean
2720 hildon_pannable_area_button_release_cb (GtkWidget * widget,
2721                                         GdkEventButton * event)
2722 {
2723   HildonPannableArea *area = HILDON_PANNABLE_AREA (widget);
2724   HildonPannableAreaPrivate *priv = area->priv;
2725   gint x, y;
2726   gdouble dx, dy;
2727   GdkWindow *child;
2728   gboolean force_fast = TRUE;
2729
2730   if  (((event->time == priv->last_time) && (priv->last_type == 3))
2731        || (gtk_bin_get_child (GTK_BIN (widget)) == NULL)
2732        || (!priv->button_pressed) || (!priv->enabled) || (event->button != 1))
2733     return TRUE;
2734
2735   /* if last event was a motion-notify we have to check the movement
2736      and launch the animation */
2737   if (priv->last_type == 2) {
2738
2739     dx = event->x - priv->x;
2740     dy = event->y - priv->y;
2741
2742     hildon_pannable_area_check_move (area, (GdkEventMotion *) event, &dx, &dy);
2743
2744     if (priv->moved) {
2745       gdouble delta = event->time - priv->last_time;
2746
2747       hildon_pannable_area_handle_move (area, (GdkEventMotion *) event, &dx, &dy);
2748
2749       /* move all the way to the last position now */
2750       if (priv->motion_event_scroll_timeout) {
2751         g_source_remove (priv->motion_event_scroll_timeout);
2752         hildon_pannable_area_motion_event_scroll_timeout (HILDON_PANNABLE_AREA (widget));
2753         priv->motion_x = 0;
2754         priv->motion_y = 0;
2755       }
2756
2757       if ((ABS (dx) < 4.0) && (delta >= CURSOR_STOPPED_TIMEOUT))
2758         priv->vel_x = 0;
2759
2760       if ((ABS (dy) < 4.0) && (delta >= CURSOR_STOPPED_TIMEOUT))
2761         priv->vel_y = 0;
2762     }
2763   }
2764
2765   /* If overshoot has been initiated with a finger down, on release set max speed */
2766   if (priv->overshot_dist_y != 0) {
2767     priv->overshooting_y = priv->bounce_steps; /* Hack to stop a bounce in the finger down case */
2768     priv->vel_y = priv->overshot_dist_y * 0.9;
2769   }
2770
2771   if (priv->overshot_dist_x != 0) {
2772     priv->overshooting_x = priv->bounce_steps; /* Hack to stop a bounce in the finger down case */
2773     priv->vel_x = priv->overshot_dist_x * 0.9;
2774   }
2775
2776   priv->button_pressed = FALSE;
2777
2778   /* if widget was moving fast in the panning, increase speed even more */
2779   if ((event->time - priv->last_press_time < FAST_CLICK) &&
2780       ((ABS (priv->old_vel_x) > priv->vmin) ||
2781        (ABS (priv->old_vel_y) > priv->vmin)) &&
2782       ((ABS (priv->old_vel_x) > MIN_ACCEL_THRESHOLD) ||
2783        (ABS (priv->old_vel_y) > MIN_ACCEL_THRESHOLD)))
2784     {
2785       gint symbol = 0;
2786
2787       if (priv->vel_x != 0)
2788         symbol = ((priv->vel_x * priv->old_vel_x) > 0) ? 1 : -1;
2789
2790       priv->vel_x = symbol *
2791         (priv->old_vel_x + ((priv->old_vel_x > 0) ? priv->accel_vel_x
2792                             : -priv->accel_vel_x));
2793
2794       symbol = 0;
2795
2796       if (priv->vel_y != 0)
2797         symbol = ((priv->vel_y * priv->old_vel_y) > 0) ? 1 : -1;
2798
2799       priv->vel_y = symbol *
2800         (priv->old_vel_y + ((priv->old_vel_y > 0) ? priv->accel_vel_y
2801                             : -priv->accel_vel_y));
2802
2803       force_fast = FALSE;
2804     }
2805
2806   if  ((ABS (priv->vel_y) >= priv->vmin) ||
2807        (ABS (priv->vel_x) >= priv->vmin)) {
2808
2809     /* we have to move because we are in overshooting position*/
2810     if (!priv->moved) {
2811       gboolean result_val;
2812
2813       g_signal_emit (area,
2814                      pannable_area_signals[PANNING_STARTED],
2815                      0, &result_val);
2816     }
2817
2818     priv->scroll_indicator_alpha = 1.0;
2819
2820     if (force_fast) {
2821       if ((ABS (priv->vel_x) > MAX_SPEED_THRESHOLD) &&
2822           (priv->accel_vel_x > MAX_SPEED_THRESHOLD))
2823         priv->vel_x = (priv->vel_x > 0) ? priv->accel_vel_x : -priv->accel_vel_x;
2824
2825       if ((ABS (priv->vel_y) > MAX_SPEED_THRESHOLD) &&
2826           (priv->accel_vel_y > MAX_SPEED_THRESHOLD))
2827         priv->vel_y = (priv->vel_y > 0) ? priv->accel_vel_y : -priv->accel_vel_y;
2828     }
2829
2830     if (!priv->idle_id)
2831       priv->idle_id = gdk_threads_add_timeout_full (G_PRIORITY_HIGH_IDLE + 20,
2832                                                     (gint) (1000.0 / (gdouble) priv->sps),
2833                                                     (GSourceFunc) hildon_pannable_area_timeout,
2834                                                     widget, NULL);
2835   } else {
2836     if (priv->center_on_child_focus_pending) {
2837       hildon_pannable_area_center_on_child_focus (area);
2838     }
2839
2840     if (priv->moved)
2841       g_signal_emit (widget, pannable_area_signals[PANNING_FINISHED], 0);
2842   }
2843
2844   area->priv->center_on_child_focus_pending = FALSE;
2845
2846   priv->scroll_indicator_event_interrupt = 0;
2847   priv->scroll_delay_counter = priv->scrollbar_fade_delay;
2848
2849   hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget),
2850                                             priv->scroll_indicator_alpha);
2851
2852   priv->last_time = event->time;
2853   priv->last_type = 3;
2854
2855   if (!priv->child) {
2856     priv->moved = FALSE;
2857     return TRUE;
2858   }
2859
2860   child =
2861     hildon_pannable_area_get_topmost (gtk_bin_get_child (GTK_BIN (widget))->window,
2862                                       event->x, event->y, &x, &y, GDK_BUTTON_RELEASE_MASK);
2863
2864   event = (GdkEventButton *) gdk_event_copy ((GdkEvent *) event);
2865   /* remove the reference we added with the copy */
2866   g_object_unref (priv->event_window);
2867   event->x = x;
2868   event->y = y;
2869
2870   /* Leave the widget if we've moved - This doesn't break selection,
2871    * but stops buttons from being clicked.
2872    */
2873   if ((child != priv->child) || (priv->moved)) {
2874     /* Send synthetic leave event */
2875     synth_crossing (priv->child, x, y, event->x_root,
2876                     event->y_root, event->time, FALSE);
2877     /* insure no click will happen for widgets that do not handle
2878        leave-notify */
2879     event->x = -16384;
2880     event->y = -16384;
2881     /* Send synthetic button release event */
2882     ((GdkEventAny *) event)->window = g_object_ref (priv->child);
2883     gdk_event_put ((GdkEvent *) event);
2884   } else {
2885     /* Send synthetic button release event */
2886     ((GdkEventAny *) event)->window = g_object_ref (child);
2887     gdk_event_put ((GdkEvent *) event);
2888     /* Send synthetic leave event */
2889     synth_crossing (priv->child, x, y, event->x_root,
2890                     event->y_root, event->time, FALSE);
2891   }
2892   g_object_remove_weak_pointer ((GObject *) priv->child,
2893                                 (gpointer) & priv->child);
2894
2895   priv->moved = FALSE;
2896   gdk_event_free ((GdkEvent *) event);
2897
2898   return TRUE;
2899 }
2900
2901 /* utility event handler */
2902 static gboolean
2903 hildon_pannable_area_scroll_cb (GtkWidget *widget,
2904                                 GdkEventScroll *event)
2905 {
2906   GtkAdjustment *adj = NULL;
2907   HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
2908
2909   if ((!priv->enabled) ||
2910       (gtk_bin_get_child (GTK_BIN (widget)) == NULL))
2911     return TRUE;
2912
2913   priv->scroll_indicator_event_interrupt = 0;
2914   priv->scroll_delay_counter = priv->scrollbar_fade_delay + 20;
2915
2916   hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget), 1.0);
2917
2918   /* Stop inertial scrolling */
2919   if (priv->idle_id) {
2920     priv->vel_x = 0.0;
2921     priv->vel_y = 0.0;
2922     priv->overshooting_x = 0;
2923     priv->overshooting_y = 0;
2924
2925     if ((priv->overshot_dist_x>0)||(priv->overshot_dist_y>0)) {
2926       priv->overshot_dist_x = 0;
2927       priv->overshot_dist_y = 0;
2928
2929       gtk_widget_queue_resize (GTK_WIDGET (widget));
2930     }
2931
2932     g_signal_emit (widget, pannable_area_signals[PANNING_FINISHED], 0);
2933
2934     g_source_remove (priv->idle_id);
2935     priv->idle_id = 0;
2936   }
2937
2938   if (event->direction == GDK_SCROLL_UP || event->direction == GDK_SCROLL_DOWN)
2939     adj = priv->vadjust;
2940   else
2941     adj = priv->hadjust;
2942
2943   if (adj)
2944     {
2945       gdouble delta, new_value;
2946
2947       /* from gtkrange.c calculate delta*/
2948       delta = pow (adj->page_size, 2.0 / 3.0);
2949
2950       if (event->direction == GDK_SCROLL_UP ||
2951           event->direction == GDK_SCROLL_LEFT)
2952         delta = - delta;
2953
2954       new_value = CLAMP (adj->value + delta, adj->lower, adj->upper - adj->page_size);
2955
2956       gtk_adjustment_set_value (adj, new_value);
2957     }
2958
2959   return TRUE;
2960 }
2961
2962 static void
2963 hildon_pannable_area_child_mapped (GtkWidget *widget,
2964                                    GdkEvent  *event,
2965                                    gpointer user_data)
2966 {
2967   HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (user_data)->priv;
2968
2969   if (priv->event_window != NULL && priv->enabled)
2970     gdk_window_raise (priv->event_window);
2971 }
2972
2973 static void
2974 hildon_pannable_area_add (GtkContainer *container, GtkWidget *child)
2975 {
2976   HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (container)->priv;
2977
2978   g_return_if_fail (gtk_bin_get_child (GTK_BIN (container)) == NULL);
2979
2980   gtk_widget_set_parent (child, GTK_WIDGET (container));
2981   GTK_BIN (container)->child = child;
2982
2983   g_signal_connect_after (child, "map-event",
2984                           G_CALLBACK (hildon_pannable_area_child_mapped),
2985                           container);
2986
2987   if (!gtk_widget_set_scroll_adjustments (child, priv->hadjust, priv->vadjust)) {
2988     g_warning ("%s: cannot add non scrollable widget, "
2989                "wrap it in a viewport", __FUNCTION__);
2990   }
2991 }
2992
2993 /* call this function if you are not panning */
2994 static void
2995 hildon_pannable_area_center_on_child_focus      (HildonPannableArea *area)
2996 {
2997   GtkWidget *focused_child = NULL;
2998   GtkWidget *window = NULL;
2999
3000   window = gtk_widget_get_toplevel (GTK_WIDGET (area));
3001
3002   if (GTK_WIDGET_TOPLEVEL (window)) {
3003     focused_child = gtk_window_get_focus (GTK_WINDOW (window));
3004   }
3005
3006   if (focused_child) {
3007     hildon_pannable_area_scroll_to_child (area, focused_child);
3008   }
3009 }
3010
3011 static void
3012 hildon_pannable_area_set_focus_child            (GtkContainer     *container,
3013                                                  GtkWidget        *child)
3014 {
3015   HildonPannableArea *area = HILDON_PANNABLE_AREA (container);
3016
3017   if (!area->priv->center_on_child_focus) {
3018     return;
3019   }
3020
3021   if (GTK_IS_WIDGET (child)) {
3022     area->priv->center_on_child_focus_pending = TRUE;
3023   }
3024 }
3025
3026 static void
3027 hildon_pannable_area_remove (GtkContainer *container, GtkWidget *child)
3028 {
3029   g_return_if_fail (HILDON_IS_PANNABLE_AREA (container));
3030   g_return_if_fail (child != NULL);
3031   g_return_if_fail (gtk_bin_get_child (GTK_BIN (container)) == child);
3032
3033   gtk_widget_set_scroll_adjustments (child, NULL, NULL);
3034
3035   g_signal_handlers_disconnect_by_func (child,
3036                                         hildon_pannable_area_child_mapped,
3037                                         container);
3038
3039   /* chain parent class handler to remove child */
3040   GTK_CONTAINER_CLASS (hildon_pannable_area_parent_class)->remove (container, child);
3041 }
3042
3043 /*
3044  * This method calculates a factor necessary to determine the initial distance
3045  * to jump in hildon_pannable_area_scroll_to(). For fixed time and frames per
3046  * second, we know in how many frames 'n' we need to reach the destination
3047  * point. We know that, for a distance d,
3048  *
3049  *   d = d_0 + d_1 + ... + d_n
3050  *
3051  * where d_i is the distance travelled in the i-th frame and decel_factor is
3052  * the deceleration factor. This can be rewritten as
3053  *
3054  *   d = d_0 + (d_0 * decel_factor) + ... + (d_n-1 * decel_factor),
3055  *
3056  * since the distance travelled on each frame is the distance travelled in the
3057  * previous frame reduced by the deceleration factor. Reducing this and
3058  * factoring d_0 out, we get
3059  *
3060  *   d = d_0 (1 + decel_factor + ... + decel_factor^(n-1)).
3061  *
3062  * Since the sum is independent of the distance to be travelled, we can define
3063  * vel_factor as
3064  *
3065  *   vel_factor = 1 + decel_factor + ... + decel_factor^(n-1).
3066  *
3067  * That's the gem we calculate in this method.
3068  */
3069 static void
3070 hildon_pannable_calculate_vel_factor (HildonPannableArea * self)
3071 {
3072   HildonPannableAreaPrivate *priv = self->priv;
3073   gfloat fct = 1;
3074   gfloat fct_i = 1;
3075   gint i, n;
3076
3077   n = ceil (priv->sps * priv->scroll_time);
3078
3079   for (i = 1; i < n && fct_i >= RATIO_TOLERANCE; i++) {
3080     fct_i *= priv->decel;
3081     fct += fct_i;
3082   }
3083
3084   priv->vel_factor = fct;
3085 }
3086
3087 /**
3088  * hildon_pannable_area_new:
3089  *
3090  * Create a new pannable area widget
3091  *
3092  * Returns: the newly created #HildonPannableArea
3093  *
3094  * Since: 2.2
3095  */
3096
3097 GtkWidget *
3098 hildon_pannable_area_new (void)
3099 {
3100   return g_object_new (HILDON_TYPE_PANNABLE_AREA, NULL);
3101 }
3102
3103 /**
3104  * hildon_pannable_area_new_full:
3105  * @mode: #HildonPannableAreaMode
3106  * @enabled: Value for the enabled property
3107  * @vel_min: Value for the velocity-min property
3108  * @vel_max: Value for the velocity-max property
3109  * @decel: Value for the deceleration property
3110  * @sps: Value for the sps property
3111  *
3112  * Create a new #HildonPannableArea widget and set various properties
3113  *
3114  * returns: the newly create #HildonPannableArea
3115  *
3116  * Since: 2.2
3117  */
3118
3119 GtkWidget *
3120 hildon_pannable_area_new_full (gint mode, gboolean enabled,
3121                                gdouble vel_min, gdouble vel_max,
3122                                gdouble decel, guint sps)
3123 {
3124   return g_object_new (HILDON_TYPE_PANNABLE_AREA,
3125                        "mode", mode,
3126                        "enabled", enabled,
3127                        "velocity_min", vel_min,
3128                        "velocity_max", vel_max,
3129                        "deceleration", decel, "sps", sps, NULL);
3130 }
3131
3132 /**
3133  * hildon_pannable_area_add_with_viewport:
3134  * @area: A #HildonPannableArea
3135  * @child: Child widget to add to the viewport
3136  *
3137  * Convenience function used to add a child to a #GtkViewport, and add the
3138  * viewport to the scrolled window.
3139  *
3140  * Since: 2.2
3141  */
3142
3143 void
3144 hildon_pannable_area_add_with_viewport (HildonPannableArea * area,
3145                                         GtkWidget * child)
3146 {
3147   GtkBin *bin;
3148   GtkWidget *viewport;
3149
3150   g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3151   g_return_if_fail (GTK_IS_WIDGET (child));
3152   g_return_if_fail (child->parent == NULL);
3153
3154   bin = GTK_BIN (area);
3155
3156   if (bin->child != NULL)
3157     {
3158       g_return_if_fail (GTK_IS_VIEWPORT (bin->child));
3159       g_return_if_fail (GTK_BIN (bin->child)->child == NULL);
3160
3161       viewport = bin->child;
3162     }
3163   else
3164     {
3165       HildonPannableAreaPrivate *priv = area->priv;
3166
3167       viewport = gtk_viewport_new (priv->hadjust,
3168                                    priv->vadjust);
3169       gtk_viewport_set_shadow_type (GTK_VIEWPORT (viewport), GTK_SHADOW_NONE);
3170       gtk_container_add (GTK_CONTAINER (area), viewport);
3171     }
3172
3173   gtk_widget_show (viewport);
3174   gtk_container_add (GTK_CONTAINER (viewport), child);
3175 }
3176
3177 /**
3178  * hildon_pannable_area_scroll_to:
3179  * @area: A #HildonPannableArea.
3180  * @x: The x coordinate of the destination point or -1 to ignore this axis.
3181  * @y: The y coordinate of the destination point or -1 to ignore this axis.
3182  *
3183  * Smoothly scrolls @area to ensure that (@x, @y) is a visible point
3184  * on the widget. To move in only one coordinate, you must set the other one
3185  * to -1. Notice that, in %HILDON_PANNABLE_AREA_MODE_PUSH mode, this function
3186  * works just like hildon_pannable_area_jump_to().
3187  *
3188  * This function is useful if you need to present the user with a particular
3189  * element inside a scrollable widget, like #GtkTreeView. For instance,
3190  * the following example shows how to scroll inside a #GtkTreeView to
3191  * make visible an item, indicated by the #GtkTreeIter @iter.
3192  *
3193  * <example>
3194  * <programlisting>
3195  *  GtkTreePath *path;
3196  *  GdkRectangle *rect;
3197  *  <!-- -->
3198  *  path = gtk_tree_model_get_path (model, &amp;iter);
3199  *  gtk_tree_view_get_background_area (GTK_TREE_VIEW (treeview),
3200  *                                     path, NULL, &amp;rect);
3201  *  gtk_tree_view_convert_bin_window_to_tree_coords (GTK_TREE_VIEW (treeview),
3202  *                                                   0, rect.y, NULL, &amp;y);
3203  *  hildon_pannable_area_scroll_to (panarea, -1, y);
3204  *  gtk_tree_path_free (path);
3205  * </programlisting>
3206  * </example>
3207  *
3208  * If you want to present a child widget in simpler scenarios,
3209  * use hildon_pannable_area_scroll_to_child() instead.
3210  *
3211  * There is a precondition to this function: the widget must be
3212  * already realized. Check the hildon_pannable_area_jump_to_child() for
3213  * more tips regarding how to call this function during
3214  * initialization.
3215  *
3216  * Since: 2.2
3217  **/
3218 void
3219 hildon_pannable_area_scroll_to (HildonPannableArea *area,
3220                                 const gint x, const gint y)
3221 {
3222   HildonPannableAreaPrivate *priv;
3223   gint width, height;
3224   gint dist_x, dist_y;
3225   gboolean hscroll_visible, vscroll_visible;
3226
3227   g_return_if_fail (GTK_WIDGET_REALIZED (area));
3228   g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3229
3230   priv = area->priv;
3231
3232   vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
3233              priv->vadjust->page_size);
3234   hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
3235              priv->hadjust->page_size);
3236
3237   if (((!vscroll_visible)&&(!hscroll_visible)) ||
3238       (x == -1 && y == -1)) {
3239     return;
3240   }
3241
3242   if (priv->mode == HILDON_PANNABLE_AREA_MODE_PUSH)
3243     hildon_pannable_area_jump_to (area, x, y);
3244
3245   width = priv->hadjust->upper - priv->hadjust->lower;
3246   height = priv->vadjust->upper - priv->vadjust->lower;
3247
3248   g_return_if_fail (x < width || y < height);
3249
3250   if ((x > -1)&&(hscroll_visible)) {
3251     priv->scroll_to_x = CLAMP (x - priv->hadjust->page_size/2,
3252                                priv->hadjust->lower,
3253                                priv->hadjust->upper - priv->hadjust->page_size);
3254     dist_x = priv->scroll_to_x - priv->hadjust->value;
3255     if (dist_x == 0) {
3256       priv->scroll_to_x = -1;
3257     } else {
3258       priv->vel_x = - dist_x/priv->vel_factor;
3259     }
3260   } else {
3261     priv->scroll_to_x = -1;
3262   }
3263
3264   if ((y > -1)&&(vscroll_visible)) {
3265     priv->scroll_to_y = CLAMP (y - priv->vadjust->page_size/2,
3266                                priv->vadjust->lower,
3267                                priv->vadjust->upper - priv->vadjust->page_size);
3268     dist_y = priv->scroll_to_y - priv->vadjust->value;
3269     if (dist_y == 0) {
3270       priv->scroll_to_y = -1;
3271     } else {
3272       priv->vel_y = - dist_y/priv->vel_factor;
3273     }
3274   } else {
3275     priv->scroll_to_y = y;
3276   }
3277
3278   if ((priv->scroll_to_y == -1) && (priv->scroll_to_x == -1)) {
3279     return;
3280   }
3281
3282   hildon_pannable_area_launch_fade_timeout (area, 1.0);
3283
3284   if (!priv->idle_id)
3285     priv->idle_id = gdk_threads_add_timeout_full (G_PRIORITY_HIGH_IDLE + 20,
3286                                                   (gint) (1000.0 / (gdouble) priv->sps),
3287                                                   (GSourceFunc) hildon_pannable_area_timeout,
3288                                                   area, NULL);
3289 }
3290
3291 /**
3292  * hildon_pannable_area_jump_to:
3293  * @area: A #HildonPannableArea.
3294  * @x: The x coordinate of the destination point or -1 to ignore this axis.
3295  * @y: The y coordinate of the destination point or -1 to ignore this axis.
3296  *
3297  * Jumps the position of @area to ensure that (@x, @y) is a visible
3298  * point in the widget. In order to move in only one coordinate, you
3299  * must set the other one to -1. See hildon_pannable_area_scroll_to()
3300  * function for an example of how to calculate the position of
3301  * children in scrollable widgets like #GtkTreeview.
3302  *
3303  * There is a precondition to this function: the widget must be
3304  * already realized. Check the hildon_pannable_area_jump_to_child() for
3305  * more tips regarding how to call this function during
3306  * initialization.
3307  *
3308  * Since: 2.2
3309  **/
3310 void
3311 hildon_pannable_area_jump_to (HildonPannableArea *area,
3312                               const gint x, const gint y)
3313 {
3314   HildonPannableAreaPrivate *priv;
3315   gint width, height;
3316   gdouble hv, vv;
3317
3318   g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3319   g_return_if_fail (GTK_WIDGET_REALIZED (area));
3320   g_return_if_fail (x >= -1 && y >= -1);
3321
3322   if (x == -1 && y == -1) {
3323     return;
3324   }
3325
3326   priv = area->priv;
3327
3328   width = priv->hadjust->upper - priv->hadjust->lower;
3329   height = priv->vadjust->upper - priv->vadjust->lower;
3330
3331   g_return_if_fail (x < width || y < height);
3332
3333   hv = priv->hadjust->value;
3334   vv = priv->vadjust->value;
3335
3336   if (x != -1) {
3337     gdouble jump_to = x - priv->hadjust->page_size/2;
3338
3339     priv->hadjust->value = CLAMP (jump_to,
3340                                   priv->hadjust->lower,
3341                                   priv->hadjust->upper -
3342                                   priv->hadjust->page_size);
3343   }
3344
3345   if (y != -1) {
3346     gdouble jump_to =  y - priv->vadjust->page_size/2;
3347
3348     priv->vadjust->value = CLAMP (jump_to,
3349                                   priv->vadjust->lower,
3350                                   priv->vadjust->upper -
3351                                   priv->vadjust->page_size);
3352   }
3353
3354   if (hv != priv->hadjust->value)
3355     gtk_adjustment_value_changed (priv->hadjust);
3356
3357   if (vv != priv->vadjust->value)
3358     gtk_adjustment_value_changed (priv->vadjust);
3359
3360   priv->scroll_indicator_alpha = 1.0;
3361
3362   if (priv->scroll_indicator_timeout) {
3363     g_source_remove (priv->scroll_indicator_timeout);
3364     priv->scroll_indicator_timeout = 0;
3365   }
3366
3367   if (priv->idle_id) {
3368     priv->vel_x = 0.0;
3369     priv->vel_y = 0.0;
3370     priv->overshooting_x = 0;
3371     priv->overshooting_y = 0;
3372
3373     if ((priv->overshot_dist_x>0)||(priv->overshot_dist_y>0)) {
3374       priv->overshot_dist_x = 0;
3375       priv->overshot_dist_y = 0;
3376
3377       gtk_widget_queue_resize (GTK_WIDGET (area));
3378     }
3379
3380     g_signal_emit (area, pannable_area_signals[PANNING_FINISHED], 0);
3381     g_source_remove (priv->idle_id);
3382     priv->idle_id = 0;
3383   }
3384 }
3385
3386 /**
3387  * hildon_pannable_area_scroll_to_child:
3388  * @area: A #HildonPannableArea.
3389  * @child: A #GtkWidget, descendant of @area.
3390  *
3391  * Smoothly scrolls until @child is visible inside @area. @child must
3392  * be a descendant of @area. If you need to scroll inside a scrollable
3393  * widget, e.g., #GtkTreeview, see hildon_pannable_area_scroll_to().
3394  *
3395  * There is a precondition to this function: the widget must be
3396  * already realized. Check the hildon_pannable_area_jump_to_child() for
3397  * more tips regarding how to call this function during
3398  * initialization.
3399  *
3400  * Since: 2.2
3401  **/
3402 void
3403 hildon_pannable_area_scroll_to_child (HildonPannableArea *area, GtkWidget *child)
3404 {
3405   GtkWidget *bin_child;
3406   gint x, y;
3407
3408   g_return_if_fail (GTK_WIDGET_REALIZED (area));
3409   g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3410   g_return_if_fail (GTK_IS_WIDGET (child));
3411   g_return_if_fail (gtk_widget_is_ancestor (child, GTK_WIDGET (area)));
3412
3413   if (GTK_BIN (area)->child == NULL)
3414     return;
3415
3416   /* We need to get to check the child of the inside the area */
3417   bin_child = GTK_BIN (area)->child;
3418
3419   /* we check if we added a viewport */
3420   if (GTK_IS_VIEWPORT (bin_child)) {
3421     bin_child = GTK_BIN (bin_child)->child;
3422   }
3423
3424   if (gtk_widget_translate_coordinates (child, bin_child, 0, 0, &x, &y))
3425     hildon_pannable_area_scroll_to (area, x, y);
3426 }
3427
3428 /**
3429  * hildon_pannable_area_jump_to_child:
3430  * @area: A #HildonPannableArea.
3431  * @child: A #GtkWidget, descendant of @area.
3432  *
3433  * Jumps to make sure @child is visible inside @area. @child must
3434  * be a descendant of @area. If you want to move inside a scrollable
3435  * widget, like, #GtkTreeview, see hildon_pannable_area_scroll_to().
3436  *
3437  * There is a precondition to this function: the widget must be
3438  * already realized. You can control if the widget is ready with the
3439  * GTK_WIDGET_REALIZED macro. If you want to call this function during
3440  * the initialization process of the widget do it inside a callback to
3441  * the ::realize signal, using g_signal_connect_after() function.
3442  *
3443  * Since: 2.2
3444  **/
3445 void
3446 hildon_pannable_area_jump_to_child (HildonPannableArea *area, GtkWidget *child)
3447 {
3448   GtkWidget *bin_child;
3449   gint x, y;
3450
3451   g_return_if_fail (GTK_WIDGET_REALIZED (area));
3452   g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3453   g_return_if_fail (GTK_IS_WIDGET (child));
3454   g_return_if_fail (gtk_widget_is_ancestor (child, GTK_WIDGET (area)));
3455
3456   if (gtk_bin_get_child (GTK_BIN (area)) == NULL)
3457     return;
3458
3459   /* We need to get to check the child of the inside the area */
3460   bin_child = gtk_bin_get_child (GTK_BIN (area));
3461
3462   /* we check if we added a viewport */
3463   if (GTK_IS_VIEWPORT (bin_child)) {
3464     bin_child = gtk_bin_get_child (GTK_BIN (bin_child));
3465   }
3466
3467   if (gtk_widget_translate_coordinates (child, bin_child, 0, 0, &x, &y))
3468     hildon_pannable_area_jump_to (area, x, y);
3469 }
3470
3471 /**
3472  * hildon_pannable_get_child_widget_at:
3473  * @area: A #HildonPannableArea.
3474  * @x: horizontal coordinate of the point
3475  * @y: vertical coordinate of the point
3476  *
3477  * Get the widget at the point (x, y) inside the pannable area. In
3478  * case no widget found it returns NULL.
3479  *
3480  * returns: the #GtkWidget if we find a widget, NULL in any other case
3481  *
3482  * Since: 2.2
3483  **/
3484 GtkWidget*
3485 hildon_pannable_get_child_widget_at (HildonPannableArea *area,
3486                                      gdouble x, gdouble y)
3487 {
3488   GdkWindow *window = NULL;
3489   GtkWidget *child_widget = NULL;
3490
3491   window = hildon_pannable_area_get_topmost
3492     (gtk_bin_get_child (GTK_BIN (area))->window,
3493      x, y, NULL, NULL, GDK_ALL_EVENTS_MASK);
3494
3495   gdk_window_get_user_data (window, (gpointer) &child_widget);
3496
3497   return child_widget;
3498 }
3499
3500
3501 /**
3502  * hildon_pannable_area_get_hadjustment:
3503  * @area: A #HildonPannableArea.
3504  *
3505  * Returns the horizontal adjustment. This adjustment is the internal
3506  * widget adjustment used to control the animations. Do not modify it
3507  * directly to change the position of the pannable, to do that use the
3508  * pannable API. If you modify the object directly it could cause
3509  * artifacts in the animations.
3510  *
3511  * returns: The horizontal #GtkAdjustment
3512  *
3513  * Since: 2.2
3514  **/
3515 GtkAdjustment*
3516 hildon_pannable_area_get_hadjustment            (HildonPannableArea *area)
3517 {
3518
3519   g_return_val_if_fail (HILDON_IS_PANNABLE_AREA (area), NULL);
3520
3521   return area->priv->hadjust;
3522 }
3523
3524 /**
3525  * hildon_pannable_area_get_vadjustment:
3526  * @area: A #HildonPannableArea.
3527  *
3528  * Returns the vertical adjustment. This adjustment is the internal
3529  * widget adjustment used to control the animations. Do not modify it
3530  * directly to change the position of the pannable, to do that use the
3531  * pannable API. If you modify the object directly it could cause
3532  * artifacts in the animations.
3533  *
3534  * returns: The vertical #GtkAdjustment
3535  *
3536  * Since: 2.2
3537  **/
3538 GtkAdjustment*
3539 hildon_pannable_area_get_vadjustment            (HildonPannableArea *area)
3540 {
3541   g_return_val_if_fail (HILDON_IS_PANNABLE_AREA (area), NULL);
3542
3543   return area->priv->vadjust;
3544 }
3545
3546
3547 /**
3548  * hildon_pannable_area_get_size_request_policy:
3549  * @area: A #HildonPannableArea.
3550  *
3551  * This function returns the current size request policy of the
3552  * widget. That policy controls the way the size_request is done in
3553  * the pannable area. Check
3554  * hildon_pannable_area_set_size_request_policy() for a more detailed
3555  * explanation.
3556  *
3557  * returns: the policy is currently being used in the widget
3558  * #HildonSizeRequestPolicy.
3559  *
3560  * Since: 2.2
3561  **/
3562 HildonSizeRequestPolicy
3563 hildon_pannable_area_get_size_request_policy (HildonPannableArea *area)
3564 {
3565   HildonPannableAreaPrivate *priv;
3566
3567   g_return_val_if_fail (HILDON_IS_PANNABLE_AREA (area), FALSE);
3568
3569   priv = area->priv;
3570
3571   return priv->size_request_policy;
3572 }
3573
3574 /**
3575  * hildon_pannable_area_set_size_request_policy:
3576  * @area: A #HildonPannableArea.
3577  * @size_request_policy: One of the allowed #HildonSizeRequestPolicy
3578  *
3579  * This function sets the pannable area size request policy. That
3580  * policy controls the way the size_request is done in the pannable
3581  * area. Pannable can use the size request of its children
3582  * (#HILDON_SIZE_REQUEST_CHILDREN) or the minimum size required for
3583  * the area itself (#HILDON_SIZE_REQUEST_MINIMUM), the latter is the
3584  * default. Recall this size depends on the scrolling policy you are
3585  * requesting to the pannable area, if you set #GTK_POLICY_NEVER this
3586  * parameter will not have any effect with
3587  * #HILDON_SIZE_REQUEST_MINIMUM set.
3588  *
3589  * Since: 2.2
3590  *
3591  * Deprecated: This method and the policy request is deprecated, DO
3592  * NOT use it in future code, the only policy properly supported in
3593  * gtk+ nowadays is the minimum size. Use #gtk_window_set_default_size
3594  * or #gtk_window_set_geometry_hints with the proper size in your case
3595  * to define the height of your dialogs.
3596  **/
3597 void
3598 hildon_pannable_area_set_size_request_policy (HildonPannableArea *area,
3599                                               HildonSizeRequestPolicy size_request_policy)
3600 {
3601   HildonPannableAreaPrivate *priv;
3602
3603   g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3604
3605   priv = area->priv;
3606
3607   if (priv->size_request_policy == size_request_policy)
3608     return;
3609
3610   priv->size_request_policy = size_request_policy;
3611
3612   gtk_widget_queue_resize (GTK_WIDGET (area));
3613
3614   g_object_notify (G_OBJECT (area), "size-request-policy");
3615 }
3616
3617 /**
3618  * hildon_pannable_area_get_center_on_child_focus
3619  * @area: A #HildonPannableArea
3620  *
3621  * Gets the @area #HildonPannableArea:center-on-child-focus property
3622  * value.
3623  *
3624  * See #HildonPannableArea:center-on-child-focus for more information.
3625  *
3626  * Returns: the @area #HildonPannableArea:center-on-child-focus value
3627  *
3628  * Since: 2.2
3629  **/
3630 gboolean
3631 hildon_pannable_area_get_center_on_child_focus  (HildonPannableArea *area)
3632 {
3633   g_return_val_if_fail (HILDON_IS_PANNABLE_AREA (area), FALSE);
3634
3635   return area->priv->center_on_child_focus;
3636 }
3637
3638 /**
3639  * hildon_pannable_area_set_center_on_child_focus
3640  * @area: A #HildonPannableArea
3641  * @value: the new value
3642  *
3643  * Sets the @area #HildonPannableArea:center-on-child-focus property
3644  * to @value.
3645  *
3646  * See #HildonPannableArea:center-on-child-focus for more information.
3647  *
3648  * Since: 2.2
3649  **/
3650 void
3651 hildon_pannable_area_set_center_on_child_focus  (HildonPannableArea *area,
3652                                                  gboolean value)
3653 {
3654   g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3655
3656   area->priv->center_on_child_focus = value;
3657 }