2 * This file is a part of hildon
4 * Copyright (C) 2008 Nokia Corporation, all rights reserved.
6 * Contact: Rodrigo Novo <rodrigo.novo@nokia.com>
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.
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.
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.
25 * SECTION: hildon-pannable-area
26 * @short_description: A scrolling widget designed for touch screens
27 * @see_also: #GtkScrolledWindow
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.
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
41 #undef HILDON_DISABLE_DEPRECATED
44 #if USE_CAIRO_SCROLLBARS == 1
49 #include "hildon-pannable-area.h"
50 #include "hildon-marshalers.h"
51 #include "hildon-enum-types.h"
53 #define USE_CAIRO_SCROLLBARS 0
55 #define SCROLL_BAR_MIN_SIZE 5
56 #define RATIO_TOLERANCE 0.000001
57 #define SCROLL_FADE_TIMEOUT 100
58 #define MOTION_EVENTS_PER_SECOND 25
59 #define CURSOR_STOPPED_TIMEOUT 80
60 #define MAX_SPEED_THRESHOLD 250
61 #define PANNABLE_MAX_WIDTH 788
62 #define PANNABLE_MAX_HEIGHT 378
64 G_DEFINE_TYPE (HildonPannableArea, hildon_pannable_area, GTK_TYPE_BIN)
66 #define PANNABLE_AREA_PRIVATE(o) \
67 (G_TYPE_INSTANCE_GET_PRIVATE ((o), HILDON_TYPE_PANNABLE_AREA, \
68 HildonPannableAreaPrivate))
70 struct _HildonPannableAreaPrivate {
71 HildonPannableAreaMode mode;
72 HildonMovementMode mov_mode;
73 GdkWindow *event_window;
74 gdouble x; /* Used to store mouse co-ordinates of the first or */
75 gdouble y; /* previous events in a press-motion pair */
76 gdouble ex; /* Used to store mouse co-ordinates of the last */
77 gdouble ey; /* motion event in acceleration mode */
79 gboolean button_pressed;
80 guint32 last_time; /* Last event time, to stop infinite loops */
86 gdouble vmax_overshooting;
93 guint panning_threshold;
94 guint scrollbar_fade_delay;
97 guint direction_error_margin;
103 gint ix; /* Initial click mouse co-ordinates */
105 gint cx; /* Initial click child window mouse co-ordinates */
112 gint overshot_dist_x;
113 gint overshot_dist_y;
116 gdouble scroll_indicator_alpha;
117 gint motion_event_scroll_timeout;
118 gint scroll_indicator_timeout;
119 gint scroll_indicator_event_interrupt;
120 gint scroll_delay_counter;
123 gboolean initial_hint;
124 gboolean initial_effect;
125 gboolean low_friction_mode;
128 gboolean size_request_policy;
129 gboolean hscroll_visible;
130 gboolean vscroll_visible;
131 GdkRectangle hscroll_rect;
132 GdkRectangle vscroll_rect;
133 guint indicator_width;
135 GtkAdjustment *hadjust;
136 GtkAdjustment *vadjust;
138 GtkPolicyType vscrollbar_policy;
139 GtkPolicyType hscrollbar_policy;
141 GdkGC *scrollbars_gc;
151 static guint pannable_area_signals [LAST_SIGNAL] = { 0 };
159 PROP_VEL_MAX_OVERSHOOTING,
160 PROP_VELOCITY_FAST_FACTOR,
164 PROP_PANNING_THRESHOLD,
165 PROP_SCROLLBAR_FADE_DELAY,
168 PROP_DIRECTION_ERROR_MARGIN,
169 PROP_VSCROLLBAR_POLICY,
170 PROP_HSCROLLBAR_POLICY,
175 PROP_LOW_FRICTION_MODE,
176 PROP_SIZE_REQUEST_POLICY,
182 static void hildon_pannable_area_class_init (HildonPannableAreaClass * klass);
183 static void hildon_pannable_area_init (HildonPannableArea * area);
184 static void hildon_pannable_area_get_property (GObject * object,
188 static void hildon_pannable_area_set_property (GObject * object,
190 const GValue * value,
192 static void hildon_pannable_area_dispose (GObject * object);
193 static void hildon_pannable_area_realize (GtkWidget * widget);
194 static void hildon_pannable_area_unrealize (GtkWidget * widget);
195 static void hildon_pannable_area_size_request (GtkWidget * widget,
196 GtkRequisition * requisition);
197 static void hildon_pannable_area_size_allocate (GtkWidget * widget,
198 GtkAllocation * allocation);
199 static void hildon_pannable_area_child_allocate_calculate (GtkWidget * widget,
200 GtkAllocation * allocation,
201 GtkAllocation * child_allocation);
202 static void hildon_pannable_area_style_set (GtkWidget * widget,
203 GtkStyle * previous_style);
204 static void hildon_pannable_area_map (GtkWidget * widget);
205 static void hildon_pannable_area_unmap (GtkWidget * widget);
206 static void hildon_pannable_area_grab_notify (GtkWidget *widget,
207 gboolean was_grabbed,
209 #if USE_CAIRO_SCROLLBARS == 1
210 static void rgb_from_gdkcolor (GdkColor *color, gdouble *r, gdouble *g, gdouble *b);
211 #else /* USE_CAIRO_SCROLLBARS */
212 static void tranparency_color (GdkColor *color,
215 gdouble transparency);
216 #endif /* USE_CAIRO_SCROLLBARS */
217 static void hildon_pannable_draw_vscroll (GtkWidget * widget,
218 GdkColor *back_color,
219 GdkColor *scroll_color);
220 static void hildon_pannable_draw_hscroll (GtkWidget * widget,
221 GdkColor *back_color,
222 GdkColor *scroll_color);
223 static void hildon_pannable_area_initial_effect (GtkWidget * widget);
224 static void hildon_pannable_area_redraw (HildonPannableArea * area);
225 static void hildon_pannable_area_launch_fade_timeout (HildonPannableArea * area,
227 static void hildon_pannable_area_adjust_value_changed (HildonPannableArea * area,
229 static void hildon_pannable_area_adjust_changed (HildonPannableArea * area,
231 static gboolean hildon_pannable_area_scroll_indicator_fade(HildonPannableArea * area);
232 static gboolean hildon_pannable_area_expose_event (GtkWidget * widget,
233 GdkEventExpose * event);
234 static GdkWindow * hildon_pannable_area_get_topmost (GdkWindow * window,
236 gint * tx, gint * ty,
238 static void synth_crossing (GdkWindow * child,
240 gint x_root, gint y_root,
241 guint32 time, gboolean in);
242 static gboolean hildon_pannable_area_button_press_cb (GtkWidget * widget,
243 GdkEventButton * event);
244 static void hildon_pannable_area_refresh (HildonPannableArea * area);
245 static gboolean hildon_pannable_area_check_scrollbars (HildonPannableArea * area);
246 static void hildon_pannable_axis_scroll (HildonPannableArea *area,
247 GtkAdjustment *adjust,
255 static void hildon_pannable_area_scroll (HildonPannableArea *area,
256 gdouble x, gdouble y);
257 static gboolean hildon_pannable_area_timeout (HildonPannableArea * area);
258 static void hildon_pannable_area_calculate_velocity (gdouble *vel,
262 gdouble drag_inertia,
265 static gboolean hildon_pannable_area_motion_event_scroll_timeout (HildonPannableArea *area);
266 static void hildon_pannable_area_motion_event_scroll (HildonPannableArea *area,
267 gdouble x, gdouble y);
268 static gboolean hildon_pannable_area_motion_notify_cb (GtkWidget * widget,
269 GdkEventMotion * event);
270 static gboolean hildon_pannable_leave_notify_event (GtkWidget *widget,
271 GdkEventCrossing *event);
272 static gboolean hildon_pannable_area_button_release_cb (GtkWidget * widget,
273 GdkEventButton * event);
274 static gboolean hildon_pannable_area_scroll_cb (GtkWidget *widget,
275 GdkEventScroll *event);
276 static void hildon_pannable_area_child_mapped (GtkWidget *widget,
279 static void hildon_pannable_area_add (GtkContainer *container, GtkWidget *child);
280 static void hildon_pannable_area_remove (GtkContainer *container, GtkWidget *child);
281 static void hildon_pannable_calculate_vel_factor (HildonPannableArea * self);
285 hildon_pannable_area_class_init (HildonPannableAreaClass * klass)
287 GObjectClass *object_class = G_OBJECT_CLASS (klass);
288 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
289 GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
292 g_type_class_add_private (klass, sizeof (HildonPannableAreaPrivate));
294 object_class->get_property = hildon_pannable_area_get_property;
295 object_class->set_property = hildon_pannable_area_set_property;
296 object_class->dispose = hildon_pannable_area_dispose;
298 widget_class->realize = hildon_pannable_area_realize;
299 widget_class->unrealize = hildon_pannable_area_unrealize;
300 widget_class->map = hildon_pannable_area_map;
301 widget_class->unmap = hildon_pannable_area_unmap;
302 widget_class->size_request = hildon_pannable_area_size_request;
303 widget_class->size_allocate = hildon_pannable_area_size_allocate;
304 widget_class->expose_event = hildon_pannable_area_expose_event;
305 widget_class->style_set = hildon_pannable_area_style_set;
306 widget_class->button_press_event = hildon_pannable_area_button_press_cb;
307 widget_class->button_release_event = hildon_pannable_area_button_release_cb;
308 widget_class->motion_notify_event = hildon_pannable_area_motion_notify_cb;
309 widget_class->leave_notify_event = hildon_pannable_leave_notify_event;
310 widget_class->scroll_event = hildon_pannable_area_scroll_cb;
312 container_class->add = hildon_pannable_area_add;
313 container_class->remove = hildon_pannable_area_remove;
315 klass->horizontal_movement = NULL;
316 klass->vertical_movement = NULL;
318 g_object_class_install_property (object_class,
320 g_param_spec_boolean ("enabled",
322 "Enable or disable finger-scroll.",
327 g_object_class_install_property (object_class,
328 PROP_VSCROLLBAR_POLICY,
329 g_param_spec_enum ("vscrollbar_policy",
331 "Visual policy of the vertical scrollbar",
332 GTK_TYPE_POLICY_TYPE,
333 GTK_POLICY_AUTOMATIC,
337 g_object_class_install_property (object_class,
338 PROP_HSCROLLBAR_POLICY,
339 g_param_spec_enum ("hscrollbar_policy",
341 "Visual policy of the horizontal scrollbar",
342 GTK_TYPE_POLICY_TYPE,
343 GTK_POLICY_AUTOMATIC,
347 g_object_class_install_property (object_class,
349 g_param_spec_enum ("mode",
351 "Change the finger-scrolling mode.",
352 HILDON_TYPE_PANNABLE_AREA_MODE,
353 HILDON_PANNABLE_AREA_MODE_AUTO,
357 g_object_class_install_property (object_class,
359 g_param_spec_flags ("mov_mode",
360 "Scroll movement mode",
361 "Controls if the widget can scroll vertically, horizontally or both",
362 HILDON_TYPE_MOVEMENT_MODE,
363 HILDON_MOVEMENT_MODE_VERT,
367 g_object_class_install_property (object_class,
369 g_param_spec_double ("velocity_min",
370 "Minimum scroll velocity",
371 "Minimum distance the child widget should scroll "
372 "per 'frame', in pixels per frame.",
377 g_object_class_install_property (object_class,
379 g_param_spec_double ("velocity_max",
380 "Maximum scroll velocity",
381 "Maximum distance the child widget should scroll "
382 "per 'frame', in pixels per frame.",
387 g_object_class_install_property (object_class,
388 PROP_VEL_MAX_OVERSHOOTING,
389 g_param_spec_double ("velocity_overshooting_max",
390 "Maximum scroll velocity when overshooting",
391 "Maximum distance the child widget should scroll "
392 "per 'frame', in pixels per frame when it overshoots after hitting the edge.",
397 g_object_class_install_property (object_class,
398 PROP_VELOCITY_FAST_FACTOR,
399 g_param_spec_double ("velocity_fast_factor",
400 "Fast velocity factor",
401 "Minimum velocity that is considered 'fast': "
402 "children widgets won't receive button presses. "
403 "Expressed as a fraction of the maximum velocity.",
408 g_object_class_install_property (object_class,
410 g_param_spec_double ("deceleration",
411 "Deceleration multiplier",
412 "The multiplier used when decelerating when in "
413 "acceleration scrolling mode.",
418 g_object_class_install_property (object_class,
420 g_param_spec_double ("drag_inertia",
421 "Inertia of the cursor dragging",
422 "Percentage of the calculated speed in each moment we are are going to use"
423 "to calculate the launch speed, the other part would be the speed"
424 "calculated previously",
429 g_object_class_install_property (object_class,
431 g_param_spec_uint ("sps",
432 "Scrolls per second",
433 "Amount of scroll events to generate per second.",
438 g_object_class_install_property (object_class,
439 PROP_PANNING_THRESHOLD,
440 g_param_spec_uint ("panning_threshold",
441 "Threshold to consider a motion event an scroll",
442 "Amount of pixels to consider a motion event an scroll, if it is less"
443 "it is a click detected incorrectly by the touch screen.",
448 g_object_class_install_property (object_class,
449 PROP_SCROLLBAR_FADE_DELAY,
450 g_param_spec_uint ("scrollbar_fade_delay",
451 "Time before starting to fade the scrollbar",
452 "Time the scrollbar is going to be visible if the widget is not in"
453 "action in miliseconds",
458 g_object_class_install_property (object_class,
460 g_param_spec_uint ("bounce_steps",
462 "Number of steps that is going to be used to bounce when hitting the"
463 "edge, the rubberband effect depends on it",
468 g_object_class_install_property (object_class,
470 g_param_spec_uint ("force",
471 "Multiplier of the calculated speed",
472 "Force applied to the movement, multiplies the calculated speed of the"
473 "user movement the cursor in the screen",
478 g_object_class_install_property (object_class,
479 PROP_DIRECTION_ERROR_MARGIN,
480 g_param_spec_uint ("direction_error_margin",
481 "Margin in the direction detection",
482 "After detecting the direction of the movement (horizontal or"
483 "vertical), we can add this margin of error to allow the movement in"
484 "the other direction even apparently it is not",
489 g_object_class_install_property (object_class,
491 g_param_spec_int ("vovershoot_max",
492 "Vertical overshoot distance",
493 "Space we allow the widget to pass over its vertical limits when"
494 "hitting the edges, set 0 in order to deactivate overshooting.",
499 g_object_class_install_property (object_class,
501 g_param_spec_int ("hovershoot_max",
502 "Horizontal overshoot distance",
503 "Space we allow the widget to pass over its horizontal limits when"
504 "hitting the edges, set 0 in order to deactivate overshooting.",
509 g_object_class_install_property (object_class,
511 g_param_spec_double ("scroll_time",
512 "Time to scroll to a position",
513 "The time to scroll to a position when calling the hildon_pannable_scroll_to function",
518 g_object_class_install_property (object_class,
520 g_param_spec_boolean ("initial-hint",
522 "Whether to hint the user about the pannability of the container.",
527 g_object_class_install_property (object_class,
528 PROP_LOW_FRICTION_MODE,
529 g_param_spec_boolean ("low-friction-mode",
530 "Do not decelerate the initial velocity",
531 "Avoid decelerating the panning movement, like no friction, the widget"
532 "will stop in the edges or if the user clicks.",
537 g_object_class_install_property (object_class,
538 PROP_SIZE_REQUEST_POLICY,
539 g_param_spec_enum ("size-request-policy",
540 "Size Requisition policy",
541 "Controls the size request policy of the widget",
542 HILDON_TYPE_SIZE_REQUEST_POLICY,
543 HILDON_SIZE_REQUEST_MINIMUM,
547 g_object_class_install_property (object_class,
549 g_param_spec_object ("hadjustment",
550 "Horizontal Adjustment",
551 "The GtkAdjustment for the horizontal position",
554 g_object_class_install_property (object_class,
556 g_param_spec_object ("vadjustment",
557 "Vertical Adjustment",
558 "The GtkAdjustment for the vertical position",
562 gtk_widget_class_install_style_property (widget_class,
565 "Width of the scroll indicators",
566 "Pixel width used to draw the scroll indicators.",
571 * HildonPannableArea::horizontal-movement:
572 * @hildonpannable: the object which received the signal
573 * @direction: the direction of the movement #HILDON_MOVEMENT_LEFT or #HILDON_MOVEMENT_RIGHT
574 * @initial_x: the x coordinate of the point where the user clicked to start the movement
575 * @initial_y: the y coordinate of the point where the user clicked to start the movement
577 * The horizontal-movement signal is emitted when the pannable area
578 * detects a horizontal movement. The detection does not mean the
579 * widget is going to move (i.e. maybe the children are smaller
580 * horizontally than the screen).
584 pannable_area_signals[HORIZONTAL_MOVEMENT] =
585 g_signal_new ("horizontal_movement",
586 G_TYPE_FROM_CLASS (object_class),
587 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
588 G_STRUCT_OFFSET (HildonPannableAreaClass, horizontal_movement),
590 _hildon_marshal_VOID__INT_DOUBLE_DOUBLE,
597 * HildonPannableArea::vertical-movement:
598 * @hildonpannable: the object which received the signal
599 * @direction: the direction of the movement #HILDON_MOVEMENT_UP or #HILDON_MOVEMENT_DOWN
600 * @initial_x: the x coordinate of the point where the user clicked to start the movement
601 * @initial_y: the y coordinate of the point where the user clicked to start the movement
603 * The vertical-movement signal is emitted when the pannable area
604 * detects a vertical movement. The detection does not mean the
605 * widget is going to move (i.e. maybe the children are smaller
606 * vertically than the screen).
610 pannable_area_signals[VERTICAL_MOVEMENT] =
611 g_signal_new ("vertical_movement",
612 G_TYPE_FROM_CLASS (object_class),
613 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
614 G_STRUCT_OFFSET (HildonPannableAreaClass, vertical_movement),
616 _hildon_marshal_VOID__INT_DOUBLE_DOUBLE,
625 hildon_pannable_area_init (HildonPannableArea * area)
627 HildonPannableAreaPrivate *priv = PANNABLE_AREA_PRIVATE (area);
629 GTK_WIDGET_UNSET_FLAGS (area, GTK_NO_WINDOW);
634 priv->button_pressed = FALSE;
637 priv->vscroll_visible = TRUE;
638 priv->hscroll_visible = TRUE;
639 priv->indicator_width = 6;
640 priv->overshot_dist_x = 0;
641 priv->overshot_dist_y = 0;
642 priv->overshooting_y = 0;
643 priv->overshooting_x = 0;
647 priv->scroll_indicator_alpha = 0.0;
648 priv->scroll_indicator_timeout = 0;
649 priv->motion_event_scroll_timeout = 0;
650 priv->scroll_indicator_event_interrupt = 0;
651 priv->scroll_delay_counter = 0;
652 priv->scrollbar_fade_delay = 0;
653 priv->scroll_to_x = -1;
654 priv->scroll_to_y = -1;
655 priv->first_drag = TRUE;
656 priv->initial_effect = TRUE;
657 priv->child_width = 0;
658 priv->child_height = 0;
659 priv->last_in = TRUE;
661 gtk_widget_add_events (GTK_WIDGET (area), GDK_POINTER_MOTION_HINT_MASK);
664 GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
666 GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
668 g_object_ref_sink (G_OBJECT (priv->hadjust));
669 g_object_ref_sink (G_OBJECT (priv->vadjust));
671 g_signal_connect_swapped (priv->hadjust, "value-changed",
672 G_CALLBACK (hildon_pannable_area_adjust_value_changed), area);
673 g_signal_connect_swapped (priv->vadjust, "value-changed",
674 G_CALLBACK (hildon_pannable_area_adjust_value_changed), area);
675 g_signal_connect_swapped (priv->hadjust, "changed",
676 G_CALLBACK (hildon_pannable_area_adjust_changed), area);
677 g_signal_connect_swapped (priv->vadjust, "changed",
678 G_CALLBACK (hildon_pannable_area_adjust_changed), area);
679 g_signal_connect (area, "grab-notify",
680 G_CALLBACK (hildon_pannable_area_grab_notify), NULL);
684 hildon_pannable_area_get_property (GObject * object,
689 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (object)->priv;
691 switch (property_id) {
693 g_value_set_boolean (value, priv->enabled);
696 g_value_set_enum (value, priv->mode);
698 case PROP_MOVEMENT_MODE:
699 g_value_set_flags (value, priv->mov_mode);
701 case PROP_VELOCITY_MIN:
702 g_value_set_double (value, priv->vmin);
704 case PROP_VELOCITY_MAX:
705 g_value_set_double (value, priv->vmax);
707 case PROP_VEL_MAX_OVERSHOOTING:
708 g_value_set_double (value, priv->vmax_overshooting);
710 case PROP_VELOCITY_FAST_FACTOR:
711 g_value_set_double (value, priv->vfast_factor);
713 case PROP_DECELERATION:
714 g_value_set_double (value, priv->decel);
716 case PROP_DRAG_INERTIA:
717 g_value_set_double (value, priv->drag_inertia);
720 g_value_set_uint (value, priv->sps);
722 case PROP_PANNING_THRESHOLD:
723 g_value_set_uint (value, priv->panning_threshold);
725 case PROP_SCROLLBAR_FADE_DELAY:
726 /* convert to miliseconds */
727 g_value_set_uint (value, priv->scrollbar_fade_delay * SCROLL_FADE_TIMEOUT);
729 case PROP_BOUNCE_STEPS:
730 g_value_set_uint (value, priv->bounce_steps);
733 g_value_set_uint (value, priv->force);
735 case PROP_DIRECTION_ERROR_MARGIN:
736 g_value_set_uint (value, priv->direction_error_margin);
738 case PROP_VSCROLLBAR_POLICY:
739 g_value_set_enum (value, priv->vscrollbar_policy);
741 case PROP_HSCROLLBAR_POLICY:
742 g_value_set_enum (value, priv->hscrollbar_policy);
744 case PROP_VOVERSHOOT_MAX:
745 g_value_set_int (value, priv->vovershoot_max);
747 case PROP_HOVERSHOOT_MAX:
748 g_value_set_int (value, priv->hovershoot_max);
750 case PROP_SCROLL_TIME:
751 g_value_set_double (value, priv->scroll_time);
753 case PROP_INITIAL_HINT:
754 g_value_set_boolean (value, priv->initial_hint);
756 case PROP_LOW_FRICTION_MODE:
757 g_value_set_boolean (value, priv->low_friction_mode);
759 case PROP_SIZE_REQUEST_POLICY:
760 g_value_set_enum (value, priv->size_request_policy);
762 case PROP_HADJUSTMENT:
763 g_value_set_object (value,
764 hildon_pannable_area_get_hadjustment
765 (HILDON_PANNABLE_AREA (object)));
767 case PROP_VADJUSTMENT:
768 g_value_set_object (value,
769 hildon_pannable_area_get_vadjustment
770 (HILDON_PANNABLE_AREA (object)));
773 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
778 hildon_pannable_area_set_property (GObject * object,
780 const GValue * value,
783 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (object)->priv;
786 switch (property_id) {
788 enabled = g_value_get_boolean (value);
790 if ((priv->enabled != enabled) && (GTK_WIDGET_REALIZED (object))) {
792 gdk_window_raise (priv->event_window);
794 gdk_window_lower (priv->event_window);
797 priv->enabled = enabled;
800 priv->mode = g_value_get_enum (value);
802 case PROP_MOVEMENT_MODE:
803 priv->mov_mode = g_value_get_flags (value);
805 case PROP_VELOCITY_MIN:
806 priv->vmin = g_value_get_double (value);
808 case PROP_VELOCITY_MAX:
809 priv->vmax = g_value_get_double (value);
811 case PROP_VEL_MAX_OVERSHOOTING:
812 priv->vmax_overshooting = g_value_get_double (value);
814 case PROP_VELOCITY_FAST_FACTOR:
815 priv->vfast_factor = g_value_get_double (value);
817 case PROP_DECELERATION:
818 priv->decel = g_value_get_double (value);
819 hildon_pannable_calculate_vel_factor (HILDON_PANNABLE_AREA (object));
821 case PROP_DRAG_INERTIA:
822 priv->drag_inertia = g_value_get_double (value);
825 priv->sps = g_value_get_uint (value);
827 case PROP_PANNING_THRESHOLD:
829 GtkSettings *settings = gtk_settings_get_default ();
830 GtkSettingsValue svalue = { NULL, { 0, }, };
832 priv->panning_threshold = g_value_get_uint (value);
834 /* insure gtk dnd is the same we are using, not allowed
835 different thresholds in the same application */
836 svalue.origin = "panning_threshold";
837 g_value_init (&svalue.value, G_TYPE_LONG);
838 g_value_set_long (&svalue.value, priv->panning_threshold);
839 gtk_settings_set_property_value (settings, "gtk-dnd-drag-threshold", &svalue);
840 g_value_unset (&svalue.value);
843 case PROP_SCROLLBAR_FADE_DELAY:
844 /* convert to miliseconds */
845 priv->scrollbar_fade_delay = g_value_get_uint (value)/(SCROLL_FADE_TIMEOUT);
847 case PROP_BOUNCE_STEPS:
848 priv->bounce_steps = g_value_get_uint (value);
851 priv->force = g_value_get_uint (value);
853 case PROP_DIRECTION_ERROR_MARGIN:
854 priv->direction_error_margin = g_value_get_uint (value);
856 case PROP_VSCROLLBAR_POLICY:
857 priv->vscrollbar_policy = g_value_get_enum (value);
859 gtk_widget_queue_resize (GTK_WIDGET (object));
861 case PROP_HSCROLLBAR_POLICY:
862 priv->hscrollbar_policy = g_value_get_enum (value);
864 gtk_widget_queue_resize (GTK_WIDGET (object));
866 case PROP_VOVERSHOOT_MAX:
867 priv->vovershoot_max = g_value_get_int (value);
869 case PROP_HOVERSHOOT_MAX:
870 priv->hovershoot_max = g_value_get_int (value);
872 case PROP_SCROLL_TIME:
873 priv->scroll_time = g_value_get_double (value);
875 hildon_pannable_calculate_vel_factor (HILDON_PANNABLE_AREA (object));
877 case PROP_INITIAL_HINT:
878 priv->initial_hint = g_value_get_boolean (value);
880 case PROP_LOW_FRICTION_MODE:
881 priv->low_friction_mode = g_value_get_boolean (value);
883 case PROP_SIZE_REQUEST_POLICY:
884 hildon_pannable_area_set_size_request_policy (HILDON_PANNABLE_AREA (object),
885 g_value_get_enum (value));
889 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
894 hildon_pannable_area_dispose (GObject * object)
896 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (object)->priv;
897 GtkWidget *child = gtk_bin_get_child (GTK_BIN (object));
900 g_source_remove (priv->idle_id);
904 if (priv->scroll_indicator_timeout){
905 g_source_remove (priv->scroll_indicator_timeout);
906 priv->scroll_indicator_timeout = 0;
909 if (priv->motion_event_scroll_timeout){
910 g_source_remove (priv->motion_event_scroll_timeout);
911 priv->motion_event_scroll_timeout = 0;
915 g_signal_handlers_disconnect_by_func (child,
916 hildon_pannable_area_child_mapped,
920 g_signal_handlers_disconnect_by_func (object,
921 hildon_pannable_area_grab_notify,
925 g_signal_handlers_disconnect_by_func (priv->hadjust,
926 hildon_pannable_area_adjust_value_changed,
928 g_signal_handlers_disconnect_by_func (priv->hadjust,
929 hildon_pannable_area_adjust_changed,
931 g_object_unref (priv->hadjust);
932 priv->hadjust = NULL;
936 g_signal_handlers_disconnect_by_func (priv->vadjust,
937 hildon_pannable_area_adjust_value_changed,
939 g_signal_handlers_disconnect_by_func (priv->vadjust,
940 hildon_pannable_area_adjust_changed,
942 g_object_unref (priv->vadjust);
943 priv->vadjust = NULL;
946 if (G_OBJECT_CLASS (hildon_pannable_area_parent_class)->dispose)
947 G_OBJECT_CLASS (hildon_pannable_area_parent_class)->dispose (object);
951 hildon_pannable_area_realize (GtkWidget * widget)
953 GdkWindowAttr attributes;
954 gint attributes_mask;
956 HildonPannableAreaPrivate *priv;
958 priv = HILDON_PANNABLE_AREA (widget)->priv;
960 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
962 border_width = GTK_CONTAINER (widget)->border_width;
964 attributes.x = widget->allocation.x + border_width;
965 attributes.y = widget->allocation.y + border_width;
966 attributes.width = MAX (widget->allocation.width - 2 * border_width, 0);
967 attributes.height = MAX (widget->allocation.height - 2 * border_width, 0);
968 attributes.window_type = GDK_WINDOW_CHILD;
970 /* avoid using the hildon_window */
971 attributes.visual = gtk_widget_get_visual (widget);
972 attributes.colormap = gtk_widget_get_colormap (widget);
973 attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK;
974 attributes.wclass = GDK_INPUT_OUTPUT;
976 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
978 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
979 &attributes, attributes_mask);
980 gdk_window_set_user_data (widget->window, widget);
982 /* create the events window */
985 attributes.event_mask = gtk_widget_get_events (widget)
986 | GDK_BUTTON_MOTION_MASK
987 | GDK_BUTTON_PRESS_MASK
988 | GDK_BUTTON_RELEASE_MASK
990 | GDK_EXPOSURE_MASK | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK;
991 attributes.wclass = GDK_INPUT_ONLY;
993 attributes_mask = GDK_WA_X | GDK_WA_Y;
995 priv->event_window = gdk_window_new (widget->window,
996 &attributes, attributes_mask);
997 gdk_window_set_user_data (priv->event_window, widget);
999 widget->style = gtk_style_attach (widget->style, widget->window);
1000 gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
1002 priv->scrollbars_gc = gdk_gc_new (GDK_DRAWABLE (widget->window));
1003 gdk_gc_copy (priv->scrollbars_gc, widget->style->fg_gc[GTK_STATE_INSENSITIVE]);
1007 hildon_pannable_area_unrealize (GtkWidget * widget)
1009 HildonPannableAreaPrivate *priv;
1011 priv = HILDON_PANNABLE_AREA (widget)->priv;
1013 if (priv->event_window != NULL) {
1014 gdk_window_set_user_data (priv->event_window, NULL);
1015 gdk_window_destroy (priv->event_window);
1016 priv->event_window = NULL;
1019 gdk_gc_unref (priv->scrollbars_gc);
1021 if (GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->unrealize)
1022 (*GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->unrealize)(widget);
1026 hildon_pannable_area_size_request (GtkWidget * widget,
1027 GtkRequisition * requisition)
1029 GtkRequisition child_requisition = {0};
1030 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1031 GtkWidget *child = gtk_bin_get_child (GTK_BIN (widget));
1033 if (child && GTK_WIDGET_VISIBLE (child))
1035 gtk_widget_size_request (child, &child_requisition);
1038 if (priv->hscrollbar_policy == GTK_POLICY_NEVER) {
1039 requisition->width = child_requisition.width;
1041 switch (priv->size_request_policy) {
1042 case HILDON_SIZE_REQUEST_CHILDREN:
1043 requisition->width = MIN (PANNABLE_MAX_WIDTH,
1044 child_requisition.width);
1046 case HILDON_SIZE_REQUEST_MINIMUM:
1048 requisition->width = priv->indicator_width;
1052 if (priv->vscrollbar_policy == GTK_POLICY_NEVER) {
1053 requisition->height = child_requisition.height;
1055 switch (priv->size_request_policy) {
1056 case HILDON_SIZE_REQUEST_CHILDREN:
1057 requisition->height = MIN (PANNABLE_MAX_HEIGHT,
1058 child_requisition.height);
1060 case HILDON_SIZE_REQUEST_MINIMUM:
1062 requisition->height = priv->indicator_width;
1066 requisition->width += 2 * GTK_CONTAINER (widget)->border_width;
1067 requisition->height += 2 * GTK_CONTAINER (widget)->border_width;
1071 hildon_pannable_area_child_allocate_calculate (GtkWidget * widget,
1072 GtkAllocation * allocation,
1073 GtkAllocation * child_allocation)
1076 HildonPannableAreaPrivate *priv;
1078 border_width = GTK_CONTAINER (widget)->border_width;
1080 priv = HILDON_PANNABLE_AREA (widget)->priv;
1082 child_allocation->x = 0;
1083 child_allocation->y = 0;
1084 child_allocation->width = MAX (allocation->width - 2 * border_width -
1085 (priv->vscroll_visible ? priv->vscroll_rect.width : 0), 0);
1086 child_allocation->height = MAX (allocation->height - 2 * border_width -
1087 (priv->hscroll_visible ? priv->hscroll_rect.height : 0), 0);
1089 if (priv->overshot_dist_y > 0) {
1090 child_allocation->y = MIN (child_allocation->y + priv->overshot_dist_y,
1091 child_allocation->height);
1092 child_allocation->height = MAX (child_allocation->height - priv->overshot_dist_y, 0);
1093 } else if (priv->overshot_dist_y < 0) {
1094 child_allocation->height = MAX (child_allocation->height + priv->overshot_dist_y, 0);
1097 if (priv->overshot_dist_x > 0) {
1098 child_allocation->x = MIN (child_allocation->x + priv->overshot_dist_x,
1099 child_allocation->width);
1100 child_allocation->width = MAX (child_allocation->width - priv->overshot_dist_x, 0);
1101 } else if (priv->overshot_dist_x < 0) {
1102 child_allocation->width = MAX (child_allocation->width + priv->overshot_dist_x, 0);
1107 hildon_pannable_area_size_allocate (GtkWidget * widget,
1108 GtkAllocation * allocation)
1110 GtkAllocation child_allocation;
1111 HildonPannableAreaPrivate *priv;
1112 GtkWidget *child = gtk_bin_get_child (GTK_BIN (widget));
1115 border_width = GTK_CONTAINER (widget)->border_width;
1117 widget->allocation = *allocation;
1119 priv = HILDON_PANNABLE_AREA (widget)->priv;
1121 if (GTK_WIDGET_REALIZED (widget)) {
1122 gdk_window_move_resize (widget->window,
1123 allocation->x + border_width,
1124 allocation->y + border_width,
1125 allocation->width - border_width * 2,
1126 allocation->height - border_width * 2);
1127 gdk_window_move_resize (priv->event_window,
1130 allocation->width - border_width * 2,
1131 allocation->height - border_width * 2);
1134 if (child && GTK_WIDGET_VISIBLE (child)) {
1136 hildon_pannable_area_child_allocate_calculate (widget,
1140 gtk_widget_size_allocate (child, &child_allocation);
1142 if (hildon_pannable_area_check_scrollbars (HILDON_PANNABLE_AREA (widget))) {
1143 hildon_pannable_area_child_allocate_calculate (widget,
1147 gtk_widget_size_allocate (child, &child_allocation);
1150 /* we have to do this after child size_allocate because page_size is
1151 * changed when we allocate the size of the children */
1152 if (priv->overshot_dist_y < 0) {
1153 gtk_adjustment_set_value (priv->vadjust, priv->vadjust->upper -
1154 priv->vadjust->page_size);
1157 if (priv->overshot_dist_x < 0) {
1158 gtk_adjustment_set_value (priv->hadjust, priv->hadjust->upper -
1159 priv->hadjust->page_size);
1163 hildon_pannable_area_check_scrollbars (HILDON_PANNABLE_AREA (widget));
1168 hildon_pannable_area_style_set (GtkWidget * widget,
1169 GtkStyle * previous_style)
1171 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1173 GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->
1174 style_set (widget, previous_style);
1176 gtk_widget_style_get (widget, "indicator-width", &priv->indicator_width, NULL);
1180 hildon_pannable_area_map (GtkWidget * widget)
1182 HildonPannableAreaPrivate *priv;
1184 priv = HILDON_PANNABLE_AREA (widget)->priv;
1186 gdk_window_show (widget->window);
1188 if (priv->event_window != NULL && !priv->enabled)
1189 gdk_window_show (priv->event_window);
1191 (*GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->map) (widget);
1193 if (priv->event_window != NULL && priv->enabled)
1194 gdk_window_show (priv->event_window);
1198 hildon_pannable_area_unmap (GtkWidget * widget)
1200 HildonPannableAreaPrivate *priv;
1202 priv = HILDON_PANNABLE_AREA (widget)->priv;
1204 if (priv->event_window != NULL)
1205 gdk_window_hide (priv->event_window);
1207 gdk_window_hide (widget->window);
1209 (*GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->unmap) (widget);
1213 hildon_pannable_area_grab_notify (GtkWidget *widget,
1214 gboolean was_grabbed,
1217 /* an internal widget has grabbed the focus and now has returned it,
1218 we have to do some release actions */
1220 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1222 priv->scroll_indicator_event_interrupt = 0;
1224 if ((!priv->scroll_indicator_timeout)&&(priv->scroll_indicator_alpha)>0.1) {
1225 priv->scroll_delay_counter = priv->scrollbar_fade_delay;
1227 hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget),
1228 priv->scroll_indicator_alpha);
1231 priv->last_type = 3;
1232 priv->moved = FALSE;
1236 #if USE_CAIRO_SCROLLBARS == 1
1239 rgb_from_gdkcolor (GdkColor *color, gdouble *r, gdouble *g, gdouble *b)
1241 *r = (color->red >> 8) / 255.0;
1242 *g = (color->green >> 8) / 255.0;
1243 *b = (color->blue >> 8) / 255.0;
1247 hildon_pannable_draw_vscroll (GtkWidget * widget,
1248 GdkColor *back_color,
1249 GdkColor *scroll_color)
1251 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1254 cairo_pattern_t *pattern;
1256 gint radius = (priv->vscroll_rect.width/2) - 1;
1258 cr = gdk_cairo_create(widget->window);
1260 /* Draw the background */
1261 rgb_from_gdkcolor (back_color, &r, &g, &b);
1262 cairo_set_source_rgb (cr, r, g, b);
1263 cairo_rectangle(cr, priv->vscroll_rect.x, priv->vscroll_rect.y,
1264 priv->vscroll_rect.width,
1265 priv->vscroll_rect.height);
1266 cairo_fill_preserve (cr);
1269 /* Calculate the scroll bar height and position */
1270 y = ((priv->vadjust->value - priv->vadjust->lower) / (priv->vadjust->upper - priv->vadjust->lower)) *
1271 (widget->allocation.height -
1272 (priv->hscroll_visible ? priv->indicator_width : 0));
1273 height = ((((priv->vadjust->value - priv->vadjust->lower) +
1274 priv->vadjust->page_size) /
1275 (priv->vadjust->upper - priv->vadjust->lower)) *
1276 (widget->allocation.height -
1277 (priv->hscroll_visible ? priv->indicator_width : 0))) - y;
1279 /* Set a minimum height */
1280 height = MAX (SCROLL_BAR_MIN_SIZE, height);
1282 /* Check the max y position */
1283 y = MIN (y, widget->allocation.height -
1284 (priv->hscroll_visible ? priv->hscroll_rect.height : 0) -
1287 /* Draw the scrollbar */
1288 rgb_from_gdkcolor (scroll_color, &r, &g, &b);
1290 pattern = cairo_pattern_create_linear(radius+1, y, radius+1,y + height);
1291 cairo_pattern_add_color_stop_rgb(pattern, 0, r, g, b);
1292 cairo_pattern_add_color_stop_rgb(pattern, 1, r/2, g/2, b/2);
1293 cairo_set_source(cr, pattern);
1295 cairo_pattern_destroy(pattern);
1297 cairo_arc(cr, priv->vscroll_rect.x + radius + 1, y + radius + 1, radius, G_PI, 0);
1298 cairo_line_to(cr, priv->vscroll_rect.x + (radius * 2) + 1, y + height - radius);
1299 cairo_arc(cr, priv->vscroll_rect.x + radius + 1, y + height - radius, radius, 0, G_PI);
1300 cairo_line_to(cr, priv->vscroll_rect.x + 1, y + height - radius);
1303 cairo_paint_with_alpha(cr, priv->scroll_indicator_alpha);
1309 hildon_pannable_draw_hscroll (GtkWidget * widget,
1310 GdkColor *back_color,
1311 GdkColor *scroll_color)
1313 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1316 cairo_pattern_t *pattern;
1318 gint radius = (priv->hscroll_rect.height/2) - 1;
1320 cr = gdk_cairo_create(widget->window);
1322 /* Draw the background */
1323 rgb_from_gdkcolor (back_color, &r, &g, &b);
1324 cairo_set_source_rgb (cr, r, g, b);
1325 cairo_rectangle(cr, priv->hscroll_rect.x, priv->hscroll_rect.y,
1326 priv->hscroll_rect.width,
1327 priv->hscroll_rect.height);
1328 cairo_fill_preserve (cr);
1331 /* calculate the scrollbar width and position */
1332 x = ((priv->hadjust->value - priv->hadjust->lower) / (priv->hadjust->upper - priv->hadjust->lower)) *
1333 (widget->allocation.width - (priv->vscroll_visible ? priv->indicator_width : 0));
1334 width =((((priv->hadjust->value - priv->hadjust->lower) +
1335 priv->hadjust->page_size) / (priv->hadjust->upper - priv->hadjust->lower)) *
1336 (widget->allocation.width -
1337 (priv->vscroll_visible ? priv->indicator_width : 0))) - x;
1339 /* Set a minimum width */
1340 width = MAX (SCROLL_BAR_MIN_SIZE, width);
1342 /* Check the max x position */
1343 x = MIN (x, widget->allocation.width -
1344 (priv->vscroll_visible ? priv->vscroll_rect.width : 0) -
1347 /* Draw the scrollbar */
1348 rgb_from_gdkcolor (scroll_color, &r, &g, &b);
1350 pattern = cairo_pattern_create_linear(x, radius+1, x+width, radius+1);
1351 cairo_pattern_add_color_stop_rgb(pattern, 0, r, g, b);
1352 cairo_pattern_add_color_stop_rgb(pattern, 1, r/2, g/2, b/2);
1353 cairo_set_source(cr, pattern);
1355 cairo_pattern_destroy(pattern);
1357 cairo_arc_negative(cr, x + radius + 1, priv->hscroll_rect.y + radius + 1, radius, 3*G_PI_2, G_PI_2);
1358 cairo_line_to(cr, x + width - radius, priv->hscroll_rect.y + (radius * 2) + 1);
1359 cairo_arc_negative(cr, x + width - radius, priv->hscroll_rect.y + radius + 1, radius, G_PI_2, 3*G_PI_2);
1360 cairo_line_to(cr, x + width - radius, priv->hscroll_rect.y + 1);
1363 cairo_paint_with_alpha(cr, priv->scroll_indicator_alpha);
1368 #else /* USE_CAIRO_SCROLLBARS */
1371 tranparency_color (GdkColor *color,
1374 gdouble transparency)
1378 diff = colora.red - colorb.red;
1379 color->red = colora.red-diff*transparency;
1381 diff = colora.green - colorb.green;
1382 color->green = colora.green-diff*transparency;
1384 diff = colora.blue - colorb.blue;
1385 color->blue = colora.blue-diff*transparency;
1389 hildon_pannable_draw_vscroll (GtkWidget *widget,
1390 GdkColor *back_color,
1391 GdkColor *scroll_color)
1393 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1395 GdkColor transp_color;
1396 GdkGC *gc = widget->style->fg_gc[GTK_STATE_INSENSITIVE];
1398 gdk_draw_rectangle (widget->window,
1399 widget->style->bg_gc[GTK_STATE_NORMAL],
1401 priv->vscroll_rect.x, priv->vscroll_rect.y,
1402 priv->vscroll_rect.width,
1403 priv->vscroll_rect.height);
1405 y = ((priv->vadjust->value - priv->vadjust->lower) / (priv->vadjust->upper - priv->vadjust->lower)) *
1406 (widget->allocation.height - (priv->hscroll_visible ? priv->indicator_width : 0));
1407 height = ((((priv->vadjust->value - priv->vadjust->lower) + priv->vadjust->page_size) /
1408 (priv->vadjust->upper - priv->vadjust->lower)) *
1409 (widget->allocation.height -
1410 (priv->hscroll_visible ? priv->indicator_width : 0))) - y;
1412 /* Set a minimum height */
1413 height = MAX (SCROLL_BAR_MIN_SIZE, height);
1415 /* Check the max y position */
1416 y = MIN (y, widget->allocation.height -
1417 (priv->hscroll_visible ? priv->hscroll_rect.height : 0) -
1420 if (priv->scroll_indicator_alpha < 1.0) {
1421 tranparency_color (&transp_color, *back_color, *scroll_color,
1422 priv->scroll_indicator_alpha);
1424 gdk_gc_set_rgb_fg_color (priv->scrollbars_gc, &transp_color);
1426 gc = priv->scrollbars_gc;
1429 gdk_draw_rectangle (widget->window, gc,
1430 TRUE, priv->vscroll_rect.x, y,
1431 priv->vscroll_rect.width, height);
1435 hildon_pannable_draw_hscroll (GtkWidget *widget,
1436 GdkColor *back_color,
1437 GdkColor *scroll_color)
1439 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1441 GdkColor transp_color;
1442 GdkGC *gc = widget->style->fg_gc[GTK_STATE_INSENSITIVE];
1444 gdk_draw_rectangle (widget->window,
1445 widget->style->bg_gc[GTK_STATE_INSENSITIVE],
1447 priv->hscroll_rect.x, priv->hscroll_rect.y,
1448 priv->hscroll_rect.width,
1449 priv->hscroll_rect.height);
1451 /* calculate the scrollbar width and position */
1452 x = ((priv->hadjust->value - priv->hadjust->lower) / (priv->hadjust->upper - priv->hadjust->lower)) *
1453 (widget->allocation.width - (priv->vscroll_visible ? priv->indicator_width : 0));
1454 width =((((priv->hadjust->value - priv->hadjust->lower) +
1455 priv->hadjust->page_size) / (priv->hadjust->upper - priv->hadjust->lower)) *
1456 (widget->allocation.width -
1457 (priv->vscroll_visible ? priv->indicator_width : 0))) - x;
1459 /* Set a minimum width */
1460 width = MAX (SCROLL_BAR_MIN_SIZE, width);
1462 /* Check the max x position */
1463 x = MIN (x, widget->allocation.width -
1464 (priv->vscroll_visible ? priv->vscroll_rect.width : 0) -
1467 if (priv->scroll_indicator_alpha < 1.0) {
1468 tranparency_color (&transp_color, *back_color, *scroll_color,
1469 priv->scroll_indicator_alpha);
1471 gdk_gc_set_rgb_fg_color (priv->scrollbars_gc, &transp_color);
1473 gc = priv->scrollbars_gc;
1476 gdk_draw_rectangle (widget->window, gc,
1477 TRUE, x, priv->hscroll_rect.y, width,
1478 priv->hscroll_rect.height);
1481 #endif /* USE_CAIRO_SCROLLBARS */
1484 hildon_pannable_area_initial_effect (GtkWidget * widget)
1486 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1488 if (priv->vscroll_visible || priv->hscroll_visible) {
1490 priv->scroll_indicator_event_interrupt = 0;
1491 priv->scroll_delay_counter = priv->scrollbar_fade_delay;
1493 hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget), 1.0);
1495 priv->initial_effect = FALSE;
1500 hildon_pannable_area_launch_fade_timeout (HildonPannableArea * area,
1503 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (area)->priv;
1505 priv->scroll_indicator_alpha = alpha;
1507 if (!priv->scroll_indicator_timeout)
1508 priv->scroll_indicator_timeout =
1509 gdk_threads_add_timeout (SCROLL_FADE_TIMEOUT,
1510 (GSourceFunc) hildon_pannable_area_scroll_indicator_fade,
1515 hildon_pannable_area_adjust_changed (HildonPannableArea * area,
1518 if (GTK_WIDGET_REALIZED (area))
1519 hildon_pannable_area_refresh (area);
1523 hildon_pannable_area_adjust_value_changed (HildonPannableArea * area,
1526 if (GTK_WIDGET_REALIZED (area)) {
1527 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (area)->priv;
1529 hildon_pannable_area_redraw (area);
1531 if ((priv->vscroll_visible) || (priv->hscroll_visible)) {
1532 priv->scroll_indicator_event_interrupt = 0;
1533 priv->scroll_delay_counter = priv->scrollbar_fade_delay;
1535 hildon_pannable_area_launch_fade_timeout (area, 1.0);
1541 hildon_pannable_area_redraw (HildonPannableArea * area)
1543 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (area)->priv;
1545 /* Redraw scroll indicators */
1546 if (GTK_WIDGET_DRAWABLE (area)) {
1547 if (priv->hscroll_visible) {
1548 gdk_window_invalidate_rect (GTK_WIDGET (area)->window,
1549 &priv->hscroll_rect, FALSE);
1552 if (priv->vscroll_visible) {
1553 gdk_window_invalidate_rect (GTK_WIDGET (area)->window,
1554 &priv->vscroll_rect, FALSE);
1560 hildon_pannable_area_scroll_indicator_fade(HildonPannableArea * area)
1562 HildonPannableAreaPrivate *priv = area->priv;
1564 /* if moving do not fade out */
1565 if (((ABS (priv->vel_y)>priv->vmin)||
1566 (ABS (priv->vel_x)>priv->vmin))&&(!priv->button_pressed)) {
1571 if (priv->scroll_indicator_event_interrupt) {
1572 /* Stop a fade out, and fade back in */
1573 if (priv->scroll_indicator_alpha > 0.9) {
1574 priv->scroll_indicator_alpha = 1.0;
1575 priv->scroll_indicator_timeout = 0;
1579 priv->scroll_indicator_alpha += 0.2;
1580 hildon_pannable_area_redraw (area);
1586 if ((priv->scroll_indicator_alpha > 0.9) &&
1587 (priv->scroll_delay_counter > 0)) {
1588 priv->scroll_delay_counter--;
1593 if (!priv->scroll_indicator_event_interrupt) {
1594 /* Continue fade out */
1595 if (priv->scroll_indicator_alpha < 0.1) {
1596 priv->scroll_indicator_timeout = 0;
1597 priv->scroll_indicator_alpha = 0.0;
1601 priv->scroll_indicator_alpha -= 0.2;
1602 hildon_pannable_area_redraw (area);
1612 hildon_pannable_area_expose_event (GtkWidget * widget,
1613 GdkEventExpose * event)
1616 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1617 #if USE_CAIRO_SCROLLBARS == 1
1618 GdkColor back_color = widget->style->bg[GTK_STATE_NORMAL];
1619 GdkColor scroll_color = widget->style->base[GTK_STATE_SELECTED];
1620 #else /* USE_CAIRO_SCROLLBARS */
1621 GdkColor back_color = widget->style->bg[GTK_STATE_NORMAL];
1622 GdkColor scroll_color = widget->style->fg[GTK_STATE_INSENSITIVE];
1625 if (G_UNLIKELY ((priv->initial_hint) && (priv->initial_effect))) {
1626 hildon_pannable_area_initial_effect (widget);
1629 if (gtk_bin_get_child (GTK_BIN (widget))) {
1631 if (priv->scroll_indicator_alpha > 0.1) {
1632 if (priv->vscroll_visible) {
1633 hildon_pannable_draw_vscroll (widget, &back_color, &scroll_color);
1635 if (priv->hscroll_visible) {
1636 hildon_pannable_draw_hscroll (widget, &back_color, &scroll_color);
1640 /* draw overshooting rectangles */
1641 if (priv->overshot_dist_y > 0) {
1642 gint overshot_height;
1644 overshot_height = MIN (priv->overshot_dist_y, widget->allocation.height -
1645 (priv->hscroll_visible ? priv->hscroll_rect.height : 0));
1647 gdk_draw_rectangle (widget->window,
1648 widget->style->bg_gc[GTK_STATE_NORMAL],
1652 widget->allocation.width -
1653 (priv->vscroll_visible ? priv->vscroll_rect.width : 0),
1655 } else if (priv->overshot_dist_y < 0) {
1656 gint overshot_height;
1660 MAX (priv->overshot_dist_y,
1661 -(widget->allocation.height -
1662 (priv->hscroll_visible ? priv->hscroll_rect.height : 0)));
1664 overshot_y = MAX (widget->allocation.height +
1666 (priv->hscroll_visible ? priv->hscroll_rect.height : 0), 0);
1668 gdk_draw_rectangle (widget->window,
1669 widget->style->bg_gc[GTK_STATE_NORMAL],
1673 widget->allocation.width -
1674 priv->vscroll_rect.width,
1678 if (priv->overshot_dist_x > 0) {
1679 gint overshot_width;
1681 overshot_width = MIN (priv->overshot_dist_x, widget->allocation.width -
1682 (priv->vscroll_visible ? priv->vscroll_rect.width : 0));
1684 gdk_draw_rectangle (widget->window,
1685 widget->style->bg_gc[GTK_STATE_NORMAL],
1690 widget->allocation.height -
1691 (priv->hscroll_visible ? priv->hscroll_rect.height : 0));
1692 } else if (priv->overshot_dist_x < 0) {
1693 gint overshot_width;
1697 MAX (priv->overshot_dist_x,
1698 -(widget->allocation.width -
1699 (priv->vscroll_visible ? priv->vscroll_rect.width : 0)));
1701 overshot_x = MAX (widget->allocation.width +
1703 (priv->vscroll_visible ? priv->vscroll_rect.width : 0), 0);
1705 gdk_draw_rectangle (widget->window,
1706 widget->style->bg_gc[GTK_STATE_NORMAL],
1711 widget->allocation.height -
1712 priv->hscroll_rect.height);
1717 return GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->expose_event (widget, event);
1721 hildon_pannable_area_get_topmost (GdkWindow * window,
1723 gint * tx, gint * ty,
1726 /* Find the GdkWindow at the given point, by recursing from a given
1727 * parent GdkWindow. Optionally return the co-ordinates transformed
1728 * relative to the child window.
1731 GList *c, *children;
1732 GdkWindow *selected_window = NULL;
1734 gdk_drawable_get_size (GDK_DRAWABLE (window), &width, &height);
1735 if ((x < 0) || (x >= width) || (y < 0) || (y >= height))
1738 children = gdk_window_peek_children (window);
1745 selected_window = window;
1748 for (c = children; c; c = c->next) {
1749 GdkWindow *child = (GdkWindow *) c->data;
1752 gdk_drawable_get_size (GDK_DRAWABLE (child), &width, &height);
1753 gdk_window_get_position (child, &wx, &wy);
1755 if ((x >= wx) && (x < (wx + width)) && (y >= wy) && (y < (wy + height)) &&
1756 (gdk_window_is_visible (child))) {
1758 if (gdk_window_peek_children (child)) {
1759 selected_window = hildon_pannable_area_get_topmost (child, x-wx, y-wy,
1761 if (!selected_window) {
1766 selected_window = child;
1769 if ((gdk_window_get_events (child)&mask)) {
1774 selected_window = child;
1780 return selected_window;
1784 synth_crossing (GdkWindow * child,
1786 gint x_root, gint y_root,
1787 guint32 time, gboolean in)
1789 GdkEventCrossing *crossing_event;
1790 GdkEventType type = in ? GDK_ENTER_NOTIFY : GDK_LEAVE_NOTIFY;
1792 /* Send synthetic enter event */
1793 crossing_event = (GdkEventCrossing *) gdk_event_new (type);
1794 ((GdkEventAny *) crossing_event)->type = type;
1795 ((GdkEventAny *) crossing_event)->window = g_object_ref (child);
1796 ((GdkEventAny *) crossing_event)->send_event = FALSE;
1797 crossing_event->subwindow = g_object_ref (child);
1798 crossing_event->time = time;
1799 crossing_event->x = x;
1800 crossing_event->y = y;
1801 crossing_event->x_root = x_root;
1802 crossing_event->y_root = y_root;
1803 crossing_event->mode = GDK_CROSSING_NORMAL;
1804 crossing_event->detail = GDK_NOTIFY_UNKNOWN;
1805 crossing_event->focus = FALSE;
1806 crossing_event->state = 0;
1807 gdk_event_put ((GdkEvent *) crossing_event);
1808 gdk_event_free ((GdkEvent *) crossing_event);
1812 hildon_pannable_area_button_press_cb (GtkWidget * widget,
1813 GdkEventButton * event)
1816 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1818 if ((!priv->enabled) || (event->button != 1) ||
1819 ((event->time == priv->last_time) &&
1820 (priv->last_type == 1)) || (gtk_bin_get_child (GTK_BIN (widget)) == NULL))
1823 priv->scroll_indicator_event_interrupt = 1;
1825 hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget),
1826 priv->scroll_indicator_alpha);
1828 priv->last_time = event->time;
1829 priv->last_type = 1;
1831 priv->scroll_to_x = -1;
1832 priv->scroll_to_y = -1;
1834 if (priv->button_pressed && priv->child) {
1835 /* Widget stole focus on last click, send crossing-out event */
1836 synth_crossing (priv->child, 0, 0, event->x_root, event->y_root,
1837 event->time, FALSE);
1845 /* Don't allow a click if we're still moving fast */
1846 if ((ABS (priv->vel_x) <= (priv->vmax * priv->vfast_factor)) &&
1847 (ABS (priv->vel_y) <= (priv->vmax * priv->vfast_factor)))
1849 hildon_pannable_area_get_topmost (gtk_bin_get_child (GTK_BIN (widget))->window,
1850 event->x, event->y, &x, &y, GDK_BUTTON_PRESS_MASK);
1854 priv->button_pressed = TRUE;
1856 /* Stop scrolling on mouse-down (so you can flick, then hold to stop) */
1862 gdk_drawable_get_size (priv->child, &priv->child_width,
1863 &priv->child_height);
1864 priv->last_in = TRUE;
1866 g_object_add_weak_pointer ((GObject *) priv->child,
1867 (gpointer) & priv->child);
1869 event = (GdkEventButton *) gdk_event_copy ((GdkEvent *) event);
1875 synth_crossing (priv->child, x, y, event->x_root,
1876 event->y_root, event->time, TRUE);
1878 /* Send synthetic click (button press/release) event */
1879 ((GdkEventAny *) event)->window = g_object_ref (priv->child);
1881 gdk_event_put ((GdkEvent *) event);
1882 gdk_event_free ((GdkEvent *) event);
1890 hildon_pannable_area_check_scrollbars (HildonPannableArea * area)
1892 HildonPannableAreaPrivate *priv = area->priv;
1893 gboolean prev_hscroll_visible, prev_vscroll_visible;
1895 prev_hscroll_visible = priv->hscroll_visible;
1896 prev_vscroll_visible = priv->vscroll_visible;
1898 if (!gtk_bin_get_child (GTK_BIN (area))) {
1899 priv->vscroll_visible = FALSE;
1900 priv->hscroll_visible = FALSE;
1902 switch (priv->hscrollbar_policy) {
1903 case GTK_POLICY_ALWAYS:
1904 priv->hscroll_visible = TRUE;
1906 case GTK_POLICY_NEVER:
1907 priv->hscroll_visible = FALSE;
1910 priv->hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
1911 priv->hadjust->page_size);
1914 switch (priv->vscrollbar_policy) {
1915 case GTK_POLICY_ALWAYS:
1916 priv->vscroll_visible = TRUE;
1918 case GTK_POLICY_NEVER:
1919 priv->vscroll_visible = FALSE;
1922 priv->vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
1923 priv->vadjust->page_size);
1926 /* Store the vscroll/hscroll areas for redrawing */
1927 if (priv->vscroll_visible) {
1928 GtkAllocation *allocation = >K_WIDGET (area)->allocation;
1929 priv->vscroll_rect.x = allocation->width - priv->indicator_width;
1930 priv->vscroll_rect.y = 0;
1931 priv->vscroll_rect.width = priv->indicator_width;
1932 priv->vscroll_rect.height = allocation->height -
1933 (priv->hscroll_visible ? priv->indicator_width : 0);
1935 if (priv->hscroll_visible) {
1936 GtkAllocation *allocation = >K_WIDGET (area)->allocation;
1937 priv->hscroll_rect.y = allocation->height - priv->indicator_width;
1938 priv->hscroll_rect.x = 0;
1939 priv->hscroll_rect.height = priv->indicator_width;
1940 priv->hscroll_rect.width = allocation->width -
1941 (priv->vscroll_visible ? priv->indicator_width : 0);
1945 return ((priv->hscroll_visible != prev_hscroll_visible) ||
1946 (priv->vscroll_visible != prev_vscroll_visible));
1950 hildon_pannable_area_refresh (HildonPannableArea * area)
1952 if (GTK_WIDGET_DRAWABLE (area) &&
1953 hildon_pannable_area_check_scrollbars (area)) {
1954 gtk_widget_queue_resize (GTK_WIDGET (area));
1956 hildon_pannable_area_redraw (area);
1960 /* Scroll by a particular amount (in pixels). Optionally, return if
1961 * the scroll on a particular axis was successful.
1964 hildon_pannable_axis_scroll (HildonPannableArea *area,
1965 GtkAdjustment *adjust,
1969 gint *overshot_dist,
1975 HildonPannableAreaPrivate *priv = area->priv;
1977 dist = gtk_adjustment_get_value (adjust) - inc;
1980 * We use overshot_dist to define the distance of the current overshoot,
1981 * and overshooting to define the direction/whether or not we are overshot
1983 if (!(*overshooting)) {
1985 /* Initiation of the overshoot happens when the finger is released
1986 * and the current position of the pannable contents are out of range
1988 if (dist < adjust->lower) {
1991 dist = adjust->lower;
1993 if (overshoot_max!=0) {
1996 *overshot_dist = CLAMP (*overshot_dist + *vel, 0, overshoot_max);
1997 *vel = MIN (priv->vmax_overshooting, *vel);
1998 gtk_widget_queue_resize (GTK_WIDGET (area));
2002 } else if (dist > adjust->upper - adjust->page_size) {
2005 dist = adjust->upper - adjust->page_size;
2007 if (overshoot_max!=0) {
2010 *overshot_dist = CLAMP (*overshot_dist + *vel, -overshoot_max, 0);
2011 *vel = MAX (-priv->vmax_overshooting, *vel);
2012 gtk_widget_queue_resize (GTK_WIDGET (area));
2017 if ((*scroll_to) != -1) {
2018 if (((inc < 0)&&(*scroll_to <= dist))||
2019 ((inc > 0)&&(*scroll_to >= dist))) {
2027 gtk_adjustment_set_value (adjust, dist);
2029 if (!priv->button_pressed) {
2031 /* When the overshoot has started we continue for
2032 * PROP_BOUNCE_STEPS more steps into the overshoot before we
2033 * reverse direction. The deceleration factor is calculated
2034 * based on the percentage distance from the first item with
2035 * each iteration, therefore always returning us to the
2036 * top/bottom most element
2038 if (*overshot_dist > 0) {
2040 if ((*overshooting < priv->bounce_steps) && (*vel > 0)) {
2042 *vel = (((gdouble)*overshot_dist)/overshoot_max) * (*vel);
2043 } else if ((*overshooting >= priv->bounce_steps) && (*vel > 0)) {
2045 } else if ((*overshooting > 1) && (*vel < 0)) {
2046 /* we add the MIN in order to avoid very small speeds */
2047 *vel = MIN ((((gdouble)*overshot_dist)*0.4) * -1, -2.0);
2050 *overshot_dist = CLAMP (*overshot_dist + *vel, 0, overshoot_max);
2052 gtk_widget_queue_resize (GTK_WIDGET (area));
2054 } else if (*overshot_dist < 0) {
2056 if ((*overshooting < priv->bounce_steps) && (*vel < 0)) {
2058 *vel = (((gdouble)*overshot_dist)/overshoot_max) * (*vel) * -1;
2059 } else if ((*overshooting >= priv->bounce_steps) && (*vel < 0)) {
2061 } else if ((*overshooting > 1) && (*vel > 0)) {
2062 /* we add the MAX in order to avoid very small speeds */
2063 *vel = MAX ((((gdouble)*overshot_dist)*0.4) * -1, 2.0);
2066 *overshot_dist = CLAMP (*overshot_dist + (*vel), -overshoot_max, 0);
2068 gtk_widget_queue_resize (GTK_WIDGET (area));
2073 gtk_widget_queue_resize (GTK_WIDGET (area));
2077 gint overshot_dist_old = *overshot_dist;
2079 if (*overshot_dist > 0) {
2080 *overshot_dist = CLAMP ((*overshot_dist) + inc, 0, overshoot_max);
2081 } else if (*overshot_dist < 0) {
2082 *overshot_dist = CLAMP ((*overshot_dist) + inc, -1 * overshoot_max, 0);
2085 gtk_adjustment_set_value (adjust, dist);
2088 if (*overshot_dist != overshot_dist_old)
2089 gtk_widget_queue_resize (GTK_WIDGET (area));
2095 hildon_pannable_area_scroll (HildonPannableArea *area,
2096 gdouble x, gdouble y)
2099 HildonPannableAreaPrivate *priv = area->priv;
2100 gboolean hscroll_visible, vscroll_visible;
2102 if (gtk_bin_get_child (GTK_BIN (area)) == NULL)
2105 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
2106 priv->vadjust->page_size);
2107 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
2108 priv->hadjust->page_size);
2113 if (vscroll_visible) {
2114 hildon_pannable_axis_scroll (area, priv->vadjust, &priv->vel_y, y,
2115 &priv->overshooting_y, &priv->overshot_dist_y,
2116 &priv->scroll_to_y, priv->vovershoot_max, &sy);
2121 if (hscroll_visible) {
2122 hildon_pannable_axis_scroll (area, priv->hadjust, &priv->vel_x, x,
2123 &priv->overshooting_x, &priv->overshot_dist_x,
2124 &priv->scroll_to_x, priv->hovershoot_max, &sx);
2129 /* If the scroll on a particular axis wasn't succesful, reset the
2130 * initial scroll position to the new mouse co-ordinate. This means
2131 * when you get to the top of the page, dragging down works immediately.
2133 if (priv->mode == HILDON_PANNABLE_AREA_MODE_ACCEL) {
2145 hildon_pannable_area_timeout (HildonPannableArea * area)
2147 HildonPannableAreaPrivate *priv = area->priv;
2149 if ((!priv->enabled) || (priv->mode == HILDON_PANNABLE_AREA_MODE_PUSH)) {
2155 if (!priv->button_pressed) {
2156 /* Decelerate gradually when pointer is raised */
2157 if ((!priv->overshot_dist_y) &&
2158 (!priv->overshot_dist_x)) {
2160 /* in case we move to a specific point do not decelerate when arriving */
2161 if ((priv->scroll_to_x != -1)||(priv->scroll_to_y != -1)) {
2163 if (ABS (priv->vel_x) >= 1.5) {
2164 priv->vel_x *= priv->decel;
2167 if (ABS (priv->vel_y) >= 1.5) {
2168 priv->vel_y *= priv->decel;
2172 if ((!priv->low_friction_mode) ||
2173 ((priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ) &&
2174 (ABS (priv->vel_x) < 0.8*priv->vmax)))
2175 priv->vel_x *= priv->decel;
2177 if ((!priv->low_friction_mode) ||
2178 ((priv->mov_mode&HILDON_MOVEMENT_MODE_VERT) &&
2179 (ABS (priv->vel_y) < 0.8*priv->vmax)))
2180 priv->vel_y *= priv->decel;
2182 if ((ABS (priv->vel_x) < 1.0) && (ABS (priv->vel_y) < 1.0)) {
2191 } else if (priv->mode == HILDON_PANNABLE_AREA_MODE_AUTO) {
2197 hildon_pannable_area_scroll (area, priv->vel_x, priv->vel_y);
2203 hildon_pannable_area_calculate_velocity (gdouble *vel,
2207 gdouble drag_inertia,
2213 if (ABS (dist) >= RATIO_TOLERANCE) {
2214 rawvel = (dist / ABS (delta)) * force;
2215 *vel = *vel * (1 - drag_inertia) +
2216 rawvel * drag_inertia;
2217 *vel = *vel > 0 ? MIN (*vel, vmax)
2218 : MAX (*vel, -1 * vmax);
2223 hildon_pannable_area_motion_event_scroll_timeout (HildonPannableArea *area)
2225 HildonPannableAreaPrivate *priv = area->priv;
2227 if ((priv->motion_x != 0)||(priv->motion_y != 0))
2228 hildon_pannable_area_scroll (area, priv->motion_x, priv->motion_y);
2230 priv->motion_event_scroll_timeout = 0;
2236 hildon_pannable_area_motion_event_scroll (HildonPannableArea *area,
2237 gdouble x, gdouble y)
2239 HildonPannableAreaPrivate *priv = area->priv;
2241 if (priv->motion_event_scroll_timeout) {
2243 priv->motion_x += x;
2244 priv->motion_y += y;
2248 /* we do not delay the first event but the next ones */
2249 hildon_pannable_area_scroll (area, x, y);
2254 priv->motion_event_scroll_timeout = gdk_threads_add_timeout
2255 ((gint) (1000.0 / (gdouble) MOTION_EVENTS_PER_SECOND),
2256 (GSourceFunc) hildon_pannable_area_motion_event_scroll_timeout, area);
2261 hildon_pannable_area_motion_notify_cb (GtkWidget * widget,
2262 GdkEventMotion * event)
2264 HildonPannableArea *area = HILDON_PANNABLE_AREA (widget);
2265 HildonPannableAreaPrivate *priv = area->priv;
2269 if (gtk_bin_get_child (GTK_BIN (widget)) == NULL)
2272 if ((!priv->enabled) || (!priv->button_pressed) ||
2273 ((event->time == priv->last_time) && (priv->last_type == 2))) {
2274 gdk_window_get_pointer (widget->window, NULL, NULL, 0);
2278 if (priv->last_type == 1) {
2279 priv->first_drag = TRUE;
2282 x = event->x - priv->x;
2283 y = event->y - priv->y;
2285 if (priv->first_drag && (!priv->moved) &&
2286 ((ABS (x) > (priv->panning_threshold))
2287 || (ABS (y) > (priv->panning_threshold)))) {
2292 if (priv->first_drag) {
2293 gboolean vscroll_visible;
2294 gboolean hscroll_visible;
2296 if (ABS (priv->iy - event->y) >=
2297 ABS (priv->ix - event->x)) {
2299 g_signal_emit (area,
2300 pannable_area_signals[VERTICAL_MOVEMENT],
2301 0, (priv->iy > event->y) ?
2302 HILDON_MOVEMENT_UP :
2303 HILDON_MOVEMENT_DOWN,
2304 (gdouble)priv->ix, (gdouble)priv->iy);
2306 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
2307 priv->vadjust->page_size);
2309 if (!((vscroll_visible)&&
2310 (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT))) {
2312 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
2313 priv->hadjust->page_size);
2315 /* even in case we do not have to move we check if this
2316 could be a fake horizontal movement */
2317 if (!((hscroll_visible)&&
2318 (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ)) ||
2319 (ABS (priv->iy - event->y) -
2320 ABS (priv->ix - event->x) >= priv->direction_error_margin))
2321 priv->moved = FALSE;
2325 g_signal_emit (area,
2326 pannable_area_signals[HORIZONTAL_MOVEMENT],
2327 0, (priv->ix > event->x) ?
2328 HILDON_MOVEMENT_LEFT :
2329 HILDON_MOVEMENT_RIGHT,
2330 (gdouble)priv->ix, (gdouble)priv->iy);
2332 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
2333 priv->hadjust->page_size);
2335 if (!((hscroll_visible)&&
2336 (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ))) {
2338 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
2339 priv->vadjust->page_size);
2341 /* even in case we do not have to move we check if this
2342 could be a fake vertical movement */
2343 if (!((vscroll_visible) &&
2344 (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT)) ||
2345 (ABS (priv->ix - event->x) -
2346 ABS (priv->iy - event->y) >= priv->direction_error_margin))
2347 priv->moved = FALSE;
2351 if ((priv->moved)&&(priv->child)) {
2354 pos_x = priv->cx + (event->x - priv->ix);
2355 pos_y = priv->cy + (event->y - priv->iy);
2357 synth_crossing (priv->child, pos_x, pos_y, event->x_root,
2358 event->y_root, event->time, FALSE);
2362 priv->first_drag = FALSE;
2364 if ((priv->mode != HILDON_PANNABLE_AREA_MODE_PUSH) &&
2365 (priv->mode != HILDON_PANNABLE_AREA_MODE_AUTO)) {
2368 priv->idle_id = gdk_threads_add_timeout ((gint)
2369 (1000.0 / (gdouble) priv->sps),
2371 hildon_pannable_area_timeout, area);
2376 switch (priv->mode) {
2377 case HILDON_PANNABLE_AREA_MODE_PUSH:
2378 /* Scroll by the amount of pixels the cursor has moved
2379 * since the last motion event.
2381 hildon_pannable_area_motion_event_scroll (area, x, y);
2385 case HILDON_PANNABLE_AREA_MODE_ACCEL:
2386 /* Set acceleration relative to the initial click */
2387 priv->ex = event->x;
2388 priv->ey = event->y;
2389 priv->vel_x = ((x > 0) ? 1 : -1) *
2391 (gdouble) widget->allocation.width) *
2392 (priv->vmax - priv->vmin)) + priv->vmin);
2393 priv->vel_y = ((y > 0) ? 1 : -1) *
2395 (gdouble) widget->allocation.height) *
2396 (priv->vmax - priv->vmin)) + priv->vmin);
2398 case HILDON_PANNABLE_AREA_MODE_AUTO:
2400 delta = event->time - priv->last_time;
2402 if (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT) {
2403 gdouble dist = event->y - priv->y;
2405 hildon_pannable_area_calculate_velocity (&priv->vel_y,
2417 if (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ) {
2418 gdouble dist = event->x - priv->x;
2420 hildon_pannable_area_calculate_velocity (&priv->vel_x,
2432 hildon_pannable_area_motion_event_scroll (area, x, y);
2434 if (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ)
2436 if (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT)
2444 } else if (priv->child) {
2448 pos_x = priv->cx + (event->x - priv->ix);
2449 pos_y = priv->cy + (event->y - priv->iy);
2451 in = (((0 <= pos_x)&&(priv->child_width >= pos_x)) &&
2452 ((0 <= pos_y)&&(priv->child_height >= pos_y)));
2454 if (((!priv->last_in)&&in)||((priv->last_in)&&(!in))) {
2456 synth_crossing (priv->child, pos_x, pos_y, event->x_root,
2457 event->y_root, event->time, in);
2463 priv->last_time = event->time;
2464 priv->last_type = 2;
2467 /* Send motion notify to child */
2468 event = (GdkEventMotion *) gdk_event_copy ((GdkEvent *) event);
2469 event->x = priv->cx + (event->x - priv->ix);
2470 event->y = priv->cy + (event->y - priv->iy);
2471 event->window = g_object_ref (priv->child);
2472 gdk_event_put ((GdkEvent *) event);
2473 gdk_event_free ((GdkEvent *) event);
2476 gdk_window_get_pointer (widget->window, NULL, NULL, 0);
2482 hildon_pannable_leave_notify_event (GtkWidget *widget,
2483 GdkEventCrossing *event)
2485 HildonPannableArea *area = HILDON_PANNABLE_AREA (widget);
2486 HildonPannableAreaPrivate *priv = area->priv;
2488 if ((priv->child)&&(priv->last_in)) {
2489 priv->last_in = FALSE;
2491 synth_crossing (priv->child, 0, 0, event->x_root,
2492 event->y_root, event->time, FALSE);
2499 hildon_pannable_area_button_release_cb (GtkWidget * widget,
2500 GdkEventButton * event)
2502 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
2506 if (((event->time == priv->last_time) && (priv->last_type == 3))
2507 || (gtk_bin_get_child (GTK_BIN (widget)) == NULL)
2508 || (!priv->button_pressed) || (!priv->enabled) || (event->button != 1))
2511 priv->scroll_indicator_event_interrupt = 0;
2512 priv->scroll_delay_counter = priv->scrollbar_fade_delay;
2514 /* move all the way to the last position */
2515 if (priv->motion_event_scroll_timeout) {
2516 g_source_remove (priv->motion_event_scroll_timeout);
2517 hildon_pannable_area_motion_event_scroll_timeout (HILDON_PANNABLE_AREA (widget));
2522 if (priv->last_type == 2) {
2523 gdouble delta = event->time - priv->last_time;
2525 if (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT) {
2526 gdouble dist = event->y - priv->y;
2528 if (ABS (dist) >= 1.0) {
2529 hildon_pannable_area_calculate_velocity (&priv->vel_y,
2537 priv->motion_y = dist;
2538 hildon_pannable_area_motion_event_scroll_timeout (HILDON_PANNABLE_AREA (widget));
2540 if (delta >= CURSOR_STOPPED_TIMEOUT) {
2546 if (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ) {
2547 gdouble dist = event->x - priv->x;
2549 if (ABS (dist) >= 1.0) {
2550 hildon_pannable_area_calculate_velocity (&priv->vel_x,
2557 priv->motion_x = dist;
2558 hildon_pannable_area_motion_event_scroll_timeout (HILDON_PANNABLE_AREA (widget));
2560 if (delta >= CURSOR_STOPPED_TIMEOUT) {
2567 if ((ABS (priv->vel_y) > priv->vmin)||
2568 (ABS (priv->vel_x) > priv->vmin)) {
2569 priv->scroll_indicator_alpha = 1.0;
2572 hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget),
2573 priv->scroll_indicator_alpha);
2575 priv->button_pressed = FALSE;
2577 if (priv->mode == HILDON_PANNABLE_AREA_MODE_AUTO ||
2578 priv->mode == HILDON_PANNABLE_AREA_MODE_ACCEL) {
2580 /* If overshoot has been initiated with a finger down, on release set max speed */
2581 if (priv->overshot_dist_y != 0) {
2582 priv->overshooting_y = priv->bounce_steps; /* Hack to stop a bounce in the finger down case */
2583 priv->vel_y = priv->vmax_overshooting;
2586 if (priv->overshot_dist_x != 0) {
2587 priv->overshooting_x = priv->bounce_steps; /* Hack to stop a bounce in the finger down case */
2588 priv->vel_x = priv->vmax_overshooting;
2591 if ((ABS (priv->vel_y) >= priv->vmin) ||
2592 (ABS (priv->vel_x) >= priv->vmin)) {
2594 if (ABS (priv->vel_x) > MAX_SPEED_THRESHOLD)
2595 priv->vel_x = (priv->vel_x > 0) ? priv->vmax : -priv->vmax;
2597 if (ABS (priv->vel_y) > MAX_SPEED_THRESHOLD)
2598 priv->vel_y = (priv->vel_y > 0) ? priv->vmax : -priv->vmax;
2601 priv->idle_id = gdk_threads_add_timeout ((gint) (1000.0 / (gdouble) priv->sps),
2603 hildon_pannable_area_timeout, widget);
2607 priv->last_time = event->time;
2608 priv->last_type = 3;
2611 priv->moved = FALSE;
2616 hildon_pannable_area_get_topmost (gtk_bin_get_child (GTK_BIN (widget))->window,
2617 event->x, event->y, &x, &y, GDK_BUTTON_RELEASE_MASK);
2619 event = (GdkEventButton *) gdk_event_copy ((GdkEvent *) event);
2623 /* Leave the widget if we've moved - This doesn't break selection,
2624 * but stops buttons from being clicked.
2626 if ((child != priv->child) || (priv->moved)) {
2627 /* Send synthetic leave event */
2628 synth_crossing (priv->child, x, y, event->x_root,
2629 event->y_root, event->time, FALSE);
2630 /* Send synthetic button release event */
2631 ((GdkEventAny *) event)->window = g_object_ref (priv->child);
2632 gdk_event_put ((GdkEvent *) event);
2634 /* Send synthetic button release event */
2635 ((GdkEventAny *) event)->window = g_object_ref (child);
2636 gdk_event_put ((GdkEvent *) event);
2637 /* Send synthetic leave event */
2638 synth_crossing (priv->child, x, y, event->x_root,
2639 event->y_root, event->time, FALSE);
2641 g_object_remove_weak_pointer ((GObject *) priv->child,
2642 (gpointer) & priv->child);
2644 priv->moved = FALSE;
2645 gdk_event_free ((GdkEvent *) event);
2650 /* utility event handler */
2652 hildon_pannable_area_scroll_cb (GtkWidget *widget,
2653 GdkEventScroll *event)
2655 GtkAdjustment *adj = NULL;
2656 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
2658 if ((!priv->enabled) ||
2659 (gtk_bin_get_child (GTK_BIN (widget)) == NULL))
2662 priv->scroll_indicator_event_interrupt = 0;
2663 priv->scroll_delay_counter = priv->scrollbar_fade_delay + 20;
2665 hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget), 1.0);
2667 /* Stop inertial scrolling */
2668 if (priv->idle_id) {
2671 priv->overshooting_x = 0;
2672 priv->overshooting_y = 0;
2674 if ((priv->overshot_dist_x>0)||(priv->overshot_dist_y>0)) {
2675 priv->overshot_dist_x = 0;
2676 priv->overshot_dist_y = 0;
2678 gtk_widget_queue_resize (GTK_WIDGET (widget));
2681 g_source_remove (priv->idle_id);
2685 if (event->direction == GDK_SCROLL_UP || event->direction == GDK_SCROLL_DOWN)
2686 adj = priv->vadjust;
2688 adj = priv->hadjust;
2692 gdouble delta, new_value;
2694 /* from gtkrange.c calculate delta*/
2695 delta = pow (adj->page_size, 2.0 / 3.0);
2697 if (event->direction == GDK_SCROLL_UP ||
2698 event->direction == GDK_SCROLL_LEFT)
2701 new_value = CLAMP (adj->value + delta, adj->lower, adj->upper - adj->page_size);
2703 gtk_adjustment_set_value (adj, new_value);
2710 hildon_pannable_area_child_mapped (GtkWidget *widget,
2714 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (user_data)->priv;
2716 if (priv->event_window != NULL && priv->enabled)
2717 gdk_window_raise (priv->event_window);
2721 hildon_pannable_area_add (GtkContainer *container, GtkWidget *child)
2723 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (container)->priv;
2725 g_return_if_fail (gtk_bin_get_child (GTK_BIN (container)) == NULL);
2727 gtk_widget_set_parent (child, GTK_WIDGET (container));
2728 GTK_BIN (container)->child = child;
2730 g_signal_connect_after (child, "map-event",
2731 G_CALLBACK (hildon_pannable_area_child_mapped),
2734 if (!gtk_widget_set_scroll_adjustments (child, priv->hadjust, priv->vadjust)) {
2735 g_warning ("%s: cannot add non scrollable widget, "
2736 "wrap it in a viewport", __FUNCTION__);
2741 hildon_pannable_area_remove (GtkContainer *container, GtkWidget *child)
2743 g_return_if_fail (HILDON_IS_PANNABLE_AREA (container));
2744 g_return_if_fail (child != NULL);
2745 g_return_if_fail (gtk_bin_get_child (GTK_BIN (container)) == child);
2747 gtk_widget_set_scroll_adjustments (child, NULL, NULL);
2749 g_signal_handlers_disconnect_by_func (child,
2750 hildon_pannable_area_child_mapped,
2753 /* chain parent class handler to remove child */
2754 GTK_CONTAINER_CLASS (hildon_pannable_area_parent_class)->remove (container, child);
2758 * This method calculates a factor necessary to determine the initial distance
2759 * to jump in hildon_pannable_area_scroll_to(). For fixed time and frames per
2760 * second, we know in how many frames 'n' we need to reach the destination
2761 * point. We know that, for a distance d,
2763 * d = d_0 + d_1 + ... + d_n
2765 * where d_i is the distance travelled in the i-th frame and decel_factor is
2766 * the deceleration factor. This can be rewritten as
2768 * d = d_0 + (d_0 * decel_factor) + ... + (d_n-1 * decel_factor),
2770 * since the distance travelled on each frame is the distance travelled in the
2771 * previous frame reduced by the deceleration factor. Reducing this and
2772 * factoring d_0 out, we get
2774 * d = d_0 (1 + decel_factor + ... + decel_factor^(n-1)).
2776 * Since the sum is independent of the distance to be travelled, we can define
2779 * vel_factor = 1 + decel_factor + ... + decel_factor^(n-1).
2781 * That's the gem we calculate in this method.
2784 hildon_pannable_calculate_vel_factor (HildonPannableArea * self)
2786 HildonPannableAreaPrivate *priv = self->priv;
2791 n = ceil (priv->sps * priv->scroll_time);
2793 for (i = 1; i < n && fct_i >= RATIO_TOLERANCE; i++) {
2794 fct_i *= priv->decel;
2798 priv->vel_factor = fct;
2802 * hildon_pannable_area_new:
2804 * Create a new pannable area widget
2806 * Returns: the newly created #HildonPannableArea
2812 hildon_pannable_area_new (void)
2814 return g_object_new (HILDON_TYPE_PANNABLE_AREA, NULL);
2818 * hildon_pannable_area_new_full:
2819 * @mode: #HildonPannableAreaMode
2820 * @enabled: Value for the enabled property
2821 * @vel_min: Value for the velocity-min property
2822 * @vel_max: Value for the velocity-max property
2823 * @decel: Value for the deceleration property
2824 * @sps: Value for the sps property
2826 * Create a new #HildonPannableArea widget and set various properties
2828 * returns: the newly create #HildonPannableArea
2834 hildon_pannable_area_new_full (gint mode, gboolean enabled,
2835 gdouble vel_min, gdouble vel_max,
2836 gdouble decel, guint sps)
2838 return g_object_new (HILDON_TYPE_PANNABLE_AREA,
2841 "velocity_min", vel_min,
2842 "velocity_max", vel_max,
2843 "deceleration", decel, "sps", sps, NULL);
2847 * hildon_pannable_area_add_with_viewport:
2848 * @area: A #HildonPannableArea
2849 * @child: Child widget to add to the viewport
2851 * Convenience function used to add a child to a #GtkViewport, and add the
2852 * viewport to the scrolled window.
2858 hildon_pannable_area_add_with_viewport (HildonPannableArea * area,
2862 GtkWidget *viewport;
2864 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
2865 g_return_if_fail (GTK_IS_WIDGET (child));
2866 g_return_if_fail (child->parent == NULL);
2868 bin = GTK_BIN (area);
2870 if (bin->child != NULL)
2872 g_return_if_fail (GTK_IS_VIEWPORT (bin->child));
2873 g_return_if_fail (GTK_BIN (bin->child)->child == NULL);
2875 viewport = bin->child;
2879 HildonPannableAreaPrivate *priv = area->priv;
2881 viewport = gtk_viewport_new (priv->hadjust,
2883 gtk_viewport_set_shadow_type (GTK_VIEWPORT (viewport), GTK_SHADOW_NONE);
2884 gtk_container_add (GTK_CONTAINER (area), viewport);
2887 gtk_widget_show (viewport);
2888 gtk_container_add (GTK_CONTAINER (viewport), child);
2892 * hildon_pannable_area_scroll_to:
2893 * @area: A #HildonPannableArea.
2894 * @x: The x coordinate of the destination point or -1 to ignore this axis.
2895 * @y: The y coordinate of the destination point or -1 to ignore this axis.
2897 * Smoothly scrolls @area to ensure that (@x, @y) is a visible point
2898 * on the widget. To move in only one coordinate, you must set the other one
2899 * to -1. Notice that, in %HILDON_PANNABLE_AREA_MODE_PUSH mode, this function
2900 * works just like hildon_pannable_area_jump_to().
2902 * This function is useful if you need to present the user with a particular
2903 * element inside a scrollable widget, like #GtkTreeView. For instance,
2904 * the following example shows how to scroll inside a #GtkTreeView to
2905 * make visible an item, indicated by the #GtkTreeIter @iter.
2909 * GtkTreePath *path;
2910 * GdkRectangle *rect;
2912 * path = gtk_tree_model_get_path (model, &iter);
2913 * gtk_tree_view_get_background_area (GTK_TREE_VIEW (treeview),
2914 * path, NULL, &rect);
2915 * gtk_tree_view_convert_bin_window_to_tree_coords (GTK_TREE_VIEW (treeview),
2916 * 0, rect.y, NULL, &y);
2917 * hildon_pannable_area_scroll_to (panarea, -1, y);
2918 * gtk_tree_path_free (path);
2922 * If you want to present a child widget in simpler scenarios,
2923 * use hildon_pannable_area_scroll_to_child() instead.
2925 * There is a precondition to this function: the widget must be
2926 * already realized. Check the hildon_pannable_area_jump_to_child() for
2927 * more tips regarding how to call this function during
2933 hildon_pannable_area_scroll_to (HildonPannableArea *area,
2934 const gint x, const gint y)
2936 HildonPannableAreaPrivate *priv;
2938 gint dist_x, dist_y;
2939 gboolean hscroll_visible, vscroll_visible;
2941 g_return_if_fail (GTK_WIDGET_REALIZED (area));
2942 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
2946 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
2947 priv->vadjust->page_size);
2948 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
2949 priv->hadjust->page_size);
2951 if (((!vscroll_visible)&&(!hscroll_visible)) ||
2952 (x == -1 && y == -1)) {
2956 if (priv->mode == HILDON_PANNABLE_AREA_MODE_PUSH)
2957 hildon_pannable_area_jump_to (area, x, y);
2959 width = priv->hadjust->upper - priv->hadjust->lower;
2960 height = priv->vadjust->upper - priv->vadjust->lower;
2962 g_return_if_fail (x < width || y < height);
2964 if ((x > -1)&&(hscroll_visible)) {
2965 priv->scroll_to_x = x - priv->hadjust->page_size/2;
2966 dist_x = priv->scroll_to_x - priv->hadjust->value;
2968 priv->scroll_to_x = -1;
2970 priv->vel_x = - dist_x/priv->vel_factor;
2973 priv->scroll_to_x = -1;
2976 if ((y > -1)&&(vscroll_visible)) {
2977 priv->scroll_to_y = y - priv->vadjust->page_size/2;
2978 dist_y = priv->scroll_to_y - priv->vadjust->value;
2980 priv->scroll_to_y = -1;
2982 priv->vel_y = - dist_y/priv->vel_factor;
2985 priv->scroll_to_y = y;
2988 if ((priv->scroll_to_y == -1) && (priv->scroll_to_y == -1)) {
2992 hildon_pannable_area_launch_fade_timeout (area, 1.0);
2995 priv->idle_id = gdk_threads_add_timeout ((gint) (1000.0 / (gdouble) priv->sps),
2997 hildon_pannable_area_timeout, area);
3001 * hildon_pannable_area_jump_to:
3002 * @area: A #HildonPannableArea.
3003 * @x: The x coordinate of the destination point or -1 to ignore this axis.
3004 * @y: The y coordinate of the destination point or -1 to ignore this axis.
3006 * Jumps the position of @area to ensure that (@x, @y) is a visible
3007 * point in the widget. In order to move in only one coordinate, you
3008 * must set the other one to -1. See hildon_pannable_area_scroll_to()
3009 * function for an example of how to calculate the position of
3010 * children in scrollable widgets like #GtkTreeview.
3012 * There is a precondition to this function: the widget must be
3013 * already realized. Check the hildon_pannable_area_jump_to_child() for
3014 * more tips regarding how to call this function during
3020 hildon_pannable_area_jump_to (HildonPannableArea *area,
3021 const gint x, const gint y)
3023 HildonPannableAreaPrivate *priv;
3026 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3027 g_return_if_fail (GTK_WIDGET_REALIZED (area));
3028 g_return_if_fail (x >= -1 && y >= -1);
3030 if (x == -1 && y == -1) {
3036 width = priv->hadjust->upper - priv->hadjust->lower;
3037 height = priv->vadjust->upper - priv->vadjust->lower;
3039 g_return_if_fail (x < width || y < height);
3042 gdouble jump_to = x - priv->hadjust->page_size/2;
3044 if (jump_to > priv->hadjust->upper - priv->hadjust->page_size) {
3045 jump_to = priv->hadjust->upper - priv->hadjust->page_size;
3048 gtk_adjustment_set_value (priv->hadjust, jump_to);
3052 gdouble jump_to = y - priv->vadjust->page_size/2;
3054 if (jump_to > priv->vadjust->upper - priv->vadjust->page_size) {
3055 jump_to = priv->vadjust->upper - priv->vadjust->page_size;
3058 gtk_adjustment_set_value (priv->vadjust, jump_to);
3061 priv->scroll_indicator_alpha = 1.0;
3063 if (priv->scroll_indicator_timeout) {
3064 g_source_remove (priv->scroll_indicator_timeout);
3065 priv->scroll_indicator_timeout = 0;
3068 if (priv->idle_id) {
3071 priv->overshooting_x = 0;
3072 priv->overshooting_y = 0;
3074 if ((priv->overshot_dist_x>0)||(priv->overshot_dist_y>0)) {
3075 priv->overshot_dist_x = 0;
3076 priv->overshot_dist_y = 0;
3078 gtk_widget_queue_resize (GTK_WIDGET (area));
3081 g_source_remove (priv->idle_id);
3087 * hildon_pannable_area_scroll_to_child:
3088 * @area: A #HildonPannableArea.
3089 * @child: A #GtkWidget, descendant of @area.
3091 * Smoothly scrolls until @child is visible inside @area. @child must
3092 * be a descendant of @area. If you need to scroll inside a scrollable
3093 * widget, e.g., #GtkTreeview, see hildon_pannable_area_scroll_to().
3095 * There is a precondition to this function: the widget must be
3096 * already realized. Check the hildon_pannable_area_jump_to_child() for
3097 * more tips regarding how to call this function during
3103 hildon_pannable_area_scroll_to_child (HildonPannableArea *area, GtkWidget *child)
3105 GtkWidget *bin_child;
3108 g_return_if_fail (GTK_WIDGET_REALIZED (area));
3109 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3110 g_return_if_fail (GTK_IS_WIDGET (child));
3111 g_return_if_fail (gtk_widget_is_ancestor (child, GTK_WIDGET (area)));
3113 if (GTK_BIN (area)->child == NULL)
3116 /* We need to get to check the child of the inside the area */
3117 bin_child = GTK_BIN (area)->child;
3119 /* we check if we added a viewport */
3120 if (GTK_IS_VIEWPORT (bin_child)) {
3121 bin_child = GTK_BIN (bin_child)->child;
3124 if (gtk_widget_translate_coordinates (child, bin_child, 0, 0, &x, &y))
3125 hildon_pannable_area_scroll_to (area, x, y);
3129 * hildon_pannable_area_jump_to_child:
3130 * @area: A #HildonPannableArea.
3131 * @child: A #GtkWidget, descendant of @area.
3133 * Jumps to make sure @child is visible inside @area. @child must
3134 * be a descendant of @area. If you want to move inside a scrollable
3135 * widget, like, #GtkTreeview, see hildon_pannable_area_scroll_to().
3137 * There is a precondition to this function: the widget must be
3138 * already realized. You can control if the widget is ready with the
3139 * GTK_WIDGET_REALIZED macro. If you want to call this function during
3140 * the initialization process of the widget do it inside a callback to
3141 * the ::realize signal, using g_signal_connect_after() function.
3146 hildon_pannable_area_jump_to_child (HildonPannableArea *area, GtkWidget *child)
3148 GtkWidget *bin_child;
3151 g_return_if_fail (GTK_WIDGET_REALIZED (area));
3152 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3153 g_return_if_fail (GTK_IS_WIDGET (child));
3154 g_return_if_fail (gtk_widget_is_ancestor (child, GTK_WIDGET (area)));
3156 if (gtk_bin_get_child (GTK_BIN (area)) == NULL)
3159 /* We need to get to check the child of the inside the area */
3160 bin_child = gtk_bin_get_child (GTK_BIN (area));
3162 /* we check if we added a viewport */
3163 if (GTK_IS_VIEWPORT (bin_child)) {
3164 bin_child = gtk_bin_get_child (GTK_BIN (bin_child));
3167 if (gtk_widget_translate_coordinates (child, bin_child, 0, 0, &x, &y))
3168 hildon_pannable_area_jump_to (area, x, y);
3172 * hildon_pannable_get_child_widget_at:
3173 * @area: A #HildonPannableArea.
3174 * @x: horizontal coordinate of the point
3175 * @y: vertical coordinate of the point
3177 * Get the widget at the point (x, y) inside the pannable area. In
3178 * case no widget found it returns NULL.
3180 * returns: the #GtkWidget if we find a widget, NULL in any other case
3185 hildon_pannable_get_child_widget_at (HildonPannableArea *area,
3186 gdouble x, gdouble y)
3188 GdkWindow *window = NULL;
3189 GtkWidget *child_widget = NULL;
3191 window = hildon_pannable_area_get_topmost
3192 (gtk_bin_get_child (GTK_BIN (area))->window,
3193 x, y, NULL, NULL, GDK_ALL_EVENTS_MASK);
3195 gdk_window_get_user_data (window, (gpointer) &child_widget);
3197 return child_widget;
3202 * hildon_pannable_area_get_hadjustment:
3203 * @area: A #HildonPannableArea.
3205 * Returns the horizontal adjustment. This adjustment is the internal
3206 * widget adjustment used to control the animations. Do not modify it
3207 * directly to change the position of the pannable, to do that use the
3208 * pannable API. If you modify the object directly it could cause
3209 * artifacts in the animations.
3211 * returns: The horizontal #GtkAdjustment
3216 hildon_pannable_area_get_hadjustment (HildonPannableArea *area)
3219 g_return_val_if_fail (HILDON_IS_PANNABLE_AREA (area), NULL);
3221 return area->priv->hadjust;
3225 * hildon_pannable_area_get_vadjustment:
3226 * @area: A #HildonPannableArea.
3228 * Returns the vertical adjustment. This adjustment is the internal
3229 * widget adjustment used to control the animations. Do not modify it
3230 * directly to change the position of the pannable, to do that use the
3231 * pannable API. If you modify the object directly it could cause
3232 * artifacts in the animations.
3234 * returns: The vertical #GtkAdjustment
3239 hildon_pannable_area_get_vadjustment (HildonPannableArea *area)
3241 g_return_val_if_fail (HILDON_IS_PANNABLE_AREA (area), NULL);
3243 return area->priv->vadjust;
3248 * hildon_pannable_area_get_size_request_policy:
3249 * @area: A #HildonPannableArea.
3251 * This function returns the current size request policy of the
3252 * widget. That policy controls the way the size_request is done in
3253 * the pannable area. Check
3254 * hildon_pannable_area_set_size_request_policy() for a more detailed
3257 * returns: the policy is currently being used in the widget
3258 * #HildonSizeRequestPolicy.
3262 HildonSizeRequestPolicy
3263 hildon_pannable_area_get_size_request_policy (HildonPannableArea *area)
3265 HildonPannableAreaPrivate *priv;
3267 g_return_val_if_fail (HILDON_IS_PANNABLE_AREA (area), FALSE);
3271 return priv->size_request_policy;
3275 * hildon_pannable_area_set_size_request_policy:
3276 * @area: A #HildonPannableArea.
3277 * @size_request_policy: One of the allowed #HildonSizeRequestPolicy
3279 * This function sets the pannable area size request policy. That
3280 * policy controls the way the size_request is done in the pannable
3281 * area. Pannable can use the size request of its children
3282 * (#HILDON_SIZE_REQUEST_CHILDREN) or the minimum size required for
3283 * the area itself (#HILDON_SIZE_REQUEST_MINIMUM), the latter is the
3284 * default. Recall this size depends on the scrolling policy you are
3285 * requesting to the pannable area, if you set #GTK_POLICY_NEVER this
3286 * parameter will not have any effect with
3287 * #HILDON_SIZE_REQUEST_MINIMUM set.
3291 * Deprecated: This method and the policy request is deprecated, DO
3292 * NOT use it in future code, the only policy properly supported in
3293 * gtk+ nowadays is the minimum size. Use #gtk_window_set_default_size
3294 * or #gtk_window_set_geometry_hints with the proper size in your case
3295 * to define the height of your dialogs.
3298 hildon_pannable_area_set_size_request_policy (HildonPannableArea *area,
3299 HildonSizeRequestPolicy size_request_policy)
3301 HildonPannableAreaPrivate *priv;
3303 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3307 if (priv->size_request_policy == size_request_policy)
3310 priv->size_request_policy = size_request_policy;
3312 gtk_widget_queue_resize (GTK_WIDGET (area));
3314 g_object_notify (G_OBJECT (area), "size-request-policy");