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->initial_hint) {
1489 if (priv->vscroll_visible || priv->hscroll_visible) {
1491 priv->scroll_indicator_event_interrupt = 0;
1492 priv->scroll_delay_counter = priv->scrollbar_fade_delay;
1494 hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget), 1.0);
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_effect)) {
1626 hildon_pannable_area_initial_effect (widget);
1628 priv->initial_effect = FALSE;
1631 if (gtk_bin_get_child (GTK_BIN (widget))) {
1633 if (priv->scroll_indicator_alpha > 0.1) {
1634 if (priv->vscroll_visible) {
1635 hildon_pannable_draw_vscroll (widget, &back_color, &scroll_color);
1637 if (priv->hscroll_visible) {
1638 hildon_pannable_draw_hscroll (widget, &back_color, &scroll_color);
1642 /* draw overshooting rectangles */
1643 if (priv->overshot_dist_y > 0) {
1644 gint overshot_height;
1646 overshot_height = MIN (priv->overshot_dist_y, widget->allocation.height -
1647 (priv->hscroll_visible ? priv->hscroll_rect.height : 0));
1649 gdk_draw_rectangle (widget->window,
1650 widget->style->bg_gc[GTK_STATE_NORMAL],
1654 widget->allocation.width -
1655 (priv->vscroll_visible ? priv->vscroll_rect.width : 0),
1657 } else if (priv->overshot_dist_y < 0) {
1658 gint overshot_height;
1662 MAX (priv->overshot_dist_y,
1663 -(widget->allocation.height -
1664 (priv->hscroll_visible ? priv->hscroll_rect.height : 0)));
1666 overshot_y = MAX (widget->allocation.height +
1668 (priv->hscroll_visible ? priv->hscroll_rect.height : 0), 0);
1670 gdk_draw_rectangle (widget->window,
1671 widget->style->bg_gc[GTK_STATE_NORMAL],
1675 widget->allocation.width -
1676 priv->vscroll_rect.width,
1680 if (priv->overshot_dist_x > 0) {
1681 gint overshot_width;
1683 overshot_width = MIN (priv->overshot_dist_x, widget->allocation.width -
1684 (priv->vscroll_visible ? priv->vscroll_rect.width : 0));
1686 gdk_draw_rectangle (widget->window,
1687 widget->style->bg_gc[GTK_STATE_NORMAL],
1692 widget->allocation.height -
1693 (priv->hscroll_visible ? priv->hscroll_rect.height : 0));
1694 } else if (priv->overshot_dist_x < 0) {
1695 gint overshot_width;
1699 MAX (priv->overshot_dist_x,
1700 -(widget->allocation.width -
1701 (priv->vscroll_visible ? priv->vscroll_rect.width : 0)));
1703 overshot_x = MAX (widget->allocation.width +
1705 (priv->vscroll_visible ? priv->vscroll_rect.width : 0), 0);
1707 gdk_draw_rectangle (widget->window,
1708 widget->style->bg_gc[GTK_STATE_NORMAL],
1713 widget->allocation.height -
1714 priv->hscroll_rect.height);
1719 return GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->expose_event (widget, event);
1723 hildon_pannable_area_get_topmost (GdkWindow * window,
1725 gint * tx, gint * ty,
1728 /* Find the GdkWindow at the given point, by recursing from a given
1729 * parent GdkWindow. Optionally return the co-ordinates transformed
1730 * relative to the child window.
1733 GList *c, *children;
1734 GdkWindow *selected_window = NULL;
1736 gdk_drawable_get_size (GDK_DRAWABLE (window), &width, &height);
1737 if ((x < 0) || (x >= width) || (y < 0) || (y >= height))
1740 children = gdk_window_peek_children (window);
1747 selected_window = window;
1750 for (c = children; c; c = c->next) {
1751 GdkWindow *child = (GdkWindow *) c->data;
1754 gdk_drawable_get_size (GDK_DRAWABLE (child), &width, &height);
1755 gdk_window_get_position (child, &wx, &wy);
1757 if ((x >= wx) && (x < (wx + width)) && (y >= wy) && (y < (wy + height)) &&
1758 (gdk_window_is_visible (child))) {
1760 if (gdk_window_peek_children (child)) {
1761 selected_window = hildon_pannable_area_get_topmost (child, x-wx, y-wy,
1763 if (!selected_window) {
1768 selected_window = child;
1771 if ((gdk_window_get_events (child)&mask)) {
1776 selected_window = child;
1782 return selected_window;
1786 synth_crossing (GdkWindow * child,
1788 gint x_root, gint y_root,
1789 guint32 time, gboolean in)
1791 GdkEventCrossing *crossing_event;
1792 GdkEventType type = in ? GDK_ENTER_NOTIFY : GDK_LEAVE_NOTIFY;
1794 /* Send synthetic enter event */
1795 crossing_event = (GdkEventCrossing *) gdk_event_new (type);
1796 ((GdkEventAny *) crossing_event)->type = type;
1797 ((GdkEventAny *) crossing_event)->window = g_object_ref (child);
1798 ((GdkEventAny *) crossing_event)->send_event = FALSE;
1799 crossing_event->subwindow = g_object_ref (child);
1800 crossing_event->time = time;
1801 crossing_event->x = x;
1802 crossing_event->y = y;
1803 crossing_event->x_root = x_root;
1804 crossing_event->y_root = y_root;
1805 crossing_event->mode = GDK_CROSSING_NORMAL;
1806 crossing_event->detail = GDK_NOTIFY_UNKNOWN;
1807 crossing_event->focus = FALSE;
1808 crossing_event->state = 0;
1809 gdk_event_put ((GdkEvent *) crossing_event);
1810 gdk_event_free ((GdkEvent *) crossing_event);
1814 hildon_pannable_area_button_press_cb (GtkWidget * widget,
1815 GdkEventButton * event)
1818 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1820 if ((!priv->enabled) || (event->button != 1) ||
1821 ((event->time == priv->last_time) &&
1822 (priv->last_type == 1)) || (gtk_bin_get_child (GTK_BIN (widget)) == NULL))
1825 priv->scroll_indicator_event_interrupt = 1;
1827 hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget),
1828 priv->scroll_indicator_alpha);
1830 priv->last_time = event->time;
1831 priv->last_type = 1;
1833 priv->scroll_to_x = -1;
1834 priv->scroll_to_y = -1;
1836 if (priv->button_pressed && priv->child) {
1837 /* Widget stole focus on last click, send crossing-out event */
1838 synth_crossing (priv->child, 0, 0, event->x_root, event->y_root,
1839 event->time, FALSE);
1847 /* Don't allow a click if we're still moving fast */
1848 if ((ABS (priv->vel_x) <= (priv->vmax * priv->vfast_factor)) &&
1849 (ABS (priv->vel_y) <= (priv->vmax * priv->vfast_factor)))
1851 hildon_pannable_area_get_topmost (gtk_bin_get_child (GTK_BIN (widget))->window,
1852 event->x, event->y, &x, &y, GDK_BUTTON_PRESS_MASK);
1856 priv->button_pressed = TRUE;
1858 /* Stop scrolling on mouse-down (so you can flick, then hold to stop) */
1864 gdk_drawable_get_size (priv->child, &priv->child_width,
1865 &priv->child_height);
1866 priv->last_in = TRUE;
1868 g_object_add_weak_pointer ((GObject *) priv->child,
1869 (gpointer) & priv->child);
1871 event = (GdkEventButton *) gdk_event_copy ((GdkEvent *) event);
1877 synth_crossing (priv->child, x, y, event->x_root,
1878 event->y_root, event->time, TRUE);
1880 /* Send synthetic click (button press/release) event */
1881 ((GdkEventAny *) event)->window = g_object_ref (priv->child);
1883 gdk_event_put ((GdkEvent *) event);
1884 gdk_event_free ((GdkEvent *) event);
1892 hildon_pannable_area_check_scrollbars (HildonPannableArea * area)
1894 HildonPannableAreaPrivate *priv = area->priv;
1895 gboolean prev_hscroll_visible, prev_vscroll_visible;
1897 prev_hscroll_visible = priv->hscroll_visible;
1898 prev_vscroll_visible = priv->vscroll_visible;
1900 if (!gtk_bin_get_child (GTK_BIN (area))) {
1901 priv->vscroll_visible = FALSE;
1902 priv->hscroll_visible = FALSE;
1904 switch (priv->hscrollbar_policy) {
1905 case GTK_POLICY_ALWAYS:
1906 priv->hscroll_visible = TRUE;
1908 case GTK_POLICY_NEVER:
1909 priv->hscroll_visible = FALSE;
1912 priv->hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
1913 priv->hadjust->page_size);
1916 switch (priv->vscrollbar_policy) {
1917 case GTK_POLICY_ALWAYS:
1918 priv->vscroll_visible = TRUE;
1920 case GTK_POLICY_NEVER:
1921 priv->vscroll_visible = FALSE;
1924 priv->vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
1925 priv->vadjust->page_size);
1928 /* Store the vscroll/hscroll areas for redrawing */
1929 if (priv->vscroll_visible) {
1930 GtkAllocation *allocation = >K_WIDGET (area)->allocation;
1931 priv->vscroll_rect.x = allocation->width - priv->indicator_width;
1932 priv->vscroll_rect.y = 0;
1933 priv->vscroll_rect.width = priv->indicator_width;
1934 priv->vscroll_rect.height = allocation->height -
1935 (priv->hscroll_visible ? priv->indicator_width : 0);
1937 if (priv->hscroll_visible) {
1938 GtkAllocation *allocation = >K_WIDGET (area)->allocation;
1939 priv->hscroll_rect.y = allocation->height - priv->indicator_width;
1940 priv->hscroll_rect.x = 0;
1941 priv->hscroll_rect.height = priv->indicator_width;
1942 priv->hscroll_rect.width = allocation->width -
1943 (priv->vscroll_visible ? priv->indicator_width : 0);
1947 return ((priv->hscroll_visible != prev_hscroll_visible) ||
1948 (priv->vscroll_visible != prev_vscroll_visible));
1952 hildon_pannable_area_refresh (HildonPannableArea * area)
1954 if (GTK_WIDGET_DRAWABLE (area) &&
1955 hildon_pannable_area_check_scrollbars (area)) {
1956 HildonPannableAreaPrivate *priv = area->priv;
1958 gtk_widget_queue_resize (GTK_WIDGET (area));
1960 if ((priv->vscroll_visible) || (priv->hscroll_visible)) {
1961 priv->scroll_indicator_event_interrupt = 0;
1962 priv->scroll_delay_counter = area->priv->scrollbar_fade_delay;
1964 hildon_pannable_area_launch_fade_timeout (area, 1.0);
1967 hildon_pannable_area_redraw (area);
1971 /* Scroll by a particular amount (in pixels). Optionally, return if
1972 * the scroll on a particular axis was successful.
1975 hildon_pannable_axis_scroll (HildonPannableArea *area,
1976 GtkAdjustment *adjust,
1980 gint *overshot_dist,
1986 HildonPannableAreaPrivate *priv = area->priv;
1988 dist = gtk_adjustment_get_value (adjust) - inc;
1991 * We use overshot_dist to define the distance of the current overshoot,
1992 * and overshooting to define the direction/whether or not we are overshot
1994 if (!(*overshooting)) {
1996 /* Initiation of the overshoot happens when the finger is released
1997 * and the current position of the pannable contents are out of range
1999 if (dist < adjust->lower) {
2002 dist = adjust->lower;
2004 if (overshoot_max!=0) {
2007 *overshot_dist = CLAMP (*overshot_dist + *vel, 0, overshoot_max);
2008 *vel = MIN (priv->vmax_overshooting, *vel);
2009 gtk_widget_queue_resize (GTK_WIDGET (area));
2013 } else if (dist > adjust->upper - adjust->page_size) {
2016 dist = adjust->upper - adjust->page_size;
2018 if (overshoot_max!=0) {
2021 *overshot_dist = CLAMP (*overshot_dist + *vel, -overshoot_max, 0);
2022 *vel = MAX (-priv->vmax_overshooting, *vel);
2023 gtk_widget_queue_resize (GTK_WIDGET (area));
2028 if ((*scroll_to) != -1) {
2029 if (((inc < 0)&&(*scroll_to <= dist))||
2030 ((inc > 0)&&(*scroll_to >= dist))) {
2038 gtk_adjustment_set_value (adjust, dist);
2040 if (!priv->button_pressed) {
2042 /* When the overshoot has started we continue for
2043 * PROP_BOUNCE_STEPS more steps into the overshoot before we
2044 * reverse direction. The deceleration factor is calculated
2045 * based on the percentage distance from the first item with
2046 * each iteration, therefore always returning us to the
2047 * top/bottom most element
2049 if (*overshot_dist > 0) {
2051 if ((*overshooting < priv->bounce_steps) && (*vel > 0)) {
2053 *vel = (((gdouble)*overshot_dist)/overshoot_max) * (*vel);
2054 } else if ((*overshooting >= priv->bounce_steps) && (*vel > 0)) {
2056 } else if ((*overshooting > 1) && (*vel < 0)) {
2057 /* we add the MIN in order to avoid very small speeds */
2058 *vel = MIN ((((gdouble)*overshot_dist)*0.4) * -1, -2.0);
2061 *overshot_dist = CLAMP (*overshot_dist + *vel, 0, overshoot_max);
2063 gtk_widget_queue_resize (GTK_WIDGET (area));
2065 } else if (*overshot_dist < 0) {
2067 if ((*overshooting < priv->bounce_steps) && (*vel < 0)) {
2069 *vel = (((gdouble)*overshot_dist)/overshoot_max) * (*vel) * -1;
2070 } else if ((*overshooting >= priv->bounce_steps) && (*vel < 0)) {
2072 } else if ((*overshooting > 1) && (*vel > 0)) {
2073 /* we add the MAX in order to avoid very small speeds */
2074 *vel = MAX ((((gdouble)*overshot_dist)*0.4) * -1, 2.0);
2077 *overshot_dist = CLAMP (*overshot_dist + (*vel), -overshoot_max, 0);
2079 gtk_widget_queue_resize (GTK_WIDGET (area));
2084 gtk_widget_queue_resize (GTK_WIDGET (area));
2088 gint overshot_dist_old = *overshot_dist;
2090 if (*overshot_dist > 0) {
2091 *overshot_dist = CLAMP ((*overshot_dist) + inc, 0, overshoot_max);
2092 } else if (*overshot_dist < 0) {
2093 *overshot_dist = CLAMP ((*overshot_dist) + inc, -1 * overshoot_max, 0);
2096 gtk_adjustment_set_value (adjust, dist);
2099 if (*overshot_dist != overshot_dist_old)
2100 gtk_widget_queue_resize (GTK_WIDGET (area));
2106 hildon_pannable_area_scroll (HildonPannableArea *area,
2107 gdouble x, gdouble y)
2110 HildonPannableAreaPrivate *priv = area->priv;
2111 gboolean hscroll_visible, vscroll_visible;
2113 if (gtk_bin_get_child (GTK_BIN (area)) == NULL)
2116 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
2117 priv->vadjust->page_size);
2118 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
2119 priv->hadjust->page_size);
2124 if (vscroll_visible) {
2125 hildon_pannable_axis_scroll (area, priv->vadjust, &priv->vel_y, y,
2126 &priv->overshooting_y, &priv->overshot_dist_y,
2127 &priv->scroll_to_y, priv->vovershoot_max, &sy);
2132 if (hscroll_visible) {
2133 hildon_pannable_axis_scroll (area, priv->hadjust, &priv->vel_x, x,
2134 &priv->overshooting_x, &priv->overshot_dist_x,
2135 &priv->scroll_to_x, priv->hovershoot_max, &sx);
2140 /* If the scroll on a particular axis wasn't succesful, reset the
2141 * initial scroll position to the new mouse co-ordinate. This means
2142 * when you get to the top of the page, dragging down works immediately.
2144 if (priv->mode == HILDON_PANNABLE_AREA_MODE_ACCEL) {
2156 hildon_pannable_area_timeout (HildonPannableArea * area)
2158 HildonPannableAreaPrivate *priv = area->priv;
2160 if ((!priv->enabled) || (priv->mode == HILDON_PANNABLE_AREA_MODE_PUSH)) {
2166 if (!priv->button_pressed) {
2167 /* Decelerate gradually when pointer is raised */
2168 if ((!priv->overshot_dist_y) &&
2169 (!priv->overshot_dist_x)) {
2171 /* in case we move to a specific point do not decelerate when arriving */
2172 if ((priv->scroll_to_x != -1)||(priv->scroll_to_y != -1)) {
2174 if (ABS (priv->vel_x) >= 1.5) {
2175 priv->vel_x *= priv->decel;
2178 if (ABS (priv->vel_y) >= 1.5) {
2179 priv->vel_y *= priv->decel;
2183 if ((!priv->low_friction_mode) ||
2184 ((priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ) &&
2185 (ABS (priv->vel_x) < 0.8*priv->vmax)))
2186 priv->vel_x *= priv->decel;
2188 if ((!priv->low_friction_mode) ||
2189 ((priv->mov_mode&HILDON_MOVEMENT_MODE_VERT) &&
2190 (ABS (priv->vel_y) < 0.8*priv->vmax)))
2191 priv->vel_y *= priv->decel;
2193 if ((ABS (priv->vel_x) < 1.0) && (ABS (priv->vel_y) < 1.0)) {
2202 } else if (priv->mode == HILDON_PANNABLE_AREA_MODE_AUTO) {
2208 hildon_pannable_area_scroll (area, priv->vel_x, priv->vel_y);
2214 hildon_pannable_area_calculate_velocity (gdouble *vel,
2218 gdouble drag_inertia,
2224 if (ABS (dist) >= RATIO_TOLERANCE) {
2225 rawvel = (dist / ABS (delta)) * force;
2226 *vel = *vel * (1 - drag_inertia) +
2227 rawvel * drag_inertia;
2228 *vel = *vel > 0 ? MIN (*vel, vmax)
2229 : MAX (*vel, -1 * vmax);
2234 hildon_pannable_area_motion_event_scroll_timeout (HildonPannableArea *area)
2236 HildonPannableAreaPrivate *priv = area->priv;
2238 if ((priv->motion_x != 0)||(priv->motion_y != 0))
2239 hildon_pannable_area_scroll (area, priv->motion_x, priv->motion_y);
2241 priv->motion_event_scroll_timeout = 0;
2247 hildon_pannable_area_motion_event_scroll (HildonPannableArea *area,
2248 gdouble x, gdouble y)
2250 HildonPannableAreaPrivate *priv = area->priv;
2252 if (priv->motion_event_scroll_timeout) {
2254 priv->motion_x += x;
2255 priv->motion_y += y;
2259 /* we do not delay the first event but the next ones */
2260 hildon_pannable_area_scroll (area, x, y);
2265 priv->motion_event_scroll_timeout = gdk_threads_add_timeout
2266 ((gint) (1000.0 / (gdouble) MOTION_EVENTS_PER_SECOND),
2267 (GSourceFunc) hildon_pannable_area_motion_event_scroll_timeout, area);
2272 hildon_pannable_area_motion_notify_cb (GtkWidget * widget,
2273 GdkEventMotion * event)
2275 HildonPannableArea *area = HILDON_PANNABLE_AREA (widget);
2276 HildonPannableAreaPrivate *priv = area->priv;
2280 if (gtk_bin_get_child (GTK_BIN (widget)) == NULL)
2283 if ((!priv->enabled) || (!priv->button_pressed) ||
2284 ((event->time == priv->last_time) && (priv->last_type == 2))) {
2285 gdk_window_get_pointer (widget->window, NULL, NULL, 0);
2289 if (priv->last_type == 1) {
2290 priv->first_drag = TRUE;
2293 x = event->x - priv->x;
2294 y = event->y - priv->y;
2296 if (priv->first_drag && (!priv->moved) &&
2297 ((ABS (x) > (priv->panning_threshold))
2298 || (ABS (y) > (priv->panning_threshold)))) {
2303 if (priv->first_drag) {
2304 gboolean vscroll_visible;
2305 gboolean hscroll_visible;
2307 if (ABS (priv->iy - event->y) >=
2308 ABS (priv->ix - event->x)) {
2310 g_signal_emit (area,
2311 pannable_area_signals[VERTICAL_MOVEMENT],
2312 0, (priv->iy > event->y) ?
2313 HILDON_MOVEMENT_UP :
2314 HILDON_MOVEMENT_DOWN,
2315 (gdouble)priv->ix, (gdouble)priv->iy);
2317 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
2318 priv->vadjust->page_size);
2320 if (!((vscroll_visible)&&
2321 (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT))) {
2323 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
2324 priv->hadjust->page_size);
2326 /* even in case we do not have to move we check if this
2327 could be a fake horizontal movement */
2328 if (!((hscroll_visible)&&
2329 (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ)) ||
2330 (ABS (priv->iy - event->y) -
2331 ABS (priv->ix - event->x) >= priv->direction_error_margin))
2332 priv->moved = FALSE;
2336 g_signal_emit (area,
2337 pannable_area_signals[HORIZONTAL_MOVEMENT],
2338 0, (priv->ix > event->x) ?
2339 HILDON_MOVEMENT_LEFT :
2340 HILDON_MOVEMENT_RIGHT,
2341 (gdouble)priv->ix, (gdouble)priv->iy);
2343 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
2344 priv->hadjust->page_size);
2346 if (!((hscroll_visible)&&
2347 (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ))) {
2349 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
2350 priv->vadjust->page_size);
2352 /* even in case we do not have to move we check if this
2353 could be a fake vertical movement */
2354 if (!((vscroll_visible) &&
2355 (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT)) ||
2356 (ABS (priv->ix - event->x) -
2357 ABS (priv->iy - event->y) >= priv->direction_error_margin))
2358 priv->moved = FALSE;
2362 if ((priv->moved)&&(priv->child)) {
2365 pos_x = priv->cx + (event->x - priv->ix);
2366 pos_y = priv->cy + (event->y - priv->iy);
2368 synth_crossing (priv->child, pos_x, pos_y, event->x_root,
2369 event->y_root, event->time, FALSE);
2373 priv->first_drag = FALSE;
2375 if ((priv->mode != HILDON_PANNABLE_AREA_MODE_PUSH) &&
2376 (priv->mode != HILDON_PANNABLE_AREA_MODE_AUTO)) {
2379 priv->idle_id = gdk_threads_add_timeout ((gint)
2380 (1000.0 / (gdouble) priv->sps),
2382 hildon_pannable_area_timeout, area);
2387 switch (priv->mode) {
2388 case HILDON_PANNABLE_AREA_MODE_PUSH:
2389 /* Scroll by the amount of pixels the cursor has moved
2390 * since the last motion event.
2392 hildon_pannable_area_motion_event_scroll (area, x, y);
2396 case HILDON_PANNABLE_AREA_MODE_ACCEL:
2397 /* Set acceleration relative to the initial click */
2398 priv->ex = event->x;
2399 priv->ey = event->y;
2400 priv->vel_x = ((x > 0) ? 1 : -1) *
2402 (gdouble) widget->allocation.width) *
2403 (priv->vmax - priv->vmin)) + priv->vmin);
2404 priv->vel_y = ((y > 0) ? 1 : -1) *
2406 (gdouble) widget->allocation.height) *
2407 (priv->vmax - priv->vmin)) + priv->vmin);
2409 case HILDON_PANNABLE_AREA_MODE_AUTO:
2411 delta = event->time - priv->last_time;
2413 if (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT) {
2414 gdouble dist = event->y - priv->y;
2416 hildon_pannable_area_calculate_velocity (&priv->vel_y,
2428 if (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ) {
2429 gdouble dist = event->x - priv->x;
2431 hildon_pannable_area_calculate_velocity (&priv->vel_x,
2443 hildon_pannable_area_motion_event_scroll (area, x, y);
2445 if (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ)
2447 if (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT)
2455 } else if (priv->child) {
2459 pos_x = priv->cx + (event->x - priv->ix);
2460 pos_y = priv->cy + (event->y - priv->iy);
2462 in = (((0 <= pos_x)&&(priv->child_width >= pos_x)) &&
2463 ((0 <= pos_y)&&(priv->child_height >= pos_y)));
2465 if (((!priv->last_in)&&in)||((priv->last_in)&&(!in))) {
2467 synth_crossing (priv->child, pos_x, pos_y, event->x_root,
2468 event->y_root, event->time, in);
2474 priv->last_time = event->time;
2475 priv->last_type = 2;
2478 /* Send motion notify to child */
2479 event = (GdkEventMotion *) gdk_event_copy ((GdkEvent *) event);
2480 event->x = priv->cx + (event->x - priv->ix);
2481 event->y = priv->cy + (event->y - priv->iy);
2482 event->window = g_object_ref (priv->child);
2483 gdk_event_put ((GdkEvent *) event);
2484 gdk_event_free ((GdkEvent *) event);
2487 gdk_window_get_pointer (widget->window, NULL, NULL, 0);
2493 hildon_pannable_leave_notify_event (GtkWidget *widget,
2494 GdkEventCrossing *event)
2496 HildonPannableArea *area = HILDON_PANNABLE_AREA (widget);
2497 HildonPannableAreaPrivate *priv = area->priv;
2499 if ((priv->child)&&(priv->last_in)) {
2500 priv->last_in = FALSE;
2502 synth_crossing (priv->child, 0, 0, event->x_root,
2503 event->y_root, event->time, FALSE);
2510 hildon_pannable_area_button_release_cb (GtkWidget * widget,
2511 GdkEventButton * event)
2513 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
2517 if (((event->time == priv->last_time) && (priv->last_type == 3))
2518 || (gtk_bin_get_child (GTK_BIN (widget)) == NULL)
2519 || (!priv->button_pressed) || (!priv->enabled) || (event->button != 1))
2522 priv->scroll_indicator_event_interrupt = 0;
2523 priv->scroll_delay_counter = priv->scrollbar_fade_delay;
2525 /* move all the way to the last position */
2526 if (priv->motion_event_scroll_timeout) {
2527 g_source_remove (priv->motion_event_scroll_timeout);
2528 hildon_pannable_area_motion_event_scroll_timeout (HILDON_PANNABLE_AREA (widget));
2533 if (priv->last_type == 2) {
2534 gdouble delta = event->time - priv->last_time;
2536 if (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT) {
2537 gdouble dist = event->y - priv->y;
2539 if (ABS (dist) >= 1.0) {
2540 hildon_pannable_area_calculate_velocity (&priv->vel_y,
2548 priv->motion_y = dist;
2549 hildon_pannable_area_motion_event_scroll_timeout (HILDON_PANNABLE_AREA (widget));
2551 if (delta >= CURSOR_STOPPED_TIMEOUT) {
2557 if (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ) {
2558 gdouble dist = event->x - priv->x;
2560 if (ABS (dist) >= 1.0) {
2561 hildon_pannable_area_calculate_velocity (&priv->vel_x,
2568 priv->motion_x = dist;
2569 hildon_pannable_area_motion_event_scroll_timeout (HILDON_PANNABLE_AREA (widget));
2571 if (delta >= CURSOR_STOPPED_TIMEOUT) {
2578 if ((ABS (priv->vel_y) > priv->vmin)||
2579 (ABS (priv->vel_x) > priv->vmin)) {
2580 priv->scroll_indicator_alpha = 1.0;
2583 hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget),
2584 priv->scroll_indicator_alpha);
2586 priv->button_pressed = FALSE;
2588 if (priv->mode == HILDON_PANNABLE_AREA_MODE_AUTO ||
2589 priv->mode == HILDON_PANNABLE_AREA_MODE_ACCEL) {
2591 /* If overshoot has been initiated with a finger down, on release set max speed */
2592 if (priv->overshot_dist_y != 0) {
2593 priv->overshooting_y = priv->bounce_steps; /* Hack to stop a bounce in the finger down case */
2594 priv->vel_y = priv->vmax_overshooting;
2597 if (priv->overshot_dist_x != 0) {
2598 priv->overshooting_x = priv->bounce_steps; /* Hack to stop a bounce in the finger down case */
2599 priv->vel_x = priv->vmax_overshooting;
2602 if ((ABS (priv->vel_y) >= priv->vmin) ||
2603 (ABS (priv->vel_x) >= priv->vmin)) {
2605 if (ABS (priv->vel_x) > MAX_SPEED_THRESHOLD)
2606 priv->vel_x = (priv->vel_x > 0) ? priv->vmax : -priv->vmax;
2608 if (ABS (priv->vel_y) > MAX_SPEED_THRESHOLD)
2609 priv->vel_y = (priv->vel_y > 0) ? priv->vmax : -priv->vmax;
2612 priv->idle_id = gdk_threads_add_timeout ((gint) (1000.0 / (gdouble) priv->sps),
2614 hildon_pannable_area_timeout, widget);
2618 priv->last_time = event->time;
2619 priv->last_type = 3;
2622 priv->moved = FALSE;
2627 hildon_pannable_area_get_topmost (gtk_bin_get_child (GTK_BIN (widget))->window,
2628 event->x, event->y, &x, &y, GDK_BUTTON_RELEASE_MASK);
2630 event = (GdkEventButton *) gdk_event_copy ((GdkEvent *) event);
2634 /* Leave the widget if we've moved - This doesn't break selection,
2635 * but stops buttons from being clicked.
2637 if ((child != priv->child) || (priv->moved)) {
2638 /* Send synthetic leave event */
2639 synth_crossing (priv->child, x, y, event->x_root,
2640 event->y_root, event->time, FALSE);
2641 /* Send synthetic button release event */
2642 ((GdkEventAny *) event)->window = g_object_ref (priv->child);
2643 gdk_event_put ((GdkEvent *) event);
2645 /* Send synthetic button release event */
2646 ((GdkEventAny *) event)->window = g_object_ref (child);
2647 gdk_event_put ((GdkEvent *) event);
2648 /* Send synthetic leave event */
2649 synth_crossing (priv->child, x, y, event->x_root,
2650 event->y_root, event->time, FALSE);
2652 g_object_remove_weak_pointer ((GObject *) priv->child,
2653 (gpointer) & priv->child);
2655 priv->moved = FALSE;
2656 gdk_event_free ((GdkEvent *) event);
2661 /* utility event handler */
2663 hildon_pannable_area_scroll_cb (GtkWidget *widget,
2664 GdkEventScroll *event)
2666 GtkAdjustment *adj = NULL;
2667 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
2669 if ((!priv->enabled) ||
2670 (gtk_bin_get_child (GTK_BIN (widget)) == NULL))
2673 priv->scroll_indicator_event_interrupt = 0;
2674 priv->scroll_delay_counter = priv->scrollbar_fade_delay + 20;
2676 hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget), 1.0);
2678 /* Stop inertial scrolling */
2679 if (priv->idle_id) {
2682 priv->overshooting_x = 0;
2683 priv->overshooting_y = 0;
2685 if ((priv->overshot_dist_x>0)||(priv->overshot_dist_y>0)) {
2686 priv->overshot_dist_x = 0;
2687 priv->overshot_dist_y = 0;
2689 gtk_widget_queue_resize (GTK_WIDGET (widget));
2692 g_source_remove (priv->idle_id);
2696 if (event->direction == GDK_SCROLL_UP || event->direction == GDK_SCROLL_DOWN)
2697 adj = priv->vadjust;
2699 adj = priv->hadjust;
2703 gdouble delta, new_value;
2705 /* from gtkrange.c calculate delta*/
2706 delta = pow (adj->page_size, 2.0 / 3.0);
2708 if (event->direction == GDK_SCROLL_UP ||
2709 event->direction == GDK_SCROLL_LEFT)
2712 new_value = CLAMP (adj->value + delta, adj->lower, adj->upper - adj->page_size);
2714 gtk_adjustment_set_value (adj, new_value);
2721 hildon_pannable_area_child_mapped (GtkWidget *widget,
2725 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (user_data)->priv;
2727 if (priv->event_window != NULL && priv->enabled)
2728 gdk_window_raise (priv->event_window);
2732 hildon_pannable_area_add (GtkContainer *container, GtkWidget *child)
2734 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (container)->priv;
2736 g_return_if_fail (gtk_bin_get_child (GTK_BIN (container)) == NULL);
2738 gtk_widget_set_parent (child, GTK_WIDGET (container));
2739 GTK_BIN (container)->child = child;
2741 g_signal_connect_after (child, "map-event",
2742 G_CALLBACK (hildon_pannable_area_child_mapped),
2745 if (!gtk_widget_set_scroll_adjustments (child, priv->hadjust, priv->vadjust)) {
2746 g_warning ("%s: cannot add non scrollable widget, "
2747 "wrap it in a viewport", __FUNCTION__);
2752 hildon_pannable_area_remove (GtkContainer *container, GtkWidget *child)
2754 g_return_if_fail (HILDON_IS_PANNABLE_AREA (container));
2755 g_return_if_fail (child != NULL);
2756 g_return_if_fail (gtk_bin_get_child (GTK_BIN (container)) == child);
2758 gtk_widget_set_scroll_adjustments (child, NULL, NULL);
2760 g_signal_handlers_disconnect_by_func (child,
2761 hildon_pannable_area_child_mapped,
2764 /* chain parent class handler to remove child */
2765 GTK_CONTAINER_CLASS (hildon_pannable_area_parent_class)->remove (container, child);
2769 * This method calculates a factor necessary to determine the initial distance
2770 * to jump in hildon_pannable_area_scroll_to(). For fixed time and frames per
2771 * second, we know in how many frames 'n' we need to reach the destination
2772 * point. We know that, for a distance d,
2774 * d = d_0 + d_1 + ... + d_n
2776 * where d_i is the distance travelled in the i-th frame and decel_factor is
2777 * the deceleration factor. This can be rewritten as
2779 * d = d_0 + (d_0 * decel_factor) + ... + (d_n-1 * decel_factor),
2781 * since the distance travelled on each frame is the distance travelled in the
2782 * previous frame reduced by the deceleration factor. Reducing this and
2783 * factoring d_0 out, we get
2785 * d = d_0 (1 + decel_factor + ... + decel_factor^(n-1)).
2787 * Since the sum is independent of the distance to be travelled, we can define
2790 * vel_factor = 1 + decel_factor + ... + decel_factor^(n-1).
2792 * That's the gem we calculate in this method.
2795 hildon_pannable_calculate_vel_factor (HildonPannableArea * self)
2797 HildonPannableAreaPrivate *priv = self->priv;
2802 n = ceil (priv->sps * priv->scroll_time);
2804 for (i = 1; i < n && fct_i >= RATIO_TOLERANCE; i++) {
2805 fct_i *= priv->decel;
2809 priv->vel_factor = fct;
2813 * hildon_pannable_area_new:
2815 * Create a new pannable area widget
2817 * Returns: the newly created #HildonPannableArea
2823 hildon_pannable_area_new (void)
2825 return g_object_new (HILDON_TYPE_PANNABLE_AREA, NULL);
2829 * hildon_pannable_area_new_full:
2830 * @mode: #HildonPannableAreaMode
2831 * @enabled: Value for the enabled property
2832 * @vel_min: Value for the velocity-min property
2833 * @vel_max: Value for the velocity-max property
2834 * @decel: Value for the deceleration property
2835 * @sps: Value for the sps property
2837 * Create a new #HildonPannableArea widget and set various properties
2839 * returns: the newly create #HildonPannableArea
2845 hildon_pannable_area_new_full (gint mode, gboolean enabled,
2846 gdouble vel_min, gdouble vel_max,
2847 gdouble decel, guint sps)
2849 return g_object_new (HILDON_TYPE_PANNABLE_AREA,
2852 "velocity_min", vel_min,
2853 "velocity_max", vel_max,
2854 "deceleration", decel, "sps", sps, NULL);
2858 * hildon_pannable_area_add_with_viewport:
2859 * @area: A #HildonPannableArea
2860 * @child: Child widget to add to the viewport
2862 * Convenience function used to add a child to a #GtkViewport, and add the
2863 * viewport to the scrolled window.
2869 hildon_pannable_area_add_with_viewport (HildonPannableArea * area,
2873 GtkWidget *viewport;
2875 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
2876 g_return_if_fail (GTK_IS_WIDGET (child));
2877 g_return_if_fail (child->parent == NULL);
2879 bin = GTK_BIN (area);
2881 if (bin->child != NULL)
2883 g_return_if_fail (GTK_IS_VIEWPORT (bin->child));
2884 g_return_if_fail (GTK_BIN (bin->child)->child == NULL);
2886 viewport = bin->child;
2890 HildonPannableAreaPrivate *priv = area->priv;
2892 viewport = gtk_viewport_new (priv->hadjust,
2894 gtk_viewport_set_shadow_type (GTK_VIEWPORT (viewport), GTK_SHADOW_NONE);
2895 gtk_container_add (GTK_CONTAINER (area), viewport);
2898 gtk_widget_show (viewport);
2899 gtk_container_add (GTK_CONTAINER (viewport), child);
2903 * hildon_pannable_area_scroll_to:
2904 * @area: A #HildonPannableArea.
2905 * @x: The x coordinate of the destination point or -1 to ignore this axis.
2906 * @y: The y coordinate of the destination point or -1 to ignore this axis.
2908 * Smoothly scrolls @area to ensure that (@x, @y) is a visible point
2909 * on the widget. To move in only one coordinate, you must set the other one
2910 * to -1. Notice that, in %HILDON_PANNABLE_AREA_MODE_PUSH mode, this function
2911 * works just like hildon_pannable_area_jump_to().
2913 * This function is useful if you need to present the user with a particular
2914 * element inside a scrollable widget, like #GtkTreeView. For instance,
2915 * the following example shows how to scroll inside a #GtkTreeView to
2916 * make visible an item, indicated by the #GtkTreeIter @iter.
2920 * GtkTreePath *path;
2921 * GdkRectangle *rect;
2923 * path = gtk_tree_model_get_path (model, &iter);
2924 * gtk_tree_view_get_background_area (GTK_TREE_VIEW (treeview),
2925 * path, NULL, &rect);
2926 * gtk_tree_view_convert_bin_window_to_tree_coords (GTK_TREE_VIEW (treeview),
2927 * 0, rect.y, NULL, &y);
2928 * hildon_pannable_area_scroll_to (panarea, -1, y);
2929 * gtk_tree_path_free (path);
2933 * If you want to present a child widget in simpler scenarios,
2934 * use hildon_pannable_area_scroll_to_child() instead.
2936 * There is a precondition to this function: the widget must be
2937 * already realized. Check the hildon_pannable_area_jump_to_child() for
2938 * more tips regarding how to call this function during
2944 hildon_pannable_area_scroll_to (HildonPannableArea *area,
2945 const gint x, const gint y)
2947 HildonPannableAreaPrivate *priv;
2949 gint dist_x, dist_y;
2950 gboolean hscroll_visible, vscroll_visible;
2952 g_return_if_fail (GTK_WIDGET_REALIZED (area));
2953 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
2957 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
2958 priv->vadjust->page_size);
2959 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
2960 priv->hadjust->page_size);
2962 if (((!vscroll_visible)&&(!hscroll_visible)) ||
2963 (x == -1 && y == -1)) {
2967 if (priv->mode == HILDON_PANNABLE_AREA_MODE_PUSH)
2968 hildon_pannable_area_jump_to (area, x, y);
2970 width = priv->hadjust->upper - priv->hadjust->lower;
2971 height = priv->vadjust->upper - priv->vadjust->lower;
2973 g_return_if_fail (x < width || y < height);
2975 if ((x > -1)&&(hscroll_visible)) {
2976 priv->scroll_to_x = x - priv->hadjust->page_size/2;
2977 dist_x = priv->scroll_to_x - priv->hadjust->value;
2979 priv->scroll_to_x = -1;
2981 priv->vel_x = - dist_x/priv->vel_factor;
2984 priv->scroll_to_x = -1;
2987 if ((y > -1)&&(vscroll_visible)) {
2988 priv->scroll_to_y = y - priv->vadjust->page_size/2;
2989 dist_y = priv->scroll_to_y - priv->vadjust->value;
2991 priv->scroll_to_y = -1;
2993 priv->vel_y = - dist_y/priv->vel_factor;
2996 priv->scroll_to_y = y;
2999 if ((priv->scroll_to_y == -1) && (priv->scroll_to_y == -1)) {
3003 hildon_pannable_area_launch_fade_timeout (area, 1.0);
3006 priv->idle_id = gdk_threads_add_timeout ((gint) (1000.0 / (gdouble) priv->sps),
3008 hildon_pannable_area_timeout, area);
3012 * hildon_pannable_area_jump_to:
3013 * @area: A #HildonPannableArea.
3014 * @x: The x coordinate of the destination point or -1 to ignore this axis.
3015 * @y: The y coordinate of the destination point or -1 to ignore this axis.
3017 * Jumps the position of @area to ensure that (@x, @y) is a visible
3018 * point in the widget. In order to move in only one coordinate, you
3019 * must set the other one to -1. See hildon_pannable_area_scroll_to()
3020 * function for an example of how to calculate the position of
3021 * children in scrollable widgets like #GtkTreeview.
3023 * There is a precondition to this function: the widget must be
3024 * already realized. Check the hildon_pannable_area_jump_to_child() for
3025 * more tips regarding how to call this function during
3031 hildon_pannable_area_jump_to (HildonPannableArea *area,
3032 const gint x, const gint y)
3034 HildonPannableAreaPrivate *priv;
3037 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3038 g_return_if_fail (GTK_WIDGET_REALIZED (area));
3039 g_return_if_fail (x >= -1 && y >= -1);
3041 if (x == -1 && y == -1) {
3047 width = priv->hadjust->upper - priv->hadjust->lower;
3048 height = priv->vadjust->upper - priv->vadjust->lower;
3050 g_return_if_fail (x < width || y < height);
3053 gdouble jump_to = x - priv->hadjust->page_size/2;
3055 if (jump_to > priv->hadjust->upper - priv->hadjust->page_size) {
3056 jump_to = priv->hadjust->upper - priv->hadjust->page_size;
3059 gtk_adjustment_set_value (priv->hadjust, jump_to);
3063 gdouble jump_to = y - priv->vadjust->page_size/2;
3065 if (jump_to > priv->vadjust->upper - priv->vadjust->page_size) {
3066 jump_to = priv->vadjust->upper - priv->vadjust->page_size;
3069 gtk_adjustment_set_value (priv->vadjust, jump_to);
3072 priv->scroll_indicator_alpha = 1.0;
3074 if (priv->scroll_indicator_timeout) {
3075 g_source_remove (priv->scroll_indicator_timeout);
3076 priv->scroll_indicator_timeout = 0;
3079 if (priv->idle_id) {
3082 priv->overshooting_x = 0;
3083 priv->overshooting_y = 0;
3085 if ((priv->overshot_dist_x>0)||(priv->overshot_dist_y>0)) {
3086 priv->overshot_dist_x = 0;
3087 priv->overshot_dist_y = 0;
3089 gtk_widget_queue_resize (GTK_WIDGET (area));
3092 g_source_remove (priv->idle_id);
3098 * hildon_pannable_area_scroll_to_child:
3099 * @area: A #HildonPannableArea.
3100 * @child: A #GtkWidget, descendant of @area.
3102 * Smoothly scrolls until @child is visible inside @area. @child must
3103 * be a descendant of @area. If you need to scroll inside a scrollable
3104 * widget, e.g., #GtkTreeview, see hildon_pannable_area_scroll_to().
3106 * There is a precondition to this function: the widget must be
3107 * already realized. Check the hildon_pannable_area_jump_to_child() for
3108 * more tips regarding how to call this function during
3114 hildon_pannable_area_scroll_to_child (HildonPannableArea *area, GtkWidget *child)
3116 GtkWidget *bin_child;
3119 g_return_if_fail (GTK_WIDGET_REALIZED (area));
3120 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3121 g_return_if_fail (GTK_IS_WIDGET (child));
3122 g_return_if_fail (gtk_widget_is_ancestor (child, GTK_WIDGET (area)));
3124 if (GTK_BIN (area)->child == NULL)
3127 /* We need to get to check the child of the inside the area */
3128 bin_child = GTK_BIN (area)->child;
3130 /* we check if we added a viewport */
3131 if (GTK_IS_VIEWPORT (bin_child)) {
3132 bin_child = GTK_BIN (bin_child)->child;
3135 if (gtk_widget_translate_coordinates (child, bin_child, 0, 0, &x, &y))
3136 hildon_pannable_area_scroll_to (area, x, y);
3140 * hildon_pannable_area_jump_to_child:
3141 * @area: A #HildonPannableArea.
3142 * @child: A #GtkWidget, descendant of @area.
3144 * Jumps to make sure @child is visible inside @area. @child must
3145 * be a descendant of @area. If you want to move inside a scrollable
3146 * widget, like, #GtkTreeview, see hildon_pannable_area_scroll_to().
3148 * There is a precondition to this function: the widget must be
3149 * already realized. You can control if the widget is ready with the
3150 * GTK_WIDGET_REALIZED macro. If you want to call this function during
3151 * the initialization process of the widget do it inside a callback to
3152 * the ::realize signal, using g_signal_connect_after() function.
3157 hildon_pannable_area_jump_to_child (HildonPannableArea *area, GtkWidget *child)
3159 GtkWidget *bin_child;
3162 g_return_if_fail (GTK_WIDGET_REALIZED (area));
3163 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3164 g_return_if_fail (GTK_IS_WIDGET (child));
3165 g_return_if_fail (gtk_widget_is_ancestor (child, GTK_WIDGET (area)));
3167 if (gtk_bin_get_child (GTK_BIN (area)) == NULL)
3170 /* We need to get to check the child of the inside the area */
3171 bin_child = gtk_bin_get_child (GTK_BIN (area));
3173 /* we check if we added a viewport */
3174 if (GTK_IS_VIEWPORT (bin_child)) {
3175 bin_child = gtk_bin_get_child (GTK_BIN (bin_child));
3178 if (gtk_widget_translate_coordinates (child, bin_child, 0, 0, &x, &y))
3179 hildon_pannable_area_jump_to (area, x, y);
3183 * hildon_pannable_get_child_widget_at:
3184 * @area: A #HildonPannableArea.
3185 * @x: horizontal coordinate of the point
3186 * @y: vertical coordinate of the point
3188 * Get the widget at the point (x, y) inside the pannable area. In
3189 * case no widget found it returns NULL.
3191 * returns: the #GtkWidget if we find a widget, NULL in any other case
3196 hildon_pannable_get_child_widget_at (HildonPannableArea *area,
3197 gdouble x, gdouble y)
3199 GdkWindow *window = NULL;
3200 GtkWidget *child_widget = NULL;
3202 window = hildon_pannable_area_get_topmost
3203 (gtk_bin_get_child (GTK_BIN (area))->window,
3204 x, y, NULL, NULL, GDK_ALL_EVENTS_MASK);
3206 gdk_window_get_user_data (window, (gpointer) &child_widget);
3208 return child_widget;
3213 * hildon_pannable_area_get_hadjustment:
3214 * @area: A #HildonPannableArea.
3216 * Returns the horizontal adjustment. This adjustment is the internal
3217 * widget adjustment used to control the animations. Do not modify it
3218 * directly to change the position of the pannable, to do that use the
3219 * pannable API. If you modify the object directly it could cause
3220 * artifacts in the animations.
3222 * returns: The horizontal #GtkAdjustment
3227 hildon_pannable_area_get_hadjustment (HildonPannableArea *area)
3230 g_return_val_if_fail (HILDON_IS_PANNABLE_AREA (area), NULL);
3232 return area->priv->hadjust;
3236 * hildon_pannable_area_get_vadjustment:
3237 * @area: A #HildonPannableArea.
3239 * Returns the vertical adjustment. This adjustment is the internal
3240 * widget adjustment used to control the animations. Do not modify it
3241 * directly to change the position of the pannable, to do that use the
3242 * pannable API. If you modify the object directly it could cause
3243 * artifacts in the animations.
3245 * returns: The vertical #GtkAdjustment
3250 hildon_pannable_area_get_vadjustment (HildonPannableArea *area)
3252 g_return_val_if_fail (HILDON_IS_PANNABLE_AREA (area), NULL);
3254 return area->priv->vadjust;
3259 * hildon_pannable_area_get_size_request_policy:
3260 * @area: A #HildonPannableArea.
3262 * This function returns the current size request policy of the
3263 * widget. That policy controls the way the size_request is done in
3264 * the pannable area. Check
3265 * hildon_pannable_area_set_size_request_policy() for a more detailed
3268 * returns: the policy is currently being used in the widget
3269 * #HildonSizeRequestPolicy.
3273 HildonSizeRequestPolicy
3274 hildon_pannable_area_get_size_request_policy (HildonPannableArea *area)
3276 HildonPannableAreaPrivate *priv;
3278 g_return_val_if_fail (HILDON_IS_PANNABLE_AREA (area), FALSE);
3282 return priv->size_request_policy;
3286 * hildon_pannable_area_set_size_request_policy:
3287 * @area: A #HildonPannableArea.
3288 * @size_request_policy: One of the allowed #HildonSizeRequestPolicy
3290 * This function sets the pannable area size request policy. That
3291 * policy controls the way the size_request is done in the pannable
3292 * area. Pannable can use the size request of its children
3293 * (#HILDON_SIZE_REQUEST_CHILDREN) or the minimum size required for
3294 * the area itself (#HILDON_SIZE_REQUEST_MINIMUM), the latter is the
3295 * default. Recall this size depends on the scrolling policy you are
3296 * requesting to the pannable area, if you set #GTK_POLICY_NEVER this
3297 * parameter will not have any effect with
3298 * #HILDON_SIZE_REQUEST_MINIMUM set.
3302 * Deprecated: This method and the policy request is deprecated, DO
3303 * NOT use it in future code, the only policy properly supported in
3304 * gtk+ nowadays is the minimum size. Use #gtk_window_set_default_size
3305 * or #gtk_window_set_geometry_hints with the proper size in your case
3306 * to define the height of your dialogs.
3309 hildon_pannable_area_set_size_request_policy (HildonPannableArea *area,
3310 HildonSizeRequestPolicy size_request_policy)
3312 HildonPannableAreaPrivate *priv;
3314 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3318 if (priv->size_request_policy == size_request_policy)
3321 priv->size_request_policy = size_request_policy;
3323 gtk_widget_queue_resize (GTK_WIDGET (area));
3325 g_object_notify (G_OBJECT (area), "size-request-policy");