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