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