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