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