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