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