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