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