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