1bcfdd33b8b420995c99ec072045c3515f202488
[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         if (!priv->idle_id)
1075           priv->idle_id = gdk_threads_add_timeout ((gint) (1000.0 / (gdouble) priv->sps),
1076                                                    (GSourceFunc)
1077                                                    hildon_pannable_area_timeout, widget);
1078       }
1079     }
1080
1081     if (priv->vscroll_visible || priv->hscroll_visible) {
1082       priv->scroll_indicator_alpha = 1.0;
1083
1084       if (!priv->scroll_indicator_timeout)
1085         priv->scroll_indicator_timeout =
1086           gdk_threads_add_timeout ((gint) (1000.0 / (gdouble) SCROLL_FADE_TIMEOUT),
1087                                    (GSourceFunc) hildon_pannable_area_scroll_indicator_fade,
1088                                    widget);
1089     }
1090   }
1091 }
1092
1093 static void
1094 hildon_pannable_area_redraw (HildonPannableArea * area)
1095 {
1096   HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (area)->priv;
1097
1098   hildon_pannable_area_refresh (area);
1099
1100   /* Redraw scroll indicators */
1101   if (GTK_WIDGET_DRAWABLE (area)) {
1102       if (priv->hscroll_visible) {
1103         gdk_window_invalidate_rect (GTK_WIDGET (area)->window,
1104                                     &priv->hscroll_rect, FALSE);
1105       }
1106
1107       if (priv->vscroll_visible) {
1108         gdk_window_invalidate_rect (GTK_WIDGET (area)->window,
1109                                     &priv->vscroll_rect, FALSE);
1110       }
1111   }
1112 }
1113
1114 static gboolean
1115 hildon_pannable_area_scroll_indicator_fade(HildonPannableArea * area)
1116 {
1117   HildonPannableAreaPrivate *priv = area->priv;
1118
1119   /* if moving do not fade out */
1120   if (((ABS (priv->vel_y)>1.0)||
1121        (ABS (priv->vel_x)>1.0))&&(!priv->clicked)) {
1122
1123     return TRUE;
1124   }
1125
1126   if (priv->scroll_indicator_event_interrupt) {
1127     /* Stop a fade out, and fade back in */
1128     if (priv->scroll_indicator_alpha > 0.9) {
1129       priv->scroll_indicator_alpha = 1.0;
1130       priv->scroll_indicator_timeout = 0;
1131
1132       return FALSE;
1133     } else {
1134       priv->scroll_indicator_alpha += 0.2;
1135       hildon_pannable_area_redraw (area);
1136
1137       return TRUE;
1138     }
1139   }
1140
1141   if ((priv->scroll_indicator_alpha > 0.9) &&
1142       (priv->scroll_delay_counter > 0)) {
1143     priv->scroll_delay_counter--;
1144
1145     return TRUE;
1146   }
1147
1148   if (!priv->scroll_indicator_event_interrupt) {
1149     /* Continue fade out */
1150     if (priv->scroll_indicator_alpha < 0.1) {
1151       priv->scroll_indicator_timeout = 0;
1152       priv->scroll_indicator_alpha = 0.0;
1153
1154       return FALSE;
1155     } else {
1156       priv->scroll_indicator_alpha -= 0.2;
1157       hildon_pannable_area_redraw (area);
1158
1159       return TRUE;
1160     }
1161   }
1162
1163   return TRUE;
1164 }
1165
1166 static gboolean
1167 hildon_pannable_area_expose_event (GtkWidget * widget,
1168                                    GdkEventExpose * event)
1169 {
1170
1171   HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1172   GdkColor back_color = widget->style->bg[GTK_STATE_NORMAL];
1173   GdkColor scroll_color = widget->style->base[GTK_STATE_SELECTED];
1174
1175   if (gtk_bin_get_child (GTK_BIN (widget))) {
1176
1177     if (priv->scroll_indicator_alpha > 0.1) {
1178       if (priv->vscroll_visible) {
1179         hildon_pannable_draw_vscroll (widget, &back_color, &scroll_color);
1180       }
1181       if (priv->hscroll_visible) {
1182         hildon_pannable_draw_hscroll (widget, &back_color, &scroll_color);
1183       }
1184     }
1185
1186     /* draw overshooting rectangles */
1187     if (priv->overshot_dist_y > 0) {
1188       gint overshot_height;
1189
1190       overshot_height = MIN (priv->overshot_dist_y, widget->allocation.height -
1191                              (priv->hscroll_visible ? priv->hscroll_rect.height : 0));
1192
1193       gdk_draw_rectangle (widget->window,
1194                           widget->style->bg_gc[GTK_STATE_NORMAL],
1195                           TRUE,
1196                           widget->allocation.x,
1197                           widget->allocation.y,
1198                           widget->allocation.width -
1199                           (priv->vscroll_visible ? priv->vscroll_rect.width : 0),
1200                           overshot_height);
1201     } else if (priv->overshot_dist_y < 0) {
1202       gint overshot_height;
1203       gint overshot_y;
1204
1205       overshot_height =
1206         MAX (priv->overshot_dist_y,
1207              -(widget->allocation.height -
1208                (priv->hscroll_visible ? priv->hscroll_rect.height : 0)));
1209
1210       overshot_y = MAX (widget->allocation.y +
1211                         widget->allocation.height +
1212                         overshot_height -
1213                         (priv->hscroll_visible ? priv->hscroll_rect.height : 0), 0);
1214
1215       gdk_draw_rectangle (widget->window,
1216                           widget->style->bg_gc[GTK_STATE_NORMAL],
1217                           TRUE,
1218                           widget->allocation.x,
1219                           overshot_y,
1220                           widget->allocation.width -
1221                           priv->vscroll_rect.width,
1222                           -overshot_height);
1223     }
1224
1225     if (priv->overshot_dist_x > 0) {
1226       gint overshot_width;
1227
1228       overshot_width = MIN (priv->overshot_dist_x, widget->allocation.width -
1229                              (priv->vscroll_visible ? priv->vscroll_rect.width : 0));
1230
1231       gdk_draw_rectangle (widget->window,
1232                           widget->style->bg_gc[GTK_STATE_NORMAL],
1233                           TRUE,
1234                           widget->allocation.x,
1235                           widget->allocation.y,
1236                           overshot_width,
1237                           widget->allocation.height -
1238                           (priv->hscroll_visible ? priv->hscroll_rect.height : 0));
1239     } else if (priv->overshot_dist_x < 0) {
1240       gint overshot_width;
1241       gint overshot_x;
1242
1243       overshot_width =
1244         MAX (priv->overshot_dist_x,
1245              -(widget->allocation.width -
1246                (priv->vscroll_visible ? priv->vscroll_rect.width : 0)));
1247
1248       overshot_x = MAX (widget->allocation.x +
1249                         widget->allocation.width +
1250                         overshot_width -
1251                         (priv->vscroll_visible ? priv->vscroll_rect.width : 0), 0);
1252
1253       gdk_draw_rectangle (widget->window,
1254                           widget->style->bg_gc[GTK_STATE_NORMAL],
1255                           TRUE,
1256                           overshot_x,
1257                           widget->allocation.y,
1258                           -overshot_width,
1259                           widget->allocation.height -
1260                           priv->hscroll_rect.height);
1261     }
1262
1263   }
1264
1265   if (G_UNLIKELY (priv->initial_effect)) {
1266
1267     hildon_pannable_area_initial_effect (widget);
1268
1269     priv->initial_effect = FALSE;
1270   }
1271
1272   return GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->expose_event (widget, event);
1273 }
1274
1275 static GdkWindow *
1276 hildon_pannable_area_get_topmost (GdkWindow * window,
1277                                   gint x, gint y,
1278                                   gint * tx, gint * ty)
1279 {
1280   /* Find the GdkWindow at the given point, by recursing from a given
1281    * parent GdkWindow. Optionally return the co-ordinates transformed
1282    * relative to the child window.
1283    */
1284   gint width, height;
1285
1286   gdk_drawable_get_size (GDK_DRAWABLE (window), &width, &height);
1287   if ((x < 0) || (x >= width) || (y < 0) || (y >= height))
1288     return NULL;
1289
1290   while (window) {
1291     gint child_x = 0, child_y = 0;
1292     GList *c, *children = gdk_window_peek_children (window);
1293     GdkWindow *old_window = window;
1294
1295     for (c = children; c; c = c->next) {
1296       GdkWindow *child = (GdkWindow *) c->data;
1297       gint wx, wy;
1298
1299       gdk_drawable_get_size (GDK_DRAWABLE (child), &width, &height);
1300       gdk_window_get_position (child, &wx, &wy);
1301
1302       if (((x >= wx) && (x < (wx + width)) && (y >= wy)
1303            && (y < (wy + height))) && (gdk_window_is_visible (child))) {
1304         child_x = x - wx;
1305         child_y = y - wy;
1306         window = child;
1307       }
1308     }
1309
1310     if (window == old_window)
1311       break;
1312
1313     x = child_x;
1314     y = child_y;
1315   }
1316
1317   if (tx)
1318     *tx = x;
1319   if (ty)
1320     *ty = y;
1321
1322   return window;
1323 }
1324
1325 static void
1326 synth_crossing (GdkWindow * child,
1327                 gint x, gint y,
1328                 gint x_root, gint y_root,
1329                 guint32 time, gboolean in)
1330 {
1331   GdkEventCrossing *crossing_event;
1332   GdkEventType type = in ? GDK_ENTER_NOTIFY : GDK_LEAVE_NOTIFY;
1333
1334   /* Send synthetic enter event */
1335   crossing_event = (GdkEventCrossing *) gdk_event_new (type);
1336   ((GdkEventAny *) crossing_event)->type = type;
1337   ((GdkEventAny *) crossing_event)->window = g_object_ref (child);
1338   ((GdkEventAny *) crossing_event)->send_event = FALSE;
1339   crossing_event->subwindow = g_object_ref (child);
1340   crossing_event->time = time;
1341   crossing_event->x = x;
1342   crossing_event->y = y;
1343   crossing_event->x_root = x_root;
1344   crossing_event->y_root = y_root;
1345   crossing_event->mode = GDK_CROSSING_NORMAL;
1346   crossing_event->detail = GDK_NOTIFY_UNKNOWN;
1347   crossing_event->focus = FALSE;
1348   crossing_event->state = 0;
1349   gdk_event_put ((GdkEvent *) crossing_event);
1350   gdk_event_free ((GdkEvent *) crossing_event);
1351 }
1352
1353 static gboolean
1354 hildon_pannable_area_button_press_cb (GtkWidget * widget,
1355                                       GdkEventButton * event)
1356 {
1357   gint x, y;
1358   HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1359
1360   if ((!priv->enabled) || (event->button != 1) ||
1361       ((event->time == priv->last_time) &&
1362        (priv->last_type == 1)) || (gtk_bin_get_child (GTK_BIN (widget)) == NULL))
1363     return TRUE;
1364
1365   priv->scroll_indicator_event_interrupt = 1;
1366
1367   if (!priv->scroll_indicator_timeout){
1368     priv->scroll_indicator_timeout = gdk_threads_add_timeout
1369       ((gint) (1000.0 / (gdouble) (SCROLL_FADE_TIMEOUT)),
1370        (GSourceFunc) hildon_pannable_area_scroll_indicator_fade, widget);
1371   }
1372
1373   priv->last_time = event->time;
1374   priv->last_type = 1;
1375
1376   priv->scroll_to_x = -1;
1377   priv->scroll_to_y = -1;
1378
1379   if (priv->clicked && priv->child) {
1380     /* Widget stole focus on last click, send crossing-out event */
1381     synth_crossing (priv->child, 0, 0, event->x_root, event->y_root,
1382                     event->time, FALSE);
1383   }
1384
1385   priv->x = event->x;
1386   priv->y = event->y;
1387   priv->ix = priv->x;
1388   priv->iy = priv->y;
1389
1390   /* Don't allow a click if we're still moving fast */
1391   if ((ABS (priv->vel_x) <= (priv->vmax * priv->vfast_factor)) &&
1392       (ABS (priv->vel_y) <= (priv->vmax * priv->vfast_factor)))
1393     priv->child =
1394       hildon_pannable_area_get_topmost (gtk_bin_get_child (GTK_BIN (widget))->window,
1395                                         event->x, event->y, &x, &y);
1396   else
1397     priv->child = NULL;
1398
1399   priv->clicked = TRUE;
1400
1401   /* Stop scrolling on mouse-down (so you can flick, then hold to stop) */
1402   priv->vel_x = 0;
1403   priv->vel_y = 0;
1404
1405   if (priv->child) {
1406
1407     g_object_add_weak_pointer ((GObject *) priv->child,
1408                                (gpointer) & priv->child);
1409
1410     event = (GdkEventButton *) gdk_event_copy ((GdkEvent *) event);
1411     event->x = x;
1412     event->y = y;
1413     priv->cx = x;
1414     priv->cy = y;
1415
1416     synth_crossing (priv->child, x, y, event->x_root,
1417                     event->y_root, event->time, TRUE);
1418
1419     /* Send synthetic click (button press/release) event */
1420     ((GdkEventAny *) event)->window = g_object_ref (priv->child);
1421
1422     gdk_event_put ((GdkEvent *) event);
1423     gdk_event_free ((GdkEvent *) event);
1424   } else
1425     priv->child = NULL;
1426
1427   return TRUE;
1428 }
1429
1430 static void
1431 hildon_pannable_area_refresh (HildonPannableArea * area)
1432 {
1433   HildonPannableAreaPrivate *priv = area->priv;
1434   gboolean prev_hscroll_visible, prev_vscroll_visible;
1435
1436   if (!gtk_bin_get_child (GTK_BIN (area))) {
1437     priv->vscroll_visible = FALSE;
1438     priv->hscroll_visible = FALSE;
1439     return;
1440   }
1441
1442   prev_hscroll_visible = priv->hscroll_visible;
1443   prev_vscroll_visible = priv->vscroll_visible;
1444
1445   switch (priv->hscrollbar_policy) {
1446   case GTK_POLICY_ALWAYS:
1447     priv->hscroll_visible = TRUE;
1448     break;
1449   case GTK_POLICY_NEVER:
1450     priv->hscroll_visible = FALSE;
1451     break;
1452   default:
1453     priv->hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
1454                              priv->hadjust->page_size);
1455   }
1456
1457   switch (priv->vscrollbar_policy) {
1458   case GTK_POLICY_ALWAYS:
1459     priv->vscroll_visible = TRUE;
1460     break;
1461   case GTK_POLICY_NEVER:
1462     priv->vscroll_visible = FALSE;
1463     break;
1464   default:
1465     priv->vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
1466                              priv->vadjust->page_size);
1467   }
1468
1469   /* Store the vscroll/hscroll areas for redrawing */
1470   if (priv->vscroll_visible) {
1471     GtkAllocation *allocation = &GTK_WIDGET (area)->allocation;
1472     priv->vscroll_rect.x = allocation->x + allocation->width -
1473       priv->area_width;
1474     priv->vscroll_rect.y = allocation->y;
1475     priv->vscroll_rect.width = priv->area_width;
1476     priv->vscroll_rect.height = allocation->height -
1477       (priv->hscroll_visible ? priv->area_width : 0);
1478   }
1479   if (priv->hscroll_visible) {
1480     GtkAllocation *allocation = &GTK_WIDGET (area)->allocation;
1481     priv->hscroll_rect.y = allocation->y + allocation->height -
1482       priv->area_width;
1483     priv->hscroll_rect.x = allocation->x;
1484     priv->hscroll_rect.height = priv->area_width;
1485     priv->hscroll_rect.width = allocation->width -
1486       (priv->vscroll_visible ? priv->area_width : 0);
1487   }
1488
1489   if (GTK_WIDGET_DRAWABLE (area)) {
1490     if (priv->hscroll_visible != prev_hscroll_visible) {
1491       gtk_widget_queue_resize (GTK_WIDGET (area));
1492     }
1493
1494     if (priv->vscroll_visible != prev_vscroll_visible) {
1495       gtk_widget_queue_resize (GTK_WIDGET (area));
1496     }
1497   }
1498
1499 }
1500
1501 /* Scroll by a particular amount (in pixels). Optionally, return if
1502  * the scroll on a particular axis was successful.
1503  */
1504 static void
1505 hildon_pannable_axis_scroll (HildonPannableArea *area,
1506                              GtkAdjustment *adjust,
1507                              gdouble *vel,
1508                              gdouble inc,
1509                              gint *overshooting,
1510                              gint *overshot_dist,
1511                              gdouble *scroll_to,
1512                              gint overshoot_max,
1513                              gboolean *s)
1514 {
1515   gdouble dist;
1516   HildonPannableAreaPrivate *priv = area->priv;
1517
1518   dist = gtk_adjustment_get_value (adjust) - inc;
1519
1520   /* Overshooting
1521    * We use overshot_dist to define the distance of the current overshoot,
1522    * and overshooting to define the direction/whether or not we are overshot
1523    */
1524   if (!(*overshooting)) {
1525
1526     /* Initiation of the overshoot happens when the finger is released
1527      * and the current position of the pannable contents are out of range
1528      */
1529     if (dist < adjust->lower) {
1530       if (s) *s = FALSE;
1531
1532       dist = adjust->lower;
1533
1534       if (overshoot_max!=0) {
1535         *overshooting = 1;
1536         *scroll_to = -1;
1537         *overshot_dist = CLAMP (*overshot_dist + *vel, 0, overshoot_max);
1538         gtk_widget_queue_resize (GTK_WIDGET (area));
1539       } else {
1540         *vel = 0.0;
1541       }
1542     } else if (dist > adjust->upper - adjust->page_size) {
1543       if (s) *s = FALSE;
1544
1545       dist = adjust->upper - adjust->page_size;
1546
1547       if (overshoot_max!=0) {
1548         *overshooting = 1;
1549         *scroll_to = -1;
1550         *overshot_dist = CLAMP (*overshot_dist + *vel, -overshoot_max, 0);
1551         gtk_widget_queue_resize (GTK_WIDGET (area));
1552       } else {
1553         *vel = 0.0;
1554       }
1555     } else {
1556       if ((*scroll_to) != -1) {
1557         if (((inc < 0)&&(*scroll_to <= dist))||
1558             ((inc > 0)&&(*scroll_to >= dist))) {
1559           dist = *scroll_to;
1560           *scroll_to = -1;
1561           *vel = 0;
1562         }
1563       }
1564     }
1565
1566     gtk_adjustment_set_value (adjust, dist);
1567   } else {
1568     if (!priv->clicked) {
1569
1570       /* When the overshoot has started we continue for BOUNCE_STEPS more steps into the overshoot
1571        * before we reverse direction. The deceleration factor is calculated based on
1572        * the percentage distance from the first item with each iteration, therefore always
1573        * returning us to the top/bottom most element
1574        */
1575       if (*overshot_dist > 0) {
1576
1577         if ((*overshooting < BOUNCE_STEPS) && (*vel > 0)) {
1578           (*overshooting)++;
1579           *vel = (((gdouble)*overshot_dist)/overshoot_max) * (*vel);
1580         } else if ((*overshooting >= BOUNCE_STEPS) && (*vel > 0)) {
1581           *vel *= -1;
1582           (*overshooting)--;
1583         } else if ((*overshooting > 1) && (*vel < 0)) {
1584           (*overshooting)--;
1585           /* we add the MAX in order to avoid very small speeds */
1586           *vel = MIN ((((gdouble)*overshot_dist)/overshoot_max) * (*vel), -10.0);
1587         }
1588
1589         *overshot_dist = CLAMP (*overshot_dist + *vel, 0, overshoot_max);
1590
1591         gtk_widget_queue_resize (GTK_WIDGET (area));
1592
1593       } else if (*overshot_dist < 0) {
1594
1595         if ((*overshooting < BOUNCE_STEPS) && (*vel < 0)) {
1596           (*overshooting)++;
1597           *vel = (((gdouble)*overshot_dist)/overshoot_max) * (*vel) * -1;
1598         } else if ((*overshooting >= BOUNCE_STEPS) && (*vel < 0)) {
1599           *vel *= -1;
1600           (*overshooting)--;
1601         } else if ((*overshooting > 1) && (*vel > 0)) {
1602           (*overshooting)--;
1603           /* we add the MIN in order to avoid very small speeds */
1604           *vel = MAX ((((gdouble)*overshot_dist)/overshoot_max) * (*vel) * -1, 10.0);
1605         }
1606
1607         *overshot_dist = CLAMP (*overshot_dist + (*vel), -overshoot_max, 0);
1608
1609         gtk_widget_queue_resize (GTK_WIDGET (area));
1610
1611       } else {
1612         *overshooting = 0;
1613         *vel = 0;
1614         gtk_widget_queue_resize (GTK_WIDGET (area));
1615       }
1616     } else {
1617       if (*overshot_dist > 0) {
1618         *overshot_dist = CLAMP ((*overshot_dist) + inc, 0, overshoot_max);
1619       } else if (*overshot_dist < 0) {
1620         *overshot_dist = CLAMP ((*overshot_dist) + inc, -1 * overshoot_max, 0);
1621       } else {
1622         *overshooting = 0;
1623         gtk_adjustment_set_value (adjust, dist);
1624       }
1625       gtk_widget_queue_resize (GTK_WIDGET (area));
1626     }
1627   }
1628 }
1629
1630 static void
1631 hildon_pannable_area_scroll (HildonPannableArea *area,
1632                              gdouble x, gdouble y)
1633 {
1634   gboolean sx, sy;
1635   HildonPannableAreaPrivate *priv = area->priv;
1636   gboolean hscroll_visible, vscroll_visible;
1637
1638   if (gtk_bin_get_child (GTK_BIN (area)) == NULL)
1639     return;
1640
1641   vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
1642              priv->vadjust->page_size);
1643   hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
1644              priv->hadjust->page_size);
1645
1646   sx = TRUE;
1647   sy = TRUE;
1648
1649   if (vscroll_visible) {
1650     hildon_pannable_axis_scroll (area, priv->vadjust, &priv->vel_y, y,
1651                                  &priv->overshooting_y, &priv->overshot_dist_y,
1652                                  &priv->scroll_to_y, priv->vovershoot_max, &sy);
1653   } else {
1654     priv->vel_y = 0;
1655   }
1656
1657   if (hscroll_visible) {
1658     hildon_pannable_axis_scroll (area, priv->hadjust, &priv->vel_x, x,
1659                                  &priv->overshooting_x, &priv->overshot_dist_x,
1660                                  &priv->scroll_to_x, priv->hovershoot_max, &sx);
1661   } else {
1662     priv->vel_x = 0;
1663   }
1664
1665   /* If the scroll on a particular axis wasn't succesful, reset the
1666    * initial scroll position to the new mouse co-ordinate. This means
1667    * when you get to the top of the page, dragging down works immediately.
1668    */
1669   if (!sx) {
1670     priv->x = priv->ex;
1671   }
1672
1673   if (!sy) {
1674     priv->y = priv->ey;
1675   }
1676
1677 }
1678
1679 static gboolean
1680 hildon_pannable_area_timeout (HildonPannableArea * area)
1681 {
1682   HildonPannableAreaPrivate *priv = area->priv;
1683
1684   if ((!priv->enabled) || (priv->mode == HILDON_PANNABLE_AREA_MODE_PUSH)) {
1685     priv->idle_id = 0;
1686
1687     return FALSE;
1688   }
1689
1690   if (!priv->clicked) {
1691     /* Decelerate gradually when pointer is raised */
1692     if ((!priv->overshot_dist_y) &&
1693         (!priv->overshot_dist_x)) {
1694
1695       /* in case we move to a specific point do not decelerate when arriving */
1696       if ((priv->scroll_to_x != -1)||(priv->scroll_to_y != -1)) {
1697
1698         if (ABS (priv->vel_x) >= 1.5) {
1699           priv->vel_x *= priv->decel;
1700         }
1701
1702         if (ABS (priv->vel_y) >= 1.5) {
1703           priv->vel_y *= priv->decel;
1704         }
1705
1706       } else {
1707         priv->vel_x *= priv->decel;
1708         priv->vel_y *= priv->decel;
1709
1710         if ((ABS (priv->vel_x) < 1.0) && (ABS (priv->vel_y) < 1.0)) {
1711           priv->vel_x = 0;
1712           priv->vel_y = 0;
1713           priv->idle_id = 0;
1714
1715           return FALSE;
1716         }
1717       }
1718     }
1719   } else if (priv->mode == HILDON_PANNABLE_AREA_MODE_AUTO) {
1720     priv->idle_id = 0;
1721
1722     return FALSE;
1723   }
1724
1725   hildon_pannable_area_scroll (area, priv->vel_x, priv->vel_y);
1726
1727   return TRUE;
1728 }
1729
1730 static void
1731 hildon_pannable_area_calculate_velocity (gdouble *vel,
1732                                          gdouble delta,
1733                                          gdouble dist,
1734                                          gdouble vmax,
1735                                          guint sps)
1736 {
1737   gdouble rawvel;
1738
1739   if (ABS (dist) >= 0.00001) {
1740     rawvel = ((dist / ABS (delta)) *
1741               (gdouble) sps) * FORCE;
1742     *vel = *vel * (1 - SMOOTH_FACTOR) +
1743       rawvel * SMOOTH_FACTOR;
1744     *vel = *vel > 0 ? MIN (*vel, vmax)
1745       : MAX (dist, -1 * vmax);
1746   }
1747 }
1748
1749 static gboolean
1750 hildon_pannable_area_motion_notify_cb (GtkWidget * widget,
1751                                        GdkEventMotion * event)
1752 {
1753   HildonPannableArea *area = HILDON_PANNABLE_AREA (widget);
1754   HildonPannableAreaPrivate *priv = area->priv;
1755   gint dnd_threshold;
1756   gdouble x, y;
1757   gdouble delta;
1758
1759   if (gtk_bin_get_child (GTK_BIN (widget)) == NULL)
1760     return TRUE;
1761
1762   if ((!priv->enabled) || (!priv->clicked) ||
1763       ((event->time == priv->last_time) && (priv->last_type == 2))) {
1764     gdk_window_get_pointer (widget->window, NULL, NULL, 0);
1765     return TRUE;
1766   }
1767
1768   if (priv->last_type == 1) {
1769     priv->first_drag = TRUE;
1770   }
1771
1772   /* Only start the scroll if the mouse cursor passes beyond the
1773    * DnD threshold for dragging.
1774    */
1775   g_object_get (G_OBJECT (gtk_settings_get_default ()),
1776                 "gtk-dnd-drag-threshold", &dnd_threshold, NULL);
1777   x = event->x - priv->x;
1778   y = event->y - priv->y;
1779
1780   if (priv->first_drag && (!priv->moved) &&
1781       ((ABS (x) > (dnd_threshold))
1782        || (ABS (y) > (dnd_threshold)))) {
1783     priv->moved = TRUE;
1784     x = 0;
1785     y = 0;
1786
1787     if (priv->first_drag) {
1788
1789       if (ABS (priv->iy - event->y) >=
1790           ABS (priv->ix - event->x)) {
1791         gboolean vscroll_visible;
1792
1793         g_signal_emit (area,
1794                        pannable_area_signals[VERTICAL_MOVEMENT],
1795                        0, (priv->iy > event->y) ?
1796                        HILDON_MOVEMENT_UP :
1797                        HILDON_MOVEMENT_DOWN,
1798                        (gdouble)priv->ix, (gdouble)priv->iy);
1799
1800         vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
1801                    priv->vadjust->page_size);
1802
1803         if (!((vscroll_visible)&&
1804               (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT)))
1805           priv->moved = FALSE;
1806
1807       } else {
1808         gboolean hscroll_visible;
1809
1810         g_signal_emit (area,
1811                        pannable_area_signals[HORIZONTAL_MOVEMENT],
1812                        0, (priv->ix > event->x) ?
1813                        HILDON_MOVEMENT_LEFT :
1814                        HILDON_MOVEMENT_RIGHT,
1815                        (gdouble)priv->ix, (gdouble)priv->iy);
1816
1817         hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
1818                            priv->hadjust->page_size);
1819
1820         if (!((hscroll_visible)&&
1821               (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ)))
1822           priv->moved = FALSE;
1823       }
1824     }
1825
1826     priv->first_drag = FALSE;
1827
1828     if ((priv->mode != HILDON_PANNABLE_AREA_MODE_PUSH) &&
1829         (priv->mode != HILDON_PANNABLE_AREA_MODE_AUTO)) {
1830
1831       if (!priv->idle_id)
1832         priv->idle_id = gdk_threads_add_timeout ((gint)
1833                                                  (1000.0 / (gdouble) priv->sps),
1834                                                  (GSourceFunc)
1835                                                  hildon_pannable_area_timeout, area);
1836     }
1837   }
1838
1839   if (priv->moved) {
1840     switch (priv->mode) {
1841     case HILDON_PANNABLE_AREA_MODE_PUSH:
1842       /* Scroll by the amount of pixels the cursor has moved
1843        * since the last motion event.
1844        */
1845       hildon_pannable_area_scroll (area, x, y);
1846       priv->x = event->x;
1847       priv->y = event->y;
1848       break;
1849     case HILDON_PANNABLE_AREA_MODE_ACCEL:
1850       /* Set acceleration relative to the initial click */
1851       priv->ex = event->x;
1852       priv->ey = event->y;
1853       priv->vel_x = ((x > 0) ? 1 : -1) *
1854         (((ABS (x) /
1855            (gdouble) widget->allocation.width) *
1856           (priv->vmax - priv->vmin)) + priv->vmin);
1857       priv->vel_y = ((y > 0) ? 1 : -1) *
1858         (((ABS (y) /
1859            (gdouble) widget->allocation.height) *
1860           (priv->vmax - priv->vmin)) + priv->vmin);
1861       break;
1862     case HILDON_PANNABLE_AREA_MODE_AUTO:
1863
1864       delta = event->time - priv->last_time;
1865
1866       if (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT) {
1867         gdouble dist = event->y - priv->y;
1868
1869         hildon_pannable_area_calculate_velocity (&priv->vel_y,
1870                                                  delta,
1871                                                  dist,
1872                                                  priv->vmax,
1873                                                  priv->sps);
1874       } else {
1875         y = 0;
1876         priv->vel_y = 0;
1877       }
1878
1879
1880       if (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ) {
1881         gdouble dist = event->x - priv->x;
1882
1883         hildon_pannable_area_calculate_velocity (&priv->vel_x,
1884                                                  delta,
1885                                                  dist,
1886                                                  priv->vmax,
1887                                                  priv->sps);
1888       } else {
1889         x = 0;
1890         priv->vel_x = 0;
1891       }
1892
1893       hildon_pannable_area_scroll (area, x, y);
1894
1895       if (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ)
1896         priv->x = event->x;
1897       if (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT)
1898         priv->y = event->y;
1899
1900       break;
1901
1902     default:
1903       break;
1904     }
1905   }
1906
1907   if (priv->child) {
1908     /* Send motion notify to child */
1909     priv->last_time = event->time;
1910     priv->last_type = 2;
1911     event = (GdkEventMotion *) gdk_event_copy ((GdkEvent *) event);
1912     event->x = priv->cx + (event->x - priv->ix);
1913     event->y = priv->cy + (event->y - priv->iy);
1914     event->window = g_object_ref (priv->child);
1915     gdk_event_put ((GdkEvent *) event);
1916     gdk_event_free ((GdkEvent *) event);
1917   }
1918
1919   gdk_window_get_pointer (widget->window, NULL, NULL, 0);
1920
1921   return TRUE;
1922 }
1923
1924 static gboolean
1925 hildon_pannable_area_button_release_cb (GtkWidget * widget,
1926                                         GdkEventButton * event)
1927 {
1928   HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1929   gint x, y;
1930   GdkWindow *child;
1931
1932   if (gtk_bin_get_child (GTK_BIN (widget)) == NULL)
1933     return TRUE;
1934
1935   priv->scroll_indicator_event_interrupt = 0;
1936   priv->scroll_delay_counter = SCROLLBAR_FADE_DELAY;
1937
1938   if ((ABS (priv->vel_y) > 1.0)||
1939       (ABS (priv->vel_x) > 1.0)) {
1940     priv->scroll_indicator_alpha = 1.0;
1941   }
1942
1943   if (!priv->scroll_indicator_timeout) {
1944     priv->scroll_indicator_timeout = gdk_threads_add_timeout
1945       ((gint) (1000.0 / (gdouble) SCROLL_FADE_TIMEOUT),
1946        (GSourceFunc) hildon_pannable_area_scroll_indicator_fade, widget);
1947   }
1948
1949   if ((!priv->clicked) || (!priv->enabled) || (event->button != 1) ||
1950       ((event->time == priv->last_time) && (priv->last_type == 3)))
1951     return TRUE;
1952
1953   priv->clicked = FALSE;
1954
1955   if (priv->mode == HILDON_PANNABLE_AREA_MODE_AUTO ||
1956       priv->mode == HILDON_PANNABLE_AREA_MODE_ACCEL) {
1957
1958     /* If overshoot has been initiated with a finger down, on release set max speed */
1959     if (priv->overshot_dist_y != 0) {
1960       priv->overshooting_y = BOUNCE_STEPS; /* Hack to stop a bounce in the finger down case */
1961       priv->vel_y = priv->vmax;
1962     }
1963
1964     if (priv->overshot_dist_x != 0) {
1965       priv->overshooting_x = BOUNCE_STEPS; /* Hack to stop a bounce in the finger down case */
1966       priv->vel_x = priv->vmax;
1967     }
1968
1969     if (!priv->idle_id)
1970       priv->idle_id = gdk_threads_add_timeout ((gint) (1000.0 / (gdouble) priv->sps),
1971                                                (GSourceFunc)
1972                                                hildon_pannable_area_timeout, widget);
1973   }
1974
1975   priv->last_time = event->time;
1976   priv->last_type = 3;
1977
1978   if (!priv->child) {
1979     priv->moved = FALSE;
1980     return TRUE;
1981   }
1982
1983   child =
1984     hildon_pannable_area_get_topmost (gtk_bin_get_child (GTK_BIN (widget))->window,
1985                                       event->x, event->y, &x, &y);
1986
1987   event = (GdkEventButton *) gdk_event_copy ((GdkEvent *) event);
1988   event->x = x;
1989   event->y = y;
1990
1991   /* Leave the widget if we've moved - This doesn't break selection,
1992    * but stops buttons from being clicked.
1993    */
1994   if ((child != priv->child) || (priv->moved)) {
1995     /* Send synthetic leave event */
1996     synth_crossing (priv->child, x, y, event->x_root,
1997                     event->y_root, event->time, FALSE);
1998     /* Send synthetic button release event */
1999     ((GdkEventAny *) event)->window = g_object_ref (priv->child);
2000     gdk_event_put ((GdkEvent *) event);
2001   } else {
2002     /* Send synthetic button release event */
2003     ((GdkEventAny *) event)->window = g_object_ref (child);
2004     gdk_event_put ((GdkEvent *) event);
2005     /* Send synthetic leave event */
2006     synth_crossing (priv->child, x, y, event->x_root,
2007                     event->y_root, event->time, FALSE);
2008   }
2009   g_object_remove_weak_pointer ((GObject *) priv->child,
2010                                 (gpointer) & priv->child);
2011
2012   priv->moved = FALSE;
2013   gdk_event_free ((GdkEvent *) event);
2014
2015   return TRUE;
2016 }
2017
2018 /* utility event handler */
2019 static gboolean
2020 hildon_pannable_area_scroll_cb (GtkWidget *widget,
2021                                 GdkEventScroll *event)
2022 {
2023   GtkAdjustment *adj = NULL;
2024   HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
2025
2026   if ((!priv->enabled) ||
2027       (gtk_bin_get_child (GTK_BIN (widget)) == NULL))
2028     return TRUE;
2029
2030   priv->scroll_indicator_event_interrupt = 0;
2031   priv->scroll_indicator_alpha = 1.0;
2032   priv->scroll_delay_counter = SCROLLBAR_FADE_DELAY + 20;
2033
2034   if (!priv->scroll_indicator_timeout) {
2035     priv->scroll_indicator_timeout = gdk_threads_add_timeout
2036       ((gint) (1000.0 / (gdouble) (SCROLL_FADE_TIMEOUT)),
2037        (GSourceFunc) hildon_pannable_area_scroll_indicator_fade, widget);
2038   }
2039
2040   /* Stop inertial scrolling */
2041   if (priv->idle_id) {
2042     priv->vel_x = 0.0;
2043     priv->vel_y = 0.0;
2044     priv->overshooting_x = 0;
2045     priv->overshooting_y = 0;
2046
2047     if ((priv->overshot_dist_x>0)||(priv->overshot_dist_y>0)) {
2048       priv->overshot_dist_x = 0;
2049       priv->overshot_dist_y = 0;
2050
2051       gtk_widget_queue_resize (GTK_WIDGET (widget));
2052     }
2053
2054     g_source_remove (priv->idle_id);
2055     priv->idle_id = 0;
2056   }
2057
2058   if (event->direction == GDK_SCROLL_UP || event->direction == GDK_SCROLL_DOWN)
2059     adj = priv->vadjust;
2060   else
2061     adj = priv->hadjust;
2062
2063   if (adj)
2064     {
2065       gdouble delta, new_value;
2066
2067       /* from gtkrange.c calculate delta*/
2068       delta = pow (adj->page_size, 2.0 / 3.0);
2069
2070       if (event->direction == GDK_SCROLL_UP ||
2071           event->direction == GDK_SCROLL_LEFT)
2072         delta = - delta;
2073
2074       new_value = CLAMP (adj->value + delta, adj->lower, adj->upper - adj->page_size);
2075
2076       gtk_adjustment_set_value (adj, new_value);
2077     }
2078
2079   return TRUE;
2080 }
2081
2082 static void
2083 hildon_pannable_area_child_mapped (GtkWidget *widget,
2084                                    GdkEvent  *event,
2085                                    gpointer user_data)
2086 {
2087   HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (user_data)->priv;
2088
2089   if (priv->event_window != NULL && priv->enabled)
2090     gdk_window_raise (priv->event_window);
2091 }
2092
2093 static void
2094 hildon_pannable_area_add (GtkContainer *container, GtkWidget *child)
2095 {
2096   HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (container)->priv;
2097
2098   g_return_if_fail (gtk_bin_get_child (GTK_BIN (container)) == NULL);
2099
2100   gtk_widget_set_parent (child, GTK_WIDGET (container));
2101   GTK_BIN (container)->child = child;
2102
2103   g_signal_connect_after (child, "map-event",
2104                           G_CALLBACK (hildon_pannable_area_child_mapped),
2105                           container);
2106
2107   if (!gtk_widget_set_scroll_adjustments (child, priv->hadjust, priv->vadjust)) {
2108     g_warning ("%s: cannot add non scrollable widget, "
2109                "wrap it in a viewport", __FUNCTION__);
2110   }
2111 }
2112
2113 static void
2114 hildon_pannable_area_remove (GtkContainer *container, GtkWidget *child)
2115 {
2116   g_return_if_fail (HILDON_IS_PANNABLE_AREA (container));
2117   g_return_if_fail (child != NULL);
2118   g_return_if_fail (gtk_bin_get_child (GTK_BIN (container)) == child);
2119
2120   gtk_widget_set_scroll_adjustments (child, NULL, NULL);
2121
2122   g_signal_handlers_disconnect_by_func (GTK_WIDGET (child),
2123                                         G_CALLBACK (hildon_pannable_area_child_mapped),
2124                                         container);
2125
2126   /* chain parent class handler to remove child */
2127   GTK_CONTAINER_CLASS (hildon_pannable_area_parent_class)->remove (container, child);
2128 }
2129
2130 static void
2131 hildon_pannable_calculate_vel_factor (HildonPannableArea * self)
2132 {
2133   HildonPannableAreaPrivate *priv = self->priv;
2134   gfloat fct = 0;
2135   gfloat fct_i = 1;
2136   gint i, n;
2137
2138   n = ceil (priv->sps * priv->scroll_time);
2139
2140   for (i = 0; i < n && fct_i >= RATIO_TOLERANCE; i++) {
2141     fct_i *= priv->decel;
2142     fct += fct_i;
2143   }
2144
2145     priv->vel_factor = fct;
2146 }
2147
2148 /**
2149  * hildon_pannable_area_new:
2150  *
2151  * Create a new pannable area widget
2152  *
2153  * Returns: the newly created #HildonPannableArea
2154  *
2155  * Since: 2.2
2156  */
2157
2158 GtkWidget *
2159 hildon_pannable_area_new (void)
2160 {
2161   return g_object_new (HILDON_TYPE_PANNABLE_AREA, NULL);
2162 }
2163
2164 /**
2165  * hildon_pannable_area_new_full:
2166  * @mode: #HildonPannableAreaMode
2167  * @enabled: Value for the enabled property
2168  * @vel_min: Value for the velocity-min property
2169  * @vel_max: Value for the velocity-max property
2170  * @decel: Value for the deceleration property
2171  * @sps: Value for the sps property
2172  *
2173  * Create a new #HildonPannableArea widget and set various properties
2174  *
2175  * returns: the newly create #HildonPannableArea
2176  *
2177  * Since: 2.2
2178  */
2179
2180 GtkWidget *
2181 hildon_pannable_area_new_full (gint mode, gboolean enabled,
2182                                gdouble vel_min, gdouble vel_max,
2183                                gdouble decel, guint sps)
2184 {
2185   return g_object_new (HILDON_TYPE_PANNABLE_AREA,
2186                        "mode", mode,
2187                        "enabled", enabled,
2188                        "velocity_min", vel_min,
2189                        "velocity_max", vel_max,
2190                        "deceleration", decel, "sps", sps, NULL);
2191 }
2192
2193 /**
2194  * hildon_pannable_area_add_with_viewport:
2195  * @area: A #HildonPannableArea
2196  * @child: Child widget to add to the viewport
2197  *
2198  * Convenience function used to add a child to a #GtkViewport, and add the
2199  * viewport to the scrolled window.
2200  *
2201  * Since: 2.2
2202  */
2203
2204 void
2205 hildon_pannable_area_add_with_viewport (HildonPannableArea * area,
2206                                         GtkWidget * child)
2207 {
2208   GtkBin *bin;
2209   GtkWidget *viewport;
2210
2211   g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
2212   g_return_if_fail (GTK_IS_WIDGET (child));
2213   g_return_if_fail (child->parent == NULL);
2214
2215   bin = GTK_BIN (area);
2216
2217   if (bin->child != NULL)
2218     {
2219       g_return_if_fail (GTK_IS_VIEWPORT (bin->child));
2220       g_return_if_fail (GTK_BIN (bin->child)->child == NULL);
2221
2222       viewport = bin->child;
2223     }
2224   else
2225     {
2226       HildonPannableAreaPrivate *priv = area->priv;
2227
2228       viewport = gtk_viewport_new (priv->hadjust,
2229                                    priv->vadjust);
2230       gtk_viewport_set_shadow_type (GTK_VIEWPORT (viewport), GTK_SHADOW_NONE);
2231       gtk_container_add (GTK_CONTAINER (area), viewport);
2232     }
2233
2234   gtk_widget_show (viewport);
2235   gtk_container_add (GTK_CONTAINER (viewport), child);
2236 }
2237
2238 /**
2239  * hildon_pannable_area_scroll_to:
2240  * @area: A #HildonPannableArea.
2241  * @x: The x coordinate of the destination point or -1 to ignore this axis.
2242  * @y: The y coordinate of the destination point or -1 to ignore this axis.
2243  *
2244  * Smoothly scrolls @area to ensure that (@x, @y) is a visible point
2245  * on the widget. To move in only one coordinate, you must set the other one
2246  * to -1. Notice that, in %HILDON_PANNABLE_AREA_MODE_PUSH mode, this function
2247  * works just like hildon_pannable_area_jump_to().
2248  *
2249  * This function is useful if you need to present the user with a particular
2250  * element inside a scrollable widget, like #GtkTreeView. For instance,
2251  * the following example shows how to scroll inside a #GtkTreeView to
2252  * make visible an item, indicated by the #GtkTreeIter @iter.
2253  *
2254  * <example>
2255  * <programlisting>
2256  *  GtkTreePath *path;
2257  *  GdkRectangle *rect;
2258  *  <!-- -->
2259  *  path = gtk_tree_model_get_path (model, &amp;iter);
2260  *  gtk_tree_view_get_background_area (GTK_TREE_VIEW (treeview),
2261  *                                     path, NULL, &amp;rect);
2262  *  gtk_tree_view_convert_bin_window_to_tree_coords (GTK_TREE_VIEW (treeview),
2263  *                                                   0, rect.y, NULL, &amp;y);
2264  *  hildon_pannable_area_scroll_to (panarea, -1, y);
2265  *  gtk_tree_path_free (path);
2266  * </programlisting>
2267  * </example>
2268  *
2269  * If you want to present a child widget in simpler scenarios,
2270  * use hildon_pannable_area_scroll_to_child() instead.
2271  *
2272  * There is a precondition to this function: the widget must be
2273  * already realized. Check the hildon_pannable_area_jump_to_child() for
2274  * more tips regarding how to call this function during
2275  * initialization.
2276  *
2277  * Since: 2.2
2278  **/
2279 void
2280 hildon_pannable_area_scroll_to (HildonPannableArea *area,
2281                                 const gint x, const gint y)
2282 {
2283   HildonPannableAreaPrivate *priv;
2284   gint width, height;
2285   gint dist_x, dist_y;
2286   gboolean hscroll_visible, vscroll_visible;
2287
2288   g_return_if_fail (GTK_WIDGET_REALIZED (area));
2289   g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
2290
2291   priv = area->priv;
2292
2293   vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
2294              priv->vadjust->page_size);
2295   hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
2296              priv->hadjust->page_size);
2297
2298   if (((!vscroll_visible)&&(!hscroll_visible)) ||
2299       (x == -1 && y == -1)) {
2300     return;
2301   }
2302
2303   if (priv->mode == HILDON_PANNABLE_AREA_MODE_PUSH)
2304     hildon_pannable_area_jump_to (area, x, y);
2305
2306   width = priv->hadjust->upper - priv->hadjust->lower;
2307   height = priv->vadjust->upper - priv->vadjust->lower;
2308
2309   g_return_if_fail (x < width || y < height);
2310
2311   if (x > -1) {
2312     priv->scroll_to_x = x - priv->hadjust->page_size/2;
2313     dist_x = priv->scroll_to_x - priv->hadjust->value;
2314     if (dist_x == 0) {
2315       priv->scroll_to_x = -1;
2316     } else {
2317       priv->vel_x = - dist_x/priv->vel_factor;
2318     }
2319   } else {
2320     priv->scroll_to_x = -1;
2321   }
2322
2323   if (y > -1) {
2324     priv->scroll_to_y = y - priv->vadjust->page_size/2;
2325     dist_y = priv->scroll_to_y - priv->vadjust->value;
2326     if (dist_y == 0) {
2327       priv->scroll_to_y = -1;
2328     } else {
2329       priv->vel_y = - dist_y/priv->vel_factor;
2330     }
2331   } else {
2332     priv->scroll_to_y = y;
2333   }
2334
2335   if ((priv->scroll_to_y == -1) && (priv->scroll_to_y == -1)) {
2336     return;
2337   }
2338
2339   priv->scroll_indicator_alpha = 1.0;
2340
2341   if (!priv->scroll_indicator_timeout)
2342     priv->scroll_indicator_timeout = gdk_threads_add_timeout
2343       ((gint) (1000.0 / (gdouble) SCROLL_FADE_TIMEOUT),
2344        (GSourceFunc) hildon_pannable_area_scroll_indicator_fade, area);
2345
2346   if (!priv->idle_id)
2347     priv->idle_id = gdk_threads_add_timeout ((gint) (1000.0 / (gdouble) priv->sps),
2348                                              (GSourceFunc)
2349                                              hildon_pannable_area_timeout, area);
2350 }
2351
2352 /**
2353  * hildon_pannable_area_jump_to:
2354  * @area: A #HildonPannableArea.
2355  * @x: The x coordinate of the destination point or -1 to ignore this axis.
2356  * @y: The y coordinate of the destination point or -1 to ignore this axis.
2357  *
2358  * Jumps the position of @area to ensure that (@x, @y) is a visible
2359  * point in the widget. In order to move in only one coordinate, you
2360  * must set the other one to -1. See hildon_pannable_area_scroll_to()
2361  * function for an example of how to calculate the position of
2362  * children in scrollable widgets like #GtkTreeview.
2363  *
2364  * There is a precondition to this function: the widget must be
2365  * already realized. Check the hildon_pannable_area_jump_to_child() for
2366  * more tips regarding how to call this function during
2367  * initialization.
2368  *
2369  * Since: 2.2
2370  **/
2371 void
2372 hildon_pannable_area_jump_to (HildonPannableArea *area,
2373                               const gint x, const gint y)
2374 {
2375   HildonPannableAreaPrivate *priv;
2376   gint width, height;
2377
2378   g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
2379   g_return_if_fail (GTK_WIDGET_REALIZED (area));
2380   g_return_if_fail (x >= -1 && y >= -1);
2381
2382   if (x == -1 && y == -1) {
2383     return;
2384   }
2385
2386   priv = area->priv;
2387
2388   width = priv->hadjust->upper - priv->hadjust->lower;
2389   height = priv->vadjust->upper - priv->vadjust->lower;
2390
2391   g_return_if_fail (x < width || y < height);
2392
2393   if (x != -1) {
2394     gdouble jump_to = x - priv->hadjust->page_size/2;
2395
2396     if (jump_to > priv->hadjust->upper - priv->hadjust->page_size) {
2397       jump_to = priv->hadjust->upper - priv->hadjust->page_size;
2398     }
2399
2400     gtk_adjustment_set_value (priv->hadjust, jump_to);
2401   }
2402
2403   if (y != -1) {
2404     gdouble jump_to =  y - priv->vadjust->page_size/2;
2405
2406     if (jump_to > priv->vadjust->upper - priv->vadjust->page_size) {
2407       jump_to = priv->vadjust->upper - priv->vadjust->page_size;
2408     }
2409
2410     gtk_adjustment_set_value (priv->vadjust, jump_to);
2411   }
2412
2413   priv->scroll_indicator_alpha = 1.0;
2414
2415   if (priv->scroll_indicator_timeout) {
2416     g_source_remove (priv->scroll_indicator_timeout);
2417     priv->scroll_indicator_timeout = 0;
2418   }
2419
2420   if (priv->idle_id) {
2421     priv->vel_x = 0.0;
2422     priv->vel_y = 0.0;
2423     priv->overshooting_x = 0;
2424     priv->overshooting_y = 0;
2425
2426     if ((priv->overshot_dist_x>0)||(priv->overshot_dist_y>0)) {
2427       priv->overshot_dist_x = 0;
2428       priv->overshot_dist_y = 0;
2429
2430       gtk_widget_queue_resize (GTK_WIDGET (area));
2431     }
2432
2433     g_source_remove (priv->idle_id);
2434     priv->idle_id = 0;
2435   }
2436 }
2437
2438 /**
2439  * hildon_pannable_area_scroll_to_child:
2440  * @area: A #HildonPannableArea.
2441  * @child: A #GtkWidget, descendant of @area.
2442  *
2443  * Smoothly scrolls until @child is visible inside @area. @child must
2444  * be a descendant of @area. If you need to scroll inside a scrollable
2445  * widget, e.g., #GtkTreeview, see hildon_pannable_area_scroll_to().
2446  *
2447  * There is a precondition to this function: the widget must be
2448  * already realized. Check the hildon_pannable_area_jump_to_child() for
2449  * more tips regarding how to call this function during
2450  * initialization.
2451  *
2452  * Since: 2.2
2453  **/
2454 void
2455 hildon_pannable_area_scroll_to_child (HildonPannableArea *area, GtkWidget *child)
2456 {
2457   GtkWidget *bin_child;
2458   gint x, y;
2459
2460   g_return_if_fail (GTK_WIDGET_REALIZED (area));
2461   g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
2462   g_return_if_fail (GTK_IS_WIDGET (child));
2463   g_return_if_fail (gtk_widget_is_ancestor (child, GTK_WIDGET (area)));
2464
2465   if (GTK_BIN (area)->child == NULL)
2466     return;
2467
2468   /* We need to get to check the child of the inside the area */
2469   bin_child = GTK_BIN (area)->child;
2470
2471   /* we check if we added a viewport */
2472   if (GTK_IS_VIEWPORT (bin_child)) {
2473     bin_child = GTK_BIN (bin_child)->child;
2474   }
2475
2476   if (gtk_widget_translate_coordinates (child, bin_child, 0, 0, &x, &y))
2477     hildon_pannable_area_scroll_to (area, x, y);
2478 }
2479
2480 /**
2481  * hildon_pannable_area_jump_to_child:
2482  * @area: A #HildonPannableArea.
2483  * @child: A #GtkWidget, descendant of @area.
2484  *
2485  * Jumps to make sure @child is visible inside @area. @child must
2486  * be a descendant of @area. If you want to move inside a scrollable
2487  * widget, like, #GtkTreeview, see hildon_pannable_area_scroll_to().
2488  *
2489  * There is a precondition to this function: the widget must be
2490  * already realized. You can control if the widget is ready with the
2491  * GTK_WIDGET_REALIZED macro. If you want to call this function during
2492  * the initialization process of the widget do it inside a callback to
2493  * the ::realize signal, using g_signal_connect_after() function.
2494  *
2495  * Since: 2.2
2496  **/
2497 void
2498 hildon_pannable_area_jump_to_child (HildonPannableArea *area, GtkWidget *child)
2499 {
2500   GtkWidget *bin_child;
2501   gint x, y;
2502
2503   g_return_if_fail (GTK_WIDGET_REALIZED (area));
2504   g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
2505   g_return_if_fail (GTK_IS_WIDGET (child));
2506   g_return_if_fail (gtk_widget_is_ancestor (child, GTK_WIDGET (area)));
2507
2508   if (gtk_bin_get_child (GTK_BIN (area)) == NULL)
2509     return;
2510
2511   /* We need to get to check the child of the inside the area */
2512   bin_child = gtk_bin_get_child (GTK_BIN (area));
2513
2514   /* we check if we added a viewport */
2515   if (GTK_IS_VIEWPORT (bin_child)) {
2516     bin_child = gtk_bin_get_child (GTK_BIN (bin_child));
2517   }
2518
2519   if (gtk_widget_translate_coordinates (child, bin_child, 0, 0, &x, &y))
2520     hildon_pannable_area_jump_to (area, x, y);
2521 }
2522
2523 /**
2524  * hildon_pannable_get_child_widget_at:
2525  * @area: A #HildonPannableArea.
2526  * @x: horizontal coordinate of the point
2527  * @y: vertical coordinate of the point
2528  *
2529  * Get the widget at the point (x, y) inside the pannable area. In
2530  * case no widget found it returns NULL.
2531  *
2532  * returns: the #GtkWidget if we find a widget, NULL in any other case
2533  *
2534  * Since: 2.2
2535  **/
2536 GtkWidget*
2537 hildon_pannable_get_child_widget_at (HildonPannableArea *area,
2538                                      gdouble x, gdouble y)
2539 {
2540   GdkWindow *window = NULL;
2541   GtkWidget *child_widget = NULL;
2542
2543   window = hildon_pannable_area_get_topmost
2544     (gtk_bin_get_child (GTK_BIN (area))->window,
2545      x, y, NULL, NULL);
2546
2547   gdk_window_get_user_data (window, (gpointer) &child_widget);
2548
2549   return child_widget;
2550 }
2551
2552
2553 /**
2554  * hildon_pannable_area_get_hadjustment:
2555  * @area: A #HildonPannableArea.
2556  *
2557  * Returns the horizontal adjustment
2558  *
2559  * returns: The horizontal #GtkAdjustment
2560  **/
2561 GtkAdjustment*
2562 hildon_pannable_area_get_hadjustment            (HildonPannableArea *area)
2563 {
2564
2565   g_return_val_if_fail (HILDON_IS_PANNABLE_AREA (area), NULL);
2566
2567   return area->priv->hadjust;
2568 }
2569
2570 /**
2571  * hildon_pannable_area_get_vadjustment:
2572  * @area: A #HildonPannableArea.
2573  *
2574  * Returns the vertical adjustment
2575  *
2576  * returns: The vertical #GtkAdjustment
2577  **/
2578 GtkAdjustment*
2579 hildon_pannable_area_get_vadjustment            (HildonPannableArea *area)
2580 {
2581   g_return_val_if_fail (HILDON_IS_PANNABLE_AREA (area), NULL);
2582
2583   return area->priv->vadjust;
2584 }
2585
2586
2587 /**
2588  * hildon_pannable_area_get_size_request_policy:
2589  * @area: A #HildonPannableArea.
2590  *
2591  * This function returns the current size request policy of the
2592  * widget. That policy controls the way the size_request is done in
2593  * the pannable area. Check
2594  * hildon_pannable_area_set_size_request_policy() for a more detailed
2595  * explanation.
2596  *
2597  * returns: the policy is currently being used in the widget
2598  * #HildonSizeRequestPolicy.
2599  **/
2600 HildonSizeRequestPolicy
2601 hildon_pannable_area_get_size_request_policy (HildonPannableArea *area)
2602 {
2603   HildonPannableAreaPrivate *priv;
2604
2605   g_return_val_if_fail (HILDON_IS_PANNABLE_AREA (area), FALSE);
2606
2607   priv = area->priv;
2608
2609   return priv->size_request_policy;
2610 }
2611
2612 /**
2613  * hildon_pannable_area_set_size_request_policy:
2614  * @area: A #HildonPannableArea.
2615  * @size_request_policy: One of the allowed #HildonSizeRequestPolicy
2616  *
2617  * This function sets the pannable area size request policy. That
2618  * policy controls the way the size_request is done in the pannable
2619  * area. Pannable can use the size request of its children
2620  * (#HILDON_SIZE_REQUEST_CHILDREN) or the minimum size required for
2621  * the area itself (#HILDON_SIZE_REQUEST_MINIMUM), the latter is the
2622  * default. Recall this size depends on the scrolling policy you are
2623  * requesting to the pannable area, if you set #GTK_POLICY_NEVER this
2624  * parameter will not have any effect with
2625  * #HILDON_SIZE_REQUEST_MINIMUM set.
2626  *
2627  **/
2628 void
2629 hildon_pannable_area_set_size_request_policy (HildonPannableArea *area,
2630                                               HildonSizeRequestPolicy size_request_policy)
2631 {
2632   HildonPannableAreaPrivate *priv;
2633
2634   g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
2635
2636   priv = area->priv;
2637
2638   if (priv->size_request_policy == size_request_policy)
2639     return;
2640
2641   priv->size_request_policy = size_request_policy;
2642
2643   gtk_widget_queue_resize (GTK_WIDGET (area));
2644
2645   g_object_notify (G_OBJECT (area), "size-request-policy");
2646 }
2647