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