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