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