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