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