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;
140 GtkPolicyType vscrollbar_policy;
141 GtkPolicyType hscrollbar_policy;
143 GdkGC *scrollbars_gc;
153 static guint pannable_area_signals [LAST_SIGNAL] = { 0 };
161 PROP_VEL_MAX_OVERSHOOTING,
162 PROP_VELOCITY_FAST_FACTOR,
166 PROP_PANNING_THRESHOLD,
167 PROP_SCROLLBAR_FADE_DELAY,
170 PROP_DIRECTION_ERROR_MARGIN,
171 PROP_VSCROLLBAR_POLICY,
172 PROP_HSCROLLBAR_POLICY,
177 PROP_LOW_FRICTION_MODE,
178 PROP_SIZE_REQUEST_POLICY,
184 static void hildon_pannable_area_class_init (HildonPannableAreaClass * klass);
185 static void hildon_pannable_area_init (HildonPannableArea * area);
186 static void hildon_pannable_area_get_property (GObject * object,
190 static void hildon_pannable_area_set_property (GObject * object,
192 const GValue * value,
194 static void hildon_pannable_area_dispose (GObject * object);
195 static void hildon_pannable_area_realize (GtkWidget * widget);
196 static void hildon_pannable_area_unrealize (GtkWidget * widget);
197 static void hildon_pannable_area_size_request (GtkWidget * widget,
198 GtkRequisition * requisition);
199 static void hildon_pannable_area_size_allocate (GtkWidget * widget,
200 GtkAllocation * allocation);
201 static void hildon_pannable_area_child_allocate_calculate (GtkWidget * widget,
202 GtkAllocation * allocation,
203 GtkAllocation * child_allocation);
204 static void hildon_pannable_area_style_set (GtkWidget * widget,
205 GtkStyle * previous_style);
206 static void hildon_pannable_area_map (GtkWidget * widget);
207 static void hildon_pannable_area_unmap (GtkWidget * widget);
208 static void hildon_pannable_area_grab_notify (GtkWidget *widget,
209 gboolean was_grabbed,
211 #if USE_CAIRO_SCROLLBARS == 1
212 static void rgb_from_gdkcolor (GdkColor *color, gdouble *r, gdouble *g, gdouble *b);
213 #else /* USE_CAIRO_SCROLLBARS */
214 static void tranparency_color (GdkColor *color,
217 gdouble transparency);
218 #endif /* USE_CAIRO_SCROLLBARS */
219 static void hildon_pannable_draw_vscroll (GtkWidget * widget,
220 GdkColor *back_color,
221 GdkColor *scroll_color);
222 static void hildon_pannable_draw_hscroll (GtkWidget * widget,
223 GdkColor *back_color,
224 GdkColor *scroll_color);
225 static void hildon_pannable_area_initial_effect (GtkWidget * widget);
226 static void hildon_pannable_area_redraw (HildonPannableArea * area);
227 static void hildon_pannable_area_launch_fade_timeout (HildonPannableArea * area,
229 static void hildon_pannable_area_adjust_value_changed (HildonPannableArea * area,
231 static void hildon_pannable_area_adjust_changed (HildonPannableArea * area,
233 static gboolean hildon_pannable_area_scroll_indicator_fade(HildonPannableArea * area);
234 static gboolean hildon_pannable_area_expose_event (GtkWidget * widget,
235 GdkEventExpose * event);
236 static GdkWindow * hildon_pannable_area_get_topmost (GdkWindow * window,
238 gint * tx, gint * ty,
240 static void synth_crossing (GdkWindow * child,
242 gint x_root, gint y_root,
243 guint32 time, gboolean in);
244 static gboolean hildon_pannable_area_button_press_cb (GtkWidget * widget,
245 GdkEventButton * event);
246 static void hildon_pannable_area_refresh (HildonPannableArea * area);
247 static gboolean hildon_pannable_area_check_scrollbars (HildonPannableArea * area);
248 static void hildon_pannable_axis_scroll (HildonPannableArea *area,
249 GtkAdjustment *adjust,
257 static void hildon_pannable_area_scroll (HildonPannableArea *area,
258 gdouble x, gdouble y);
259 static gboolean hildon_pannable_area_timeout (HildonPannableArea * area);
260 static void hildon_pannable_area_calculate_velocity (gdouble *vel,
264 gdouble drag_inertia,
267 static gboolean hildon_pannable_area_motion_event_scroll_timeout (HildonPannableArea *area);
268 static void hildon_pannable_area_motion_event_scroll (HildonPannableArea *area,
269 gdouble x, gdouble y);
270 static gboolean hildon_pannable_area_motion_notify_cb (GtkWidget * widget,
271 GdkEventMotion * event);
272 static gboolean hildon_pannable_leave_notify_event (GtkWidget *widget,
273 GdkEventCrossing *event);
274 static gboolean hildon_pannable_area_button_release_cb (GtkWidget * widget,
275 GdkEventButton * event);
276 static gboolean hildon_pannable_area_scroll_cb (GtkWidget *widget,
277 GdkEventScroll *event);
278 static void hildon_pannable_area_child_mapped (GtkWidget *widget,
281 static void hildon_pannable_area_add (GtkContainer *container, GtkWidget *child);
282 static void hildon_pannable_area_remove (GtkContainer *container, GtkWidget *child);
283 static void hildon_pannable_calculate_vel_factor (HildonPannableArea * self);
287 hildon_pannable_area_class_init (HildonPannableAreaClass * klass)
289 GObjectClass *object_class = G_OBJECT_CLASS (klass);
290 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
291 GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
294 g_type_class_add_private (klass, sizeof (HildonPannableAreaPrivate));
296 object_class->get_property = hildon_pannable_area_get_property;
297 object_class->set_property = hildon_pannable_area_set_property;
298 object_class->dispose = hildon_pannable_area_dispose;
300 widget_class->realize = hildon_pannable_area_realize;
301 widget_class->unrealize = hildon_pannable_area_unrealize;
302 widget_class->map = hildon_pannable_area_map;
303 widget_class->unmap = hildon_pannable_area_unmap;
304 widget_class->size_request = hildon_pannable_area_size_request;
305 widget_class->size_allocate = hildon_pannable_area_size_allocate;
306 widget_class->expose_event = hildon_pannable_area_expose_event;
307 widget_class->style_set = hildon_pannable_area_style_set;
308 widget_class->button_press_event = hildon_pannable_area_button_press_cb;
309 widget_class->button_release_event = hildon_pannable_area_button_release_cb;
310 widget_class->motion_notify_event = hildon_pannable_area_motion_notify_cb;
311 widget_class->leave_notify_event = hildon_pannable_leave_notify_event;
312 widget_class->scroll_event = hildon_pannable_area_scroll_cb;
314 container_class->add = hildon_pannable_area_add;
315 container_class->remove = hildon_pannable_area_remove;
317 klass->horizontal_movement = NULL;
318 klass->vertical_movement = NULL;
320 g_object_class_install_property (object_class,
322 g_param_spec_boolean ("enabled",
324 "Enable or disable finger-scroll.",
329 g_object_class_install_property (object_class,
330 PROP_VSCROLLBAR_POLICY,
331 g_param_spec_enum ("vscrollbar_policy",
333 "Visual policy of the vertical scrollbar",
334 GTK_TYPE_POLICY_TYPE,
335 GTK_POLICY_AUTOMATIC,
339 g_object_class_install_property (object_class,
340 PROP_HSCROLLBAR_POLICY,
341 g_param_spec_enum ("hscrollbar_policy",
343 "Visual policy of the horizontal scrollbar",
344 GTK_TYPE_POLICY_TYPE,
345 GTK_POLICY_AUTOMATIC,
349 g_object_class_install_property (object_class,
351 g_param_spec_enum ("mode",
353 "Change the finger-scrolling mode.",
354 HILDON_TYPE_PANNABLE_AREA_MODE,
355 HILDON_PANNABLE_AREA_MODE_AUTO,
359 g_object_class_install_property (object_class,
361 g_param_spec_flags ("mov_mode",
362 "Scroll movement mode",
363 "Controls if the widget can scroll vertically, horizontally or both",
364 HILDON_TYPE_MOVEMENT_MODE,
365 HILDON_MOVEMENT_MODE_VERT,
369 g_object_class_install_property (object_class,
371 g_param_spec_double ("velocity_min",
372 "Minimum scroll velocity",
373 "Minimum distance the child widget should scroll "
374 "per 'frame', in pixels per frame.",
379 g_object_class_install_property (object_class,
381 g_param_spec_double ("velocity_max",
382 "Maximum scroll velocity",
383 "Maximum distance the child widget should scroll "
384 "per 'frame', in pixels per frame.",
389 g_object_class_install_property (object_class,
390 PROP_VEL_MAX_OVERSHOOTING,
391 g_param_spec_double ("velocity_overshooting_max",
392 "Maximum scroll velocity when overshooting",
393 "Maximum distance the child widget should scroll "
394 "per 'frame', in pixels per frame when it overshoots after hitting the edge.",
399 g_object_class_install_property (object_class,
400 PROP_VELOCITY_FAST_FACTOR,
401 g_param_spec_double ("velocity_fast_factor",
402 "Fast velocity factor",
403 "Minimum velocity that is considered 'fast': "
404 "children widgets won't receive button presses. "
405 "Expressed as a fraction of the maximum velocity.",
410 g_object_class_install_property (object_class,
412 g_param_spec_double ("deceleration",
413 "Deceleration multiplier",
414 "The multiplier used when decelerating when in "
415 "acceleration scrolling mode.",
420 g_object_class_install_property (object_class,
422 g_param_spec_double ("drag_inertia",
423 "Inertia of the cursor dragging",
424 "Percentage of the calculated speed in each moment we are are going to use"
425 "to calculate the launch speed, the other part would be the speed"
426 "calculated previously",
431 g_object_class_install_property (object_class,
433 g_param_spec_uint ("sps",
434 "Scrolls per second",
435 "Amount of scroll events to generate per second.",
440 g_object_class_install_property (object_class,
441 PROP_PANNING_THRESHOLD,
442 g_param_spec_uint ("panning_threshold",
443 "Threshold to consider a motion event an scroll",
444 "Amount of pixels to consider a motion event an scroll, if it is less"
445 "it is a click detected incorrectly by the touch screen.",
450 g_object_class_install_property (object_class,
451 PROP_SCROLLBAR_FADE_DELAY,
452 g_param_spec_uint ("scrollbar_fade_delay",
453 "Time before starting to fade the scrollbar",
454 "Time the scrollbar is going to be visible if the widget is not in"
455 "action in miliseconds",
460 g_object_class_install_property (object_class,
462 g_param_spec_uint ("bounce_steps",
464 "Number of steps that is going to be used to bounce when hitting the"
465 "edge, the rubberband effect depends on it",
470 g_object_class_install_property (object_class,
472 g_param_spec_uint ("force",
473 "Multiplier of the calculated speed",
474 "Force applied to the movement, multiplies the calculated speed of the"
475 "user movement the cursor in the screen",
480 g_object_class_install_property (object_class,
481 PROP_DIRECTION_ERROR_MARGIN,
482 g_param_spec_uint ("direction_error_margin",
483 "Margin in the direction detection",
484 "After detecting the direction of the movement (horizontal or"
485 "vertical), we can add this margin of error to allow the movement in"
486 "the other direction even apparently it is not",
491 g_object_class_install_property (object_class,
493 g_param_spec_int ("vovershoot_max",
494 "Vertical overshoot distance",
495 "Space we allow the widget to pass over its vertical limits when"
496 "hitting the edges, set 0 in order to deactivate overshooting.",
501 g_object_class_install_property (object_class,
503 g_param_spec_int ("hovershoot_max",
504 "Horizontal overshoot distance",
505 "Space we allow the widget to pass over its horizontal limits when"
506 "hitting the edges, set 0 in order to deactivate overshooting.",
511 g_object_class_install_property (object_class,
513 g_param_spec_double ("scroll_time",
514 "Time to scroll to a position",
515 "The time to scroll to a position when calling the hildon_pannable_scroll_to function",
520 g_object_class_install_property (object_class,
522 g_param_spec_boolean ("initial-hint",
524 "Whether to hint the user about the pannability of the container.",
529 g_object_class_install_property (object_class,
530 PROP_LOW_FRICTION_MODE,
531 g_param_spec_boolean ("low-friction-mode",
532 "Do not decelerate the initial velocity",
533 "Avoid decelerating the panning movement, like no friction, the widget"
534 "will stop in the edges or if the user clicks.",
539 g_object_class_install_property (object_class,
540 PROP_SIZE_REQUEST_POLICY,
541 g_param_spec_enum ("size-request-policy",
542 "Size Requisition policy",
543 "Controls the size request policy of the widget",
544 HILDON_TYPE_SIZE_REQUEST_POLICY,
545 HILDON_SIZE_REQUEST_MINIMUM,
549 g_object_class_install_property (object_class,
551 g_param_spec_object ("hadjustment",
552 "Horizontal Adjustment",
553 "The GtkAdjustment for the horizontal position",
556 g_object_class_install_property (object_class,
558 g_param_spec_object ("vadjustment",
559 "Vertical Adjustment",
560 "The GtkAdjustment for the vertical position",
564 gtk_widget_class_install_style_property (widget_class,
567 "Width of the scroll indicators",
568 "Pixel width used to draw the scroll indicators.",
573 * HildonPannableArea::horizontal-movement:
574 * @hildonpannable: the object which received the signal
575 * @direction: the direction of the movement #HILDON_MOVEMENT_LEFT or #HILDON_MOVEMENT_RIGHT
576 * @initial_x: the x coordinate of the point where the user clicked to start the movement
577 * @initial_y: the y coordinate of the point where the user clicked to start the movement
579 * The horizontal-movement signal is emitted when the pannable area
580 * detects a horizontal movement. The detection does not mean the
581 * widget is going to move (i.e. maybe the children are smaller
582 * horizontally than the screen).
586 pannable_area_signals[HORIZONTAL_MOVEMENT] =
587 g_signal_new ("horizontal_movement",
588 G_TYPE_FROM_CLASS (object_class),
589 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
590 G_STRUCT_OFFSET (HildonPannableAreaClass, horizontal_movement),
592 _hildon_marshal_VOID__INT_DOUBLE_DOUBLE,
599 * HildonPannableArea::vertical-movement:
600 * @hildonpannable: the object which received the signal
601 * @direction: the direction of the movement #HILDON_MOVEMENT_UP or #HILDON_MOVEMENT_DOWN
602 * @initial_x: the x coordinate of the point where the user clicked to start the movement
603 * @initial_y: the y coordinate of the point where the user clicked to start the movement
605 * The vertical-movement signal is emitted when the pannable area
606 * detects a vertical movement. The detection does not mean the
607 * widget is going to move (i.e. maybe the children are smaller
608 * vertically than the screen).
612 pannable_area_signals[VERTICAL_MOVEMENT] =
613 g_signal_new ("vertical_movement",
614 G_TYPE_FROM_CLASS (object_class),
615 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
616 G_STRUCT_OFFSET (HildonPannableAreaClass, vertical_movement),
618 _hildon_marshal_VOID__INT_DOUBLE_DOUBLE,
627 hildon_pannable_area_init (HildonPannableArea * area)
629 HildonPannableAreaPrivate *priv = PANNABLE_AREA_PRIVATE (area);
631 GTK_WIDGET_UNSET_FLAGS (area, GTK_NO_WINDOW);
636 priv->button_pressed = FALSE;
639 priv->vscroll_visible = TRUE;
640 priv->hscroll_visible = TRUE;
641 priv->indicator_width = 6;
642 priv->overshot_dist_x = 0;
643 priv->overshot_dist_y = 0;
644 priv->overshooting_y = 0;
645 priv->overshooting_x = 0;
649 priv->scroll_indicator_alpha = 0.0;
650 priv->scroll_indicator_timeout = 0;
651 priv->motion_event_scroll_timeout = 0;
652 priv->scroll_indicator_event_interrupt = 0;
653 priv->scroll_delay_counter = 0;
654 priv->scrollbar_fade_delay = 0;
655 priv->scroll_to_x = -1;
656 priv->scroll_to_y = -1;
657 priv->first_drag = TRUE;
658 priv->initial_effect = TRUE;
659 priv->child_width = 0;
660 priv->child_height = 0;
661 priv->last_in = TRUE;
665 gtk_widget_add_events (GTK_WIDGET (area), GDK_POINTER_MOTION_HINT_MASK);
668 GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
670 GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
672 g_object_ref_sink (G_OBJECT (priv->hadjust));
673 g_object_ref_sink (G_OBJECT (priv->vadjust));
675 g_signal_connect_swapped (priv->hadjust, "value-changed",
676 G_CALLBACK (hildon_pannable_area_adjust_value_changed), area);
677 g_signal_connect_swapped (priv->vadjust, "value-changed",
678 G_CALLBACK (hildon_pannable_area_adjust_value_changed), area);
679 g_signal_connect_swapped (priv->hadjust, "changed",
680 G_CALLBACK (hildon_pannable_area_adjust_changed), area);
681 g_signal_connect_swapped (priv->vadjust, "changed",
682 G_CALLBACK (hildon_pannable_area_adjust_changed), area);
683 g_signal_connect (area, "grab-notify",
684 G_CALLBACK (hildon_pannable_area_grab_notify), NULL);
688 hildon_pannable_area_get_property (GObject * object,
693 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (object)->priv;
695 switch (property_id) {
697 g_value_set_boolean (value, priv->enabled);
700 g_value_set_enum (value, priv->mode);
702 case PROP_MOVEMENT_MODE:
703 g_value_set_flags (value, priv->mov_mode);
705 case PROP_VELOCITY_MIN:
706 g_value_set_double (value, priv->vmin);
708 case PROP_VELOCITY_MAX:
709 g_value_set_double (value, priv->vmax);
711 case PROP_VEL_MAX_OVERSHOOTING:
712 g_value_set_double (value, priv->vmax_overshooting);
714 case PROP_VELOCITY_FAST_FACTOR:
715 g_value_set_double (value, priv->vfast_factor);
717 case PROP_DECELERATION:
718 g_value_set_double (value, priv->decel);
720 case PROP_DRAG_INERTIA:
721 g_value_set_double (value, priv->drag_inertia);
724 g_value_set_uint (value, priv->sps);
726 case PROP_PANNING_THRESHOLD:
727 g_value_set_uint (value, priv->panning_threshold);
729 case PROP_SCROLLBAR_FADE_DELAY:
730 /* convert to miliseconds */
731 g_value_set_uint (value, priv->scrollbar_fade_delay * SCROLL_FADE_TIMEOUT);
733 case PROP_BOUNCE_STEPS:
734 g_value_set_uint (value, priv->bounce_steps);
737 g_value_set_uint (value, priv->force);
739 case PROP_DIRECTION_ERROR_MARGIN:
740 g_value_set_uint (value, priv->direction_error_margin);
742 case PROP_VSCROLLBAR_POLICY:
743 g_value_set_enum (value, priv->vscrollbar_policy);
745 case PROP_HSCROLLBAR_POLICY:
746 g_value_set_enum (value, priv->hscrollbar_policy);
748 case PROP_VOVERSHOOT_MAX:
749 g_value_set_int (value, priv->vovershoot_max);
751 case PROP_HOVERSHOOT_MAX:
752 g_value_set_int (value, priv->hovershoot_max);
754 case PROP_SCROLL_TIME:
755 g_value_set_double (value, priv->scroll_time);
757 case PROP_INITIAL_HINT:
758 g_value_set_boolean (value, priv->initial_hint);
760 case PROP_LOW_FRICTION_MODE:
761 g_value_set_boolean (value, priv->low_friction_mode);
763 case PROP_SIZE_REQUEST_POLICY:
764 g_value_set_enum (value, priv->size_request_policy);
766 case PROP_HADJUSTMENT:
767 g_value_set_object (value,
768 hildon_pannable_area_get_hadjustment
769 (HILDON_PANNABLE_AREA (object)));
771 case PROP_VADJUSTMENT:
772 g_value_set_object (value,
773 hildon_pannable_area_get_vadjustment
774 (HILDON_PANNABLE_AREA (object)));
777 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
782 hildon_pannable_area_set_property (GObject * object,
784 const GValue * value,
787 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (object)->priv;
790 switch (property_id) {
792 enabled = g_value_get_boolean (value);
794 if ((priv->enabled != enabled) && (GTK_WIDGET_REALIZED (object))) {
796 gdk_window_raise (priv->event_window);
798 gdk_window_lower (priv->event_window);
801 priv->enabled = enabled;
804 priv->mode = g_value_get_enum (value);
806 case PROP_MOVEMENT_MODE:
807 priv->mov_mode = g_value_get_flags (value);
809 case PROP_VELOCITY_MIN:
810 priv->vmin = g_value_get_double (value);
812 case PROP_VELOCITY_MAX:
813 priv->vmax = g_value_get_double (value);
815 case PROP_VEL_MAX_OVERSHOOTING:
816 priv->vmax_overshooting = g_value_get_double (value);
818 case PROP_VELOCITY_FAST_FACTOR:
819 priv->vfast_factor = g_value_get_double (value);
821 case PROP_DECELERATION:
822 priv->decel = g_value_get_double (value);
823 hildon_pannable_calculate_vel_factor (HILDON_PANNABLE_AREA (object));
825 case PROP_DRAG_INERTIA:
826 priv->drag_inertia = g_value_get_double (value);
829 priv->sps = g_value_get_uint (value);
831 case PROP_PANNING_THRESHOLD:
833 GtkSettings *settings = gtk_settings_get_default ();
834 GtkSettingsValue svalue = { NULL, { 0, }, };
836 priv->panning_threshold = g_value_get_uint (value);
838 /* insure gtk dnd is the same we are using, not allowed
839 different thresholds in the same application */
840 svalue.origin = "panning_threshold";
841 g_value_init (&svalue.value, G_TYPE_LONG);
842 g_value_set_long (&svalue.value, priv->panning_threshold);
843 gtk_settings_set_property_value (settings, "gtk-dnd-drag-threshold", &svalue);
844 g_value_unset (&svalue.value);
847 case PROP_SCROLLBAR_FADE_DELAY:
848 /* convert to miliseconds */
849 priv->scrollbar_fade_delay = g_value_get_uint (value)/(SCROLL_FADE_TIMEOUT);
851 case PROP_BOUNCE_STEPS:
852 priv->bounce_steps = g_value_get_uint (value);
855 priv->force = g_value_get_uint (value);
857 case PROP_DIRECTION_ERROR_MARGIN:
858 priv->direction_error_margin = g_value_get_uint (value);
860 case PROP_VSCROLLBAR_POLICY:
861 priv->vscrollbar_policy = g_value_get_enum (value);
863 gtk_widget_queue_resize (GTK_WIDGET (object));
865 case PROP_HSCROLLBAR_POLICY:
866 priv->hscrollbar_policy = g_value_get_enum (value);
868 gtk_widget_queue_resize (GTK_WIDGET (object));
870 case PROP_VOVERSHOOT_MAX:
871 priv->vovershoot_max = g_value_get_int (value);
873 case PROP_HOVERSHOOT_MAX:
874 priv->hovershoot_max = g_value_get_int (value);
876 case PROP_SCROLL_TIME:
877 priv->scroll_time = g_value_get_double (value);
879 hildon_pannable_calculate_vel_factor (HILDON_PANNABLE_AREA (object));
881 case PROP_INITIAL_HINT:
882 priv->initial_hint = g_value_get_boolean (value);
884 case PROP_LOW_FRICTION_MODE:
885 priv->low_friction_mode = g_value_get_boolean (value);
887 case PROP_SIZE_REQUEST_POLICY:
888 hildon_pannable_area_set_size_request_policy (HILDON_PANNABLE_AREA (object),
889 g_value_get_enum (value));
893 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
898 hildon_pannable_area_dispose (GObject * object)
900 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (object)->priv;
901 GtkWidget *child = gtk_bin_get_child (GTK_BIN (object));
904 g_source_remove (priv->idle_id);
908 if (priv->scroll_indicator_timeout){
909 g_source_remove (priv->scroll_indicator_timeout);
910 priv->scroll_indicator_timeout = 0;
913 if (priv->motion_event_scroll_timeout){
914 g_source_remove (priv->motion_event_scroll_timeout);
915 priv->motion_event_scroll_timeout = 0;
919 g_signal_handlers_disconnect_by_func (child,
920 hildon_pannable_area_child_mapped,
924 g_signal_handlers_disconnect_by_func (object,
925 hildon_pannable_area_grab_notify,
929 g_signal_handlers_disconnect_by_func (priv->hadjust,
930 hildon_pannable_area_adjust_value_changed,
932 g_signal_handlers_disconnect_by_func (priv->hadjust,
933 hildon_pannable_area_adjust_changed,
935 g_object_unref (priv->hadjust);
936 priv->hadjust = NULL;
940 g_signal_handlers_disconnect_by_func (priv->vadjust,
941 hildon_pannable_area_adjust_value_changed,
943 g_signal_handlers_disconnect_by_func (priv->vadjust,
944 hildon_pannable_area_adjust_changed,
946 g_object_unref (priv->vadjust);
947 priv->vadjust = NULL;
950 if (G_OBJECT_CLASS (hildon_pannable_area_parent_class)->dispose)
951 G_OBJECT_CLASS (hildon_pannable_area_parent_class)->dispose (object);
955 hildon_pannable_area_realize (GtkWidget * widget)
957 GdkWindowAttr attributes;
958 gint attributes_mask;
960 HildonPannableAreaPrivate *priv;
962 priv = HILDON_PANNABLE_AREA (widget)->priv;
964 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
966 border_width = GTK_CONTAINER (widget)->border_width;
968 attributes.x = widget->allocation.x + border_width;
969 attributes.y = widget->allocation.y + border_width;
970 attributes.width = MAX (widget->allocation.width - 2 * border_width, 0);
971 attributes.height = MAX (widget->allocation.height - 2 * border_width, 0);
972 attributes.window_type = GDK_WINDOW_CHILD;
974 /* avoid using the hildon_window */
975 attributes.visual = gtk_widget_get_visual (widget);
976 attributes.colormap = gtk_widget_get_colormap (widget);
977 attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK;
978 attributes.wclass = GDK_INPUT_OUTPUT;
980 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
982 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
983 &attributes, attributes_mask);
984 gdk_window_set_user_data (widget->window, widget);
986 /* create the events window */
989 attributes.event_mask = gtk_widget_get_events (widget)
990 | GDK_BUTTON_MOTION_MASK
991 | GDK_BUTTON_PRESS_MASK
992 | GDK_BUTTON_RELEASE_MASK
994 | GDK_EXPOSURE_MASK | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK;
995 attributes.wclass = GDK_INPUT_ONLY;
997 attributes_mask = GDK_WA_X | GDK_WA_Y;
999 priv->event_window = gdk_window_new (widget->window,
1000 &attributes, attributes_mask);
1001 gdk_window_set_user_data (priv->event_window, widget);
1003 widget->style = gtk_style_attach (widget->style, widget->window);
1004 gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
1006 priv->scrollbars_gc = gdk_gc_new (GDK_DRAWABLE (widget->window));
1007 gdk_gc_copy (priv->scrollbars_gc, widget->style->fg_gc[GTK_STATE_INSENSITIVE]);
1011 hildon_pannable_area_unrealize (GtkWidget * widget)
1013 HildonPannableAreaPrivate *priv;
1015 priv = HILDON_PANNABLE_AREA (widget)->priv;
1017 if (priv->event_window != NULL) {
1018 gdk_window_set_user_data (priv->event_window, NULL);
1019 gdk_window_destroy (priv->event_window);
1020 priv->event_window = NULL;
1023 gdk_gc_unref (priv->scrollbars_gc);
1025 if (GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->unrealize)
1026 (*GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->unrealize)(widget);
1030 hildon_pannable_area_size_request (GtkWidget * widget,
1031 GtkRequisition * requisition)
1033 GtkRequisition child_requisition = {0};
1034 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1035 GtkWidget *child = gtk_bin_get_child (GTK_BIN (widget));
1037 if (child && GTK_WIDGET_VISIBLE (child))
1039 gtk_widget_size_request (child, &child_requisition);
1042 if (priv->hscrollbar_policy == GTK_POLICY_NEVER) {
1043 requisition->width = child_requisition.width;
1045 switch (priv->size_request_policy) {
1046 case HILDON_SIZE_REQUEST_CHILDREN:
1047 requisition->width = MIN (PANNABLE_MAX_WIDTH,
1048 child_requisition.width);
1050 case HILDON_SIZE_REQUEST_MINIMUM:
1052 requisition->width = priv->indicator_width;
1056 if (priv->vscrollbar_policy == GTK_POLICY_NEVER) {
1057 requisition->height = child_requisition.height;
1059 switch (priv->size_request_policy) {
1060 case HILDON_SIZE_REQUEST_CHILDREN:
1061 requisition->height = MIN (PANNABLE_MAX_HEIGHT,
1062 child_requisition.height);
1064 case HILDON_SIZE_REQUEST_MINIMUM:
1066 requisition->height = priv->indicator_width;
1070 requisition->width += 2 * GTK_CONTAINER (widget)->border_width;
1071 requisition->height += 2 * GTK_CONTAINER (widget)->border_width;
1075 hildon_pannable_area_child_allocate_calculate (GtkWidget * widget,
1076 GtkAllocation * allocation,
1077 GtkAllocation * child_allocation)
1080 HildonPannableAreaPrivate *priv;
1082 border_width = GTK_CONTAINER (widget)->border_width;
1084 priv = HILDON_PANNABLE_AREA (widget)->priv;
1086 child_allocation->x = 0;
1087 child_allocation->y = 0;
1088 child_allocation->width = MAX (allocation->width - 2 * border_width -
1089 (priv->vscroll_visible ? priv->vscroll_rect.width : 0), 0);
1090 child_allocation->height = MAX (allocation->height - 2 * border_width -
1091 (priv->hscroll_visible ? priv->hscroll_rect.height : 0), 0);
1093 if (priv->overshot_dist_y > 0) {
1094 child_allocation->y = MIN (child_allocation->y + priv->overshot_dist_y,
1095 child_allocation->height);
1096 child_allocation->height = MAX (child_allocation->height - priv->overshot_dist_y, 0);
1097 } else if (priv->overshot_dist_y < 0) {
1098 child_allocation->height = MAX (child_allocation->height + priv->overshot_dist_y, 0);
1101 if (priv->overshot_dist_x > 0) {
1102 child_allocation->x = MIN (child_allocation->x + priv->overshot_dist_x,
1103 child_allocation->width);
1104 child_allocation->width = MAX (child_allocation->width - priv->overshot_dist_x, 0);
1105 } else if (priv->overshot_dist_x < 0) {
1106 child_allocation->width = MAX (child_allocation->width + priv->overshot_dist_x, 0);
1111 hildon_pannable_area_size_allocate (GtkWidget * widget,
1112 GtkAllocation * allocation)
1114 GtkAllocation child_allocation;
1115 HildonPannableAreaPrivate *priv;
1116 GtkWidget *child = gtk_bin_get_child (GTK_BIN (widget));
1120 border_width = GTK_CONTAINER (widget)->border_width;
1122 widget->allocation = *allocation;
1124 priv = HILDON_PANNABLE_AREA (widget)->priv;
1126 if (GTK_WIDGET_REALIZED (widget)) {
1127 gdk_window_move_resize (widget->window,
1128 allocation->x + border_width,
1129 allocation->y + border_width,
1130 allocation->width - border_width * 2,
1131 allocation->height - border_width * 2);
1132 gdk_window_move_resize (priv->event_window,
1135 allocation->width - border_width * 2,
1136 allocation->height - border_width * 2);
1139 if (child && GTK_WIDGET_VISIBLE (child)) {
1141 hildon_pannable_area_child_allocate_calculate (widget,
1145 gtk_widget_size_allocate (child, &child_allocation);
1147 if (hildon_pannable_area_check_scrollbars (HILDON_PANNABLE_AREA (widget))) {
1148 hildon_pannable_area_child_allocate_calculate (widget,
1152 gtk_widget_size_allocate (child, &child_allocation);
1155 hv = priv->hadjust->value;
1156 vv = priv->vadjust->value;
1158 /* we have to do this after child size_allocate because page_size is
1159 * changed when we allocate the size of the children */
1160 if (priv->overshot_dist_y < 0) {
1161 priv->vadjust->value = priv->vadjust->upper - priv->vadjust->page_size;
1164 if (priv->overshot_dist_x < 0) {
1165 priv->hadjust->value = priv->hadjust->upper - priv->hadjust->page_size;
1168 if (hv != priv->hadjust->value)
1169 gtk_adjustment_value_changed (priv->hadjust);
1171 if (vv != priv->vadjust->value)
1172 gtk_adjustment_value_changed (priv->vadjust);
1175 hildon_pannable_area_check_scrollbars (HILDON_PANNABLE_AREA (widget));
1180 hildon_pannable_area_style_set (GtkWidget * widget,
1181 GtkStyle * previous_style)
1183 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1185 GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->
1186 style_set (widget, previous_style);
1188 gtk_widget_style_get (widget, "indicator-width", &priv->indicator_width, NULL);
1192 hildon_pannable_area_map (GtkWidget * widget)
1194 HildonPannableAreaPrivate *priv;
1196 priv = HILDON_PANNABLE_AREA (widget)->priv;
1198 gdk_window_show (widget->window);
1200 if (priv->event_window != NULL && !priv->enabled)
1201 gdk_window_show (priv->event_window);
1203 (*GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->map) (widget);
1205 if (priv->event_window != NULL && priv->enabled)
1206 gdk_window_show (priv->event_window);
1210 hildon_pannable_area_unmap (GtkWidget * widget)
1212 HildonPannableAreaPrivate *priv;
1214 priv = HILDON_PANNABLE_AREA (widget)->priv;
1216 if (priv->event_window != NULL)
1217 gdk_window_hide (priv->event_window);
1219 gdk_window_hide (widget->window);
1221 (*GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->unmap) (widget);
1225 hildon_pannable_area_grab_notify (GtkWidget *widget,
1226 gboolean was_grabbed,
1229 /* an internal widget has grabbed the focus and now has returned it,
1230 we have to do some release actions */
1232 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1234 priv->scroll_indicator_event_interrupt = 0;
1236 if ((!priv->scroll_indicator_timeout)&&(priv->scroll_indicator_alpha)>0.1) {
1237 priv->scroll_delay_counter = priv->scrollbar_fade_delay;
1239 hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget),
1240 priv->scroll_indicator_alpha);
1243 priv->last_type = 3;
1244 priv->moved = FALSE;
1248 #if USE_CAIRO_SCROLLBARS == 1
1251 rgb_from_gdkcolor (GdkColor *color, gdouble *r, gdouble *g, gdouble *b)
1253 *r = (color->red >> 8) / 255.0;
1254 *g = (color->green >> 8) / 255.0;
1255 *b = (color->blue >> 8) / 255.0;
1259 hildon_pannable_draw_vscroll (GtkWidget * widget,
1260 GdkColor *back_color,
1261 GdkColor *scroll_color)
1263 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1266 cairo_pattern_t *pattern;
1268 gint radius = (priv->vscroll_rect.width/2) - 1;
1270 cr = gdk_cairo_create(widget->window);
1272 /* Draw the background */
1273 rgb_from_gdkcolor (back_color, &r, &g, &b);
1274 cairo_set_source_rgb (cr, r, g, b);
1275 cairo_rectangle(cr, priv->vscroll_rect.x, priv->vscroll_rect.y,
1276 priv->vscroll_rect.width,
1277 priv->vscroll_rect.height);
1278 cairo_fill_preserve (cr);
1281 /* Calculate the scroll bar height and position */
1282 y = ((priv->vadjust->value - priv->vadjust->lower) / (priv->vadjust->upper - priv->vadjust->lower)) *
1283 (widget->allocation.height -
1284 (priv->hscroll_visible ? priv->indicator_width : 0));
1285 height = ((((priv->vadjust->value - priv->vadjust->lower) +
1286 priv->vadjust->page_size) /
1287 (priv->vadjust->upper - priv->vadjust->lower)) *
1288 (widget->allocation.height -
1289 (priv->hscroll_visible ? priv->indicator_width : 0))) - y;
1291 /* Set a minimum height */
1292 height = MAX (SCROLL_BAR_MIN_SIZE, height);
1294 /* Check the max y position */
1295 y = MIN (y, widget->allocation.height -
1296 (priv->hscroll_visible ? priv->hscroll_rect.height : 0) -
1299 /* Draw the scrollbar */
1300 rgb_from_gdkcolor (scroll_color, &r, &g, &b);
1302 pattern = cairo_pattern_create_linear(radius+1, y, radius+1,y + height);
1303 cairo_pattern_add_color_stop_rgb(pattern, 0, r, g, b);
1304 cairo_pattern_add_color_stop_rgb(pattern, 1, r/2, g/2, b/2);
1305 cairo_set_source(cr, pattern);
1307 cairo_pattern_destroy(pattern);
1309 cairo_arc(cr, priv->vscroll_rect.x + radius + 1, y + radius + 1, radius, G_PI, 0);
1310 cairo_line_to(cr, priv->vscroll_rect.x + (radius * 2) + 1, y + height - radius);
1311 cairo_arc(cr, priv->vscroll_rect.x + radius + 1, y + height - radius, radius, 0, G_PI);
1312 cairo_line_to(cr, priv->vscroll_rect.x + 1, y + height - radius);
1315 cairo_paint_with_alpha(cr, priv->scroll_indicator_alpha);
1321 hildon_pannable_draw_hscroll (GtkWidget * widget,
1322 GdkColor *back_color,
1323 GdkColor *scroll_color)
1325 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1328 cairo_pattern_t *pattern;
1330 gint radius = (priv->hscroll_rect.height/2) - 1;
1332 cr = gdk_cairo_create(widget->window);
1334 /* Draw the background */
1335 rgb_from_gdkcolor (back_color, &r, &g, &b);
1336 cairo_set_source_rgb (cr, r, g, b);
1337 cairo_rectangle(cr, priv->hscroll_rect.x, priv->hscroll_rect.y,
1338 priv->hscroll_rect.width,
1339 priv->hscroll_rect.height);
1340 cairo_fill_preserve (cr);
1343 /* calculate the scrollbar width and position */
1344 x = ((priv->hadjust->value - priv->hadjust->lower) / (priv->hadjust->upper - priv->hadjust->lower)) *
1345 (widget->allocation.width - (priv->vscroll_visible ? priv->indicator_width : 0));
1346 width =((((priv->hadjust->value - priv->hadjust->lower) +
1347 priv->hadjust->page_size) / (priv->hadjust->upper - priv->hadjust->lower)) *
1348 (widget->allocation.width -
1349 (priv->vscroll_visible ? priv->indicator_width : 0))) - x;
1351 /* Set a minimum width */
1352 width = MAX (SCROLL_BAR_MIN_SIZE, width);
1354 /* Check the max x position */
1355 x = MIN (x, widget->allocation.width -
1356 (priv->vscroll_visible ? priv->vscroll_rect.width : 0) -
1359 /* Draw the scrollbar */
1360 rgb_from_gdkcolor (scroll_color, &r, &g, &b);
1362 pattern = cairo_pattern_create_linear(x, radius+1, x+width, radius+1);
1363 cairo_pattern_add_color_stop_rgb(pattern, 0, r, g, b);
1364 cairo_pattern_add_color_stop_rgb(pattern, 1, r/2, g/2, b/2);
1365 cairo_set_source(cr, pattern);
1367 cairo_pattern_destroy(pattern);
1369 cairo_arc_negative(cr, x + radius + 1, priv->hscroll_rect.y + radius + 1, radius, 3*G_PI_2, G_PI_2);
1370 cairo_line_to(cr, x + width - radius, priv->hscroll_rect.y + (radius * 2) + 1);
1371 cairo_arc_negative(cr, x + width - radius, priv->hscroll_rect.y + radius + 1, radius, G_PI_2, 3*G_PI_2);
1372 cairo_line_to(cr, x + width - radius, priv->hscroll_rect.y + 1);
1375 cairo_paint_with_alpha(cr, priv->scroll_indicator_alpha);
1380 #else /* USE_CAIRO_SCROLLBARS */
1383 tranparency_color (GdkColor *color,
1386 gdouble transparency)
1390 diff = colora.red - colorb.red;
1391 color->red = colora.red-diff*transparency;
1393 diff = colora.green - colorb.green;
1394 color->green = colora.green-diff*transparency;
1396 diff = colora.blue - colorb.blue;
1397 color->blue = colora.blue-diff*transparency;
1401 hildon_pannable_draw_vscroll (GtkWidget *widget,
1402 GdkColor *back_color,
1403 GdkColor *scroll_color)
1405 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1407 GdkColor transp_color;
1408 GdkGC *gc = widget->style->fg_gc[GTK_STATE_INSENSITIVE];
1410 gdk_draw_rectangle (widget->window,
1411 widget->style->bg_gc[GTK_STATE_NORMAL],
1413 priv->vscroll_rect.x, priv->vscroll_rect.y,
1414 priv->vscroll_rect.width,
1415 priv->vscroll_rect.height);
1417 y = ((priv->vadjust->value - priv->vadjust->lower) / (priv->vadjust->upper - priv->vadjust->lower)) *
1418 (widget->allocation.height - (priv->hscroll_visible ? priv->indicator_width : 0));
1419 height = ((((priv->vadjust->value - priv->vadjust->lower) + priv->vadjust->page_size) /
1420 (priv->vadjust->upper - priv->vadjust->lower)) *
1421 (widget->allocation.height -
1422 (priv->hscroll_visible ? priv->indicator_width : 0))) - y;
1424 /* Set a minimum height */
1425 height = MAX (SCROLL_BAR_MIN_SIZE, height);
1427 /* Check the max y position */
1428 y = MIN (y, widget->allocation.height -
1429 (priv->hscroll_visible ? priv->hscroll_rect.height : 0) -
1432 if (priv->scroll_indicator_alpha < 1.0) {
1433 tranparency_color (&transp_color, *back_color, *scroll_color,
1434 priv->scroll_indicator_alpha);
1436 gdk_gc_set_rgb_fg_color (priv->scrollbars_gc, &transp_color);
1438 gc = priv->scrollbars_gc;
1441 gdk_draw_rectangle (widget->window, gc,
1442 TRUE, priv->vscroll_rect.x, y,
1443 priv->vscroll_rect.width, height);
1447 hildon_pannable_draw_hscroll (GtkWidget *widget,
1448 GdkColor *back_color,
1449 GdkColor *scroll_color)
1451 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1453 GdkColor transp_color;
1454 GdkGC *gc = widget->style->fg_gc[GTK_STATE_INSENSITIVE];
1456 gdk_draw_rectangle (widget->window,
1457 widget->style->bg_gc[GTK_STATE_INSENSITIVE],
1459 priv->hscroll_rect.x, priv->hscroll_rect.y,
1460 priv->hscroll_rect.width,
1461 priv->hscroll_rect.height);
1463 /* calculate the scrollbar width and position */
1464 x = ((priv->hadjust->value - priv->hadjust->lower) / (priv->hadjust->upper - priv->hadjust->lower)) *
1465 (widget->allocation.width - (priv->vscroll_visible ? priv->indicator_width : 0));
1466 width =((((priv->hadjust->value - priv->hadjust->lower) +
1467 priv->hadjust->page_size) / (priv->hadjust->upper - priv->hadjust->lower)) *
1468 (widget->allocation.width -
1469 (priv->vscroll_visible ? priv->indicator_width : 0))) - x;
1471 /* Set a minimum width */
1472 width = MAX (SCROLL_BAR_MIN_SIZE, width);
1474 /* Check the max x position */
1475 x = MIN (x, widget->allocation.width -
1476 (priv->vscroll_visible ? priv->vscroll_rect.width : 0) -
1479 if (priv->scroll_indicator_alpha < 1.0) {
1480 tranparency_color (&transp_color, *back_color, *scroll_color,
1481 priv->scroll_indicator_alpha);
1483 gdk_gc_set_rgb_fg_color (priv->scrollbars_gc, &transp_color);
1485 gc = priv->scrollbars_gc;
1488 gdk_draw_rectangle (widget->window, gc,
1489 TRUE, x, priv->hscroll_rect.y, width,
1490 priv->hscroll_rect.height);
1493 #endif /* USE_CAIRO_SCROLLBARS */
1496 hildon_pannable_area_initial_effect (GtkWidget * widget)
1498 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1500 if (priv->initial_hint) {
1501 if (priv->vscroll_visible || priv->hscroll_visible) {
1503 priv->scroll_indicator_event_interrupt = 0;
1504 priv->scroll_delay_counter = priv->scrollbar_fade_delay;
1506 hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget), 1.0);
1512 hildon_pannable_area_launch_fade_timeout (HildonPannableArea * area,
1515 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (area)->priv;
1517 priv->scroll_indicator_alpha = alpha;
1519 if (!priv->scroll_indicator_timeout)
1520 priv->scroll_indicator_timeout =
1521 gdk_threads_add_timeout (SCROLL_FADE_TIMEOUT,
1522 (GSourceFunc) hildon_pannable_area_scroll_indicator_fade,
1527 hildon_pannable_area_adjust_changed (HildonPannableArea * area,
1530 if (GTK_WIDGET_REALIZED (area))
1531 hildon_pannable_area_refresh (area);
1535 hildon_pannable_area_adjust_value_changed (HildonPannableArea * area,
1538 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (area)->priv;
1540 gint x = priv->x_offset;
1541 gint y = priv->y_offset;
1543 priv->x_offset = priv->hadjust->value;
1544 xdiff = x - priv->x_offset;
1545 priv->y_offset = priv->vadjust->value;
1546 ydiff = y - priv->y_offset;
1548 if ((xdiff || ydiff) && GTK_WIDGET_DRAWABLE (area)) {
1549 hildon_pannable_area_redraw (area);
1551 if ((priv->vscroll_visible) || (priv->hscroll_visible)) {
1552 priv->scroll_indicator_event_interrupt = 0;
1553 priv->scroll_delay_counter = priv->scrollbar_fade_delay;
1555 hildon_pannable_area_launch_fade_timeout (area, 1.0);
1561 hildon_pannable_area_redraw (HildonPannableArea * area)
1563 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (area)->priv;
1565 /* Redraw scroll indicators */
1566 if (GTK_WIDGET_DRAWABLE (area)) {
1567 if (priv->hscroll_visible) {
1568 gdk_window_invalidate_rect (GTK_WIDGET (area)->window,
1569 &priv->hscroll_rect, FALSE);
1572 if (priv->vscroll_visible) {
1573 gdk_window_invalidate_rect (GTK_WIDGET (area)->window,
1574 &priv->vscroll_rect, FALSE);
1580 hildon_pannable_area_scroll_indicator_fade(HildonPannableArea * area)
1582 HildonPannableAreaPrivate *priv = area->priv;
1584 /* if moving do not fade out */
1585 if (((ABS (priv->vel_y)>priv->vmin)||
1586 (ABS (priv->vel_x)>priv->vmin))&&(!priv->button_pressed)) {
1591 if (priv->scroll_indicator_event_interrupt) {
1592 /* Stop a fade out, and fade back in */
1593 if (priv->scroll_indicator_alpha > 0.9) {
1594 priv->scroll_indicator_alpha = 1.0;
1595 priv->scroll_indicator_timeout = 0;
1599 priv->scroll_indicator_alpha += 0.2;
1600 hildon_pannable_area_redraw (area);
1606 if ((priv->scroll_indicator_alpha > 0.9) &&
1607 (priv->scroll_delay_counter > 0)) {
1608 priv->scroll_delay_counter--;
1613 if (!priv->scroll_indicator_event_interrupt) {
1614 /* Continue fade out */
1615 if (priv->scroll_indicator_alpha < 0.1) {
1616 priv->scroll_indicator_timeout = 0;
1617 priv->scroll_indicator_alpha = 0.0;
1621 priv->scroll_indicator_alpha -= 0.2;
1622 hildon_pannable_area_redraw (area);
1632 hildon_pannable_area_expose_event (GtkWidget * widget,
1633 GdkEventExpose * event)
1636 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1637 #if USE_CAIRO_SCROLLBARS == 1
1638 GdkColor back_color = widget->style->bg[GTK_STATE_NORMAL];
1639 GdkColor scroll_color = widget->style->base[GTK_STATE_SELECTED];
1640 #else /* USE_CAIRO_SCROLLBARS */
1641 GdkColor back_color = widget->style->bg[GTK_STATE_NORMAL];
1642 GdkColor scroll_color = widget->style->fg[GTK_STATE_INSENSITIVE];
1645 if (G_UNLIKELY (priv->initial_effect)) {
1646 hildon_pannable_area_initial_effect (widget);
1648 priv->initial_effect = FALSE;
1651 if (gtk_bin_get_child (GTK_BIN (widget))) {
1653 if (priv->scroll_indicator_alpha > 0.1) {
1654 if (priv->vscroll_visible) {
1655 hildon_pannable_draw_vscroll (widget, &back_color, &scroll_color);
1657 if (priv->hscroll_visible) {
1658 hildon_pannable_draw_hscroll (widget, &back_color, &scroll_color);
1662 /* draw overshooting rectangles */
1663 if (priv->overshot_dist_y > 0) {
1664 gint overshot_height;
1666 overshot_height = MIN (priv->overshot_dist_y, widget->allocation.height -
1667 (priv->hscroll_visible ? priv->hscroll_rect.height : 0));
1669 gdk_draw_rectangle (widget->window,
1670 widget->style->bg_gc[GTK_STATE_NORMAL],
1674 widget->allocation.width -
1675 (priv->vscroll_visible ? priv->vscroll_rect.width : 0),
1677 } else if (priv->overshot_dist_y < 0) {
1678 gint overshot_height;
1682 MAX (priv->overshot_dist_y,
1683 -(widget->allocation.height -
1684 (priv->hscroll_visible ? priv->hscroll_rect.height : 0)));
1686 overshot_y = MAX (widget->allocation.height +
1688 (priv->hscroll_visible ? priv->hscroll_rect.height : 0), 0);
1690 gdk_draw_rectangle (widget->window,
1691 widget->style->bg_gc[GTK_STATE_NORMAL],
1695 widget->allocation.width -
1696 priv->vscroll_rect.width,
1700 if (priv->overshot_dist_x > 0) {
1701 gint overshot_width;
1703 overshot_width = MIN (priv->overshot_dist_x, widget->allocation.width -
1704 (priv->vscroll_visible ? priv->vscroll_rect.width : 0));
1706 gdk_draw_rectangle (widget->window,
1707 widget->style->bg_gc[GTK_STATE_NORMAL],
1712 widget->allocation.height -
1713 (priv->hscroll_visible ? priv->hscroll_rect.height : 0));
1714 } else if (priv->overshot_dist_x < 0) {
1715 gint overshot_width;
1719 MAX (priv->overshot_dist_x,
1720 -(widget->allocation.width -
1721 (priv->vscroll_visible ? priv->vscroll_rect.width : 0)));
1723 overshot_x = MAX (widget->allocation.width +
1725 (priv->vscroll_visible ? priv->vscroll_rect.width : 0), 0);
1727 gdk_draw_rectangle (widget->window,
1728 widget->style->bg_gc[GTK_STATE_NORMAL],
1733 widget->allocation.height -
1734 priv->hscroll_rect.height);
1739 return GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->expose_event (widget, event);
1743 hildon_pannable_area_get_topmost (GdkWindow * window,
1745 gint * tx, gint * ty,
1748 /* Find the GdkWindow at the given point, by recursing from a given
1749 * parent GdkWindow. Optionally return the co-ordinates transformed
1750 * relative to the child window.
1753 GList *c, *children;
1754 GdkWindow *selected_window = NULL;
1756 gdk_drawable_get_size (GDK_DRAWABLE (window), &width, &height);
1757 if ((x < 0) || (x >= width) || (y < 0) || (y >= height))
1760 children = gdk_window_peek_children (window);
1767 selected_window = window;
1770 for (c = children; c; c = c->next) {
1771 GdkWindow *child = (GdkWindow *) c->data;
1774 gdk_drawable_get_size (GDK_DRAWABLE (child), &width, &height);
1775 gdk_window_get_position (child, &wx, &wy);
1777 if ((x >= wx) && (x < (wx + width)) && (y >= wy) && (y < (wy + height)) &&
1778 (gdk_window_is_visible (child))) {
1780 if (gdk_window_peek_children (child)) {
1781 selected_window = hildon_pannable_area_get_topmost (child, x-wx, y-wy,
1783 if (!selected_window) {
1788 selected_window = child;
1791 if ((gdk_window_get_events (child)&mask)) {
1796 selected_window = child;
1802 return selected_window;
1806 synth_crossing (GdkWindow * child,
1808 gint x_root, gint y_root,
1809 guint32 time, gboolean in)
1811 GdkEventCrossing *crossing_event;
1812 GdkEventType type = in ? GDK_ENTER_NOTIFY : GDK_LEAVE_NOTIFY;
1814 /* Send synthetic enter event */
1815 crossing_event = (GdkEventCrossing *) gdk_event_new (type);
1816 ((GdkEventAny *) crossing_event)->type = type;
1817 ((GdkEventAny *) crossing_event)->window = g_object_ref (child);
1818 ((GdkEventAny *) crossing_event)->send_event = FALSE;
1819 crossing_event->subwindow = g_object_ref (child);
1820 crossing_event->time = time;
1821 crossing_event->x = x;
1822 crossing_event->y = y;
1823 crossing_event->x_root = x_root;
1824 crossing_event->y_root = y_root;
1825 crossing_event->mode = GDK_CROSSING_NORMAL;
1826 crossing_event->detail = GDK_NOTIFY_UNKNOWN;
1827 crossing_event->focus = FALSE;
1828 crossing_event->state = 0;
1829 gdk_event_put ((GdkEvent *) crossing_event);
1830 gdk_event_free ((GdkEvent *) crossing_event);
1834 hildon_pannable_area_button_press_cb (GtkWidget * widget,
1835 GdkEventButton * event)
1838 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1840 if ((!priv->enabled) || (event->button != 1) ||
1841 ((event->time == priv->last_time) &&
1842 (priv->last_type == 1)) || (gtk_bin_get_child (GTK_BIN (widget)) == NULL))
1845 priv->scroll_indicator_event_interrupt = 1;
1847 hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget),
1848 priv->scroll_indicator_alpha);
1850 priv->last_time = event->time;
1851 priv->last_type = 1;
1853 priv->scroll_to_x = -1;
1854 priv->scroll_to_y = -1;
1856 if (priv->button_pressed && priv->child) {
1857 /* Widget stole focus on last click, send crossing-out event */
1858 synth_crossing (priv->child, 0, 0, event->x_root, event->y_root,
1859 event->time, FALSE);
1867 /* Don't allow a click if we're still moving fast */
1868 if ((ABS (priv->vel_x) <= (priv->vmax * priv->vfast_factor)) &&
1869 (ABS (priv->vel_y) <= (priv->vmax * priv->vfast_factor)))
1871 hildon_pannable_area_get_topmost (gtk_bin_get_child (GTK_BIN (widget))->window,
1872 event->x, event->y, &x, &y, GDK_BUTTON_PRESS_MASK);
1876 priv->button_pressed = TRUE;
1878 /* Stop scrolling on mouse-down (so you can flick, then hold to stop) */
1884 gdk_drawable_get_size (priv->child, &priv->child_width,
1885 &priv->child_height);
1886 priv->last_in = TRUE;
1888 g_object_add_weak_pointer ((GObject *) priv->child,
1889 (gpointer) & priv->child);
1891 event = (GdkEventButton *) gdk_event_copy ((GdkEvent *) event);
1897 synth_crossing (priv->child, x, y, event->x_root,
1898 event->y_root, event->time, TRUE);
1900 /* Send synthetic click (button press/release) event */
1901 ((GdkEventAny *) event)->window = g_object_ref (priv->child);
1903 gdk_event_put ((GdkEvent *) event);
1904 gdk_event_free ((GdkEvent *) event);
1912 hildon_pannable_area_check_scrollbars (HildonPannableArea * area)
1914 HildonPannableAreaPrivate *priv = area->priv;
1915 gboolean prev_hscroll_visible, prev_vscroll_visible;
1917 prev_hscroll_visible = priv->hscroll_visible;
1918 prev_vscroll_visible = priv->vscroll_visible;
1920 if (!gtk_bin_get_child (GTK_BIN (area))) {
1921 priv->vscroll_visible = FALSE;
1922 priv->hscroll_visible = FALSE;
1924 switch (priv->hscrollbar_policy) {
1925 case GTK_POLICY_ALWAYS:
1926 priv->hscroll_visible = TRUE;
1928 case GTK_POLICY_NEVER:
1929 priv->hscroll_visible = FALSE;
1932 priv->hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
1933 priv->hadjust->page_size);
1936 switch (priv->vscrollbar_policy) {
1937 case GTK_POLICY_ALWAYS:
1938 priv->vscroll_visible = TRUE;
1940 case GTK_POLICY_NEVER:
1941 priv->vscroll_visible = FALSE;
1944 priv->vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
1945 priv->vadjust->page_size);
1948 /* Store the vscroll/hscroll areas for redrawing */
1949 if (priv->vscroll_visible) {
1950 GtkAllocation *allocation = >K_WIDGET (area)->allocation;
1951 priv->vscroll_rect.x = allocation->width - priv->indicator_width;
1952 priv->vscroll_rect.y = 0;
1953 priv->vscroll_rect.width = priv->indicator_width;
1954 priv->vscroll_rect.height = allocation->height -
1955 (priv->hscroll_visible ? priv->indicator_width : 0);
1957 if (priv->hscroll_visible) {
1958 GtkAllocation *allocation = >K_WIDGET (area)->allocation;
1959 priv->hscroll_rect.y = allocation->height - priv->indicator_width;
1960 priv->hscroll_rect.x = 0;
1961 priv->hscroll_rect.height = priv->indicator_width;
1962 priv->hscroll_rect.width = allocation->width -
1963 (priv->vscroll_visible ? priv->indicator_width : 0);
1967 return ((priv->hscroll_visible != prev_hscroll_visible) ||
1968 (priv->vscroll_visible != prev_vscroll_visible));
1972 hildon_pannable_area_refresh (HildonPannableArea * area)
1974 if (GTK_WIDGET_DRAWABLE (area) &&
1975 hildon_pannable_area_check_scrollbars (area)) {
1976 HildonPannableAreaPrivate *priv = area->priv;
1978 gtk_widget_queue_resize (GTK_WIDGET (area));
1980 if ((priv->vscroll_visible) || (priv->hscroll_visible)) {
1981 priv->scroll_indicator_event_interrupt = 0;
1982 priv->scroll_delay_counter = area->priv->scrollbar_fade_delay;
1984 hildon_pannable_area_launch_fade_timeout (area, 1.0);
1987 hildon_pannable_area_redraw (area);
1991 /* Scroll by a particular amount (in pixels). Optionally, return if
1992 * the scroll on a particular axis was successful.
1995 hildon_pannable_axis_scroll (HildonPannableArea *area,
1996 GtkAdjustment *adjust,
2000 gint *overshot_dist,
2006 HildonPannableAreaPrivate *priv = area->priv;
2008 dist = gtk_adjustment_get_value (adjust) - inc;
2011 * We use overshot_dist to define the distance of the current overshoot,
2012 * and overshooting to define the direction/whether or not we are overshot
2014 if (!(*overshooting)) {
2016 /* Initiation of the overshoot happens when the finger is released
2017 * and the current position of the pannable contents are out of range
2019 if (dist < adjust->lower) {
2022 dist = adjust->lower;
2024 if (overshoot_max!=0) {
2027 *overshot_dist = CLAMP (*overshot_dist + *vel, 0, overshoot_max);
2028 *vel = MIN (priv->vmax_overshooting, *vel);
2029 gtk_widget_queue_resize (GTK_WIDGET (area));
2033 } else if (dist > adjust->upper - adjust->page_size) {
2036 dist = adjust->upper - adjust->page_size;
2038 if (overshoot_max!=0) {
2041 *overshot_dist = CLAMP (*overshot_dist + *vel, -overshoot_max, 0);
2042 *vel = MAX (-priv->vmax_overshooting, *vel);
2043 gtk_widget_queue_resize (GTK_WIDGET (area));
2048 if ((*scroll_to) != -1) {
2049 if (((inc < 0)&&(*scroll_to <= dist))||
2050 ((inc > 0)&&(*scroll_to >= dist))) {
2058 adjust->value = dist;
2060 if (!priv->button_pressed) {
2062 /* When the overshoot has started we continue for
2063 * PROP_BOUNCE_STEPS more steps into the overshoot before we
2064 * reverse direction. The deceleration factor is calculated
2065 * based on the percentage distance from the first item with
2066 * each iteration, therefore always returning us to the
2067 * top/bottom most element
2069 if (*overshot_dist > 0) {
2071 if ((*overshooting < priv->bounce_steps) && (*vel > 0)) {
2073 *vel = (((gdouble)*overshot_dist)/overshoot_max) * (*vel);
2074 } else if ((*overshooting >= priv->bounce_steps) && (*vel > 0)) {
2076 } else if ((*overshooting > 1) && (*vel < 0)) {
2077 /* we add the MIN in order to avoid very small speeds */
2078 *vel = MIN ((((gdouble)*overshot_dist)*0.4) * -1, -2.0);
2081 *overshot_dist = CLAMP (*overshot_dist + *vel, 0, overshoot_max);
2083 gtk_widget_queue_resize (GTK_WIDGET (area));
2085 } else if (*overshot_dist < 0) {
2087 if ((*overshooting < priv->bounce_steps) && (*vel < 0)) {
2089 *vel = (((gdouble)*overshot_dist)/overshoot_max) * (*vel) * -1;
2090 } else if ((*overshooting >= priv->bounce_steps) && (*vel < 0)) {
2092 } else if ((*overshooting > 1) && (*vel > 0)) {
2093 /* we add the MAX in order to avoid very small speeds */
2094 *vel = MAX ((((gdouble)*overshot_dist)*0.4) * -1, 2.0);
2097 *overshot_dist = CLAMP (*overshot_dist + (*vel), -overshoot_max, 0);
2099 gtk_widget_queue_resize (GTK_WIDGET (area));
2104 gtk_widget_queue_resize (GTK_WIDGET (area));
2108 gint overshot_dist_old = *overshot_dist;
2110 if (*overshot_dist > 0) {
2111 *overshot_dist = CLAMP ((*overshot_dist) + inc, 0, overshoot_max);
2112 } else if (*overshot_dist < 0) {
2113 *overshot_dist = CLAMP ((*overshot_dist) + inc, -1 * overshoot_max, 0);
2116 adjust->value = CLAMP (dist,
2122 if (*overshot_dist != overshot_dist_old)
2123 gtk_widget_queue_resize (GTK_WIDGET (area));
2129 hildon_pannable_area_scroll (HildonPannableArea *area,
2130 gdouble x, gdouble y)
2133 HildonPannableAreaPrivate *priv = area->priv;
2134 gboolean hscroll_visible, vscroll_visible;
2137 if (gtk_bin_get_child (GTK_BIN (area)) == NULL)
2140 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
2141 priv->vadjust->page_size);
2142 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
2143 priv->hadjust->page_size);
2148 hv = priv->hadjust->value;
2149 vv = priv->vadjust->value;
2151 if (vscroll_visible) {
2152 hildon_pannable_axis_scroll (area, priv->vadjust, &priv->vel_y, y,
2153 &priv->overshooting_y, &priv->overshot_dist_y,
2154 &priv->scroll_to_y, priv->vovershoot_max, &sy);
2159 if (hscroll_visible) {
2160 hildon_pannable_axis_scroll (area, priv->hadjust, &priv->vel_x, x,
2161 &priv->overshooting_x, &priv->overshot_dist_x,
2162 &priv->scroll_to_x, priv->hovershoot_max, &sx);
2167 if (hv != priv->hadjust->value)
2168 gtk_adjustment_value_changed (priv->hadjust);
2170 if (vv != priv->vadjust->value)
2171 gtk_adjustment_value_changed (priv->vadjust);
2173 /* If the scroll on a particular axis wasn't succesful, reset the
2174 * initial scroll position to the new mouse co-ordinate. This means
2175 * when you get to the top of the page, dragging down works immediately.
2177 if (priv->mode == HILDON_PANNABLE_AREA_MODE_ACCEL) {
2189 hildon_pannable_area_timeout (HildonPannableArea * area)
2191 HildonPannableAreaPrivate *priv = area->priv;
2193 if ((!priv->enabled) || (priv->mode == HILDON_PANNABLE_AREA_MODE_PUSH)) {
2199 if (!priv->button_pressed) {
2200 /* Decelerate gradually when pointer is raised */
2201 if ((!priv->overshot_dist_y) &&
2202 (!priv->overshot_dist_x)) {
2204 /* in case we move to a specific point do not decelerate when arriving */
2205 if ((priv->scroll_to_x != -1)||(priv->scroll_to_y != -1)) {
2207 if (ABS (priv->vel_x) >= 1.5) {
2208 priv->vel_x *= priv->decel;
2211 if (ABS (priv->vel_y) >= 1.5) {
2212 priv->vel_y *= priv->decel;
2216 if ((!priv->low_friction_mode) ||
2217 ((priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ) &&
2218 (ABS (priv->vel_x) < 0.8*priv->vmax)))
2219 priv->vel_x *= priv->decel;
2221 if ((!priv->low_friction_mode) ||
2222 ((priv->mov_mode&HILDON_MOVEMENT_MODE_VERT) &&
2223 (ABS (priv->vel_y) < 0.8*priv->vmax)))
2224 priv->vel_y *= priv->decel;
2226 if ((ABS (priv->vel_x) < 1.0) && (ABS (priv->vel_y) < 1.0)) {
2235 } else if (priv->mode == HILDON_PANNABLE_AREA_MODE_AUTO) {
2241 hildon_pannable_area_scroll (area, priv->vel_x, priv->vel_y);
2247 hildon_pannable_area_calculate_velocity (gdouble *vel,
2251 gdouble drag_inertia,
2257 if (ABS (dist) >= RATIO_TOLERANCE) {
2258 rawvel = (dist / ABS (delta)) * force;
2259 *vel = *vel * (1 - drag_inertia) +
2260 rawvel * drag_inertia;
2261 *vel = *vel > 0 ? MIN (*vel, vmax)
2262 : MAX (*vel, -1 * vmax);
2267 hildon_pannable_area_motion_event_scroll_timeout (HildonPannableArea *area)
2269 HildonPannableAreaPrivate *priv = area->priv;
2271 if ((priv->motion_x != 0)||(priv->motion_y != 0))
2272 hildon_pannable_area_scroll (area, priv->motion_x, priv->motion_y);
2274 priv->motion_event_scroll_timeout = 0;
2280 hildon_pannable_area_motion_event_scroll (HildonPannableArea *area,
2281 gdouble x, gdouble y)
2283 HildonPannableAreaPrivate *priv = area->priv;
2285 if (priv->motion_event_scroll_timeout) {
2287 priv->motion_x += x;
2288 priv->motion_y += y;
2292 /* we do not delay the first event but the next ones */
2293 hildon_pannable_area_scroll (area, x, y);
2298 priv->motion_event_scroll_timeout = gdk_threads_add_timeout
2299 ((gint) (1000.0 / (gdouble) MOTION_EVENTS_PER_SECOND),
2300 (GSourceFunc) hildon_pannable_area_motion_event_scroll_timeout, area);
2305 hildon_pannable_area_motion_notify_cb (GtkWidget * widget,
2306 GdkEventMotion * event)
2308 HildonPannableArea *area = HILDON_PANNABLE_AREA (widget);
2309 HildonPannableAreaPrivate *priv = area->priv;
2313 if (gtk_bin_get_child (GTK_BIN (widget)) == NULL)
2316 if ((!priv->enabled) || (!priv->button_pressed) ||
2317 ((event->time == priv->last_time) && (priv->last_type == 2))) {
2318 gdk_window_get_pointer (widget->window, NULL, NULL, 0);
2322 if (priv->last_type == 1) {
2323 priv->first_drag = TRUE;
2326 x = event->x - priv->x;
2327 y = event->y - priv->y;
2329 if (priv->first_drag && (!priv->moved) &&
2330 ((ABS (x) > (priv->panning_threshold))
2331 || (ABS (y) > (priv->panning_threshold)))) {
2336 if (priv->first_drag) {
2337 gboolean vscroll_visible;
2338 gboolean hscroll_visible;
2340 if (ABS (priv->iy - event->y) >=
2341 ABS (priv->ix - event->x)) {
2343 g_signal_emit (area,
2344 pannable_area_signals[VERTICAL_MOVEMENT],
2345 0, (priv->iy > event->y) ?
2346 HILDON_MOVEMENT_UP :
2347 HILDON_MOVEMENT_DOWN,
2348 (gdouble)priv->ix, (gdouble)priv->iy);
2350 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
2351 priv->vadjust->page_size);
2353 if (!((vscroll_visible)&&
2354 (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT))) {
2356 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
2357 priv->hadjust->page_size);
2359 /* even in case we do not have to move we check if this
2360 could be a fake horizontal movement */
2361 if (!((hscroll_visible)&&
2362 (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ)) ||
2363 (ABS (priv->iy - event->y) -
2364 ABS (priv->ix - event->x) >= priv->direction_error_margin))
2365 priv->moved = FALSE;
2369 g_signal_emit (area,
2370 pannable_area_signals[HORIZONTAL_MOVEMENT],
2371 0, (priv->ix > event->x) ?
2372 HILDON_MOVEMENT_LEFT :
2373 HILDON_MOVEMENT_RIGHT,
2374 (gdouble)priv->ix, (gdouble)priv->iy);
2376 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
2377 priv->hadjust->page_size);
2379 if (!((hscroll_visible)&&
2380 (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ))) {
2382 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
2383 priv->vadjust->page_size);
2385 /* even in case we do not have to move we check if this
2386 could be a fake vertical movement */
2387 if (!((vscroll_visible) &&
2388 (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT)) ||
2389 (ABS (priv->ix - event->x) -
2390 ABS (priv->iy - event->y) >= priv->direction_error_margin))
2391 priv->moved = FALSE;
2395 if ((priv->moved)&&(priv->child)) {
2398 pos_x = priv->cx + (event->x - priv->ix);
2399 pos_y = priv->cy + (event->y - priv->iy);
2401 synth_crossing (priv->child, pos_x, pos_y, event->x_root,
2402 event->y_root, event->time, FALSE);
2406 priv->first_drag = FALSE;
2408 if ((priv->mode != HILDON_PANNABLE_AREA_MODE_PUSH) &&
2409 (priv->mode != HILDON_PANNABLE_AREA_MODE_AUTO)) {
2412 priv->idle_id = gdk_threads_add_timeout ((gint)
2413 (1000.0 / (gdouble) priv->sps),
2415 hildon_pannable_area_timeout, area);
2420 switch (priv->mode) {
2421 case HILDON_PANNABLE_AREA_MODE_PUSH:
2422 /* Scroll by the amount of pixels the cursor has moved
2423 * since the last motion event.
2425 hildon_pannable_area_motion_event_scroll (area, x, y);
2429 case HILDON_PANNABLE_AREA_MODE_ACCEL:
2430 /* Set acceleration relative to the initial click */
2431 priv->ex = event->x;
2432 priv->ey = event->y;
2433 priv->vel_x = ((x > 0) ? 1 : -1) *
2435 (gdouble) widget->allocation.width) *
2436 (priv->vmax - priv->vmin)) + priv->vmin);
2437 priv->vel_y = ((y > 0) ? 1 : -1) *
2439 (gdouble) widget->allocation.height) *
2440 (priv->vmax - priv->vmin)) + priv->vmin);
2442 case HILDON_PANNABLE_AREA_MODE_AUTO:
2444 delta = event->time - priv->last_time;
2446 if (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT) {
2447 gdouble dist = event->y - priv->y;
2449 hildon_pannable_area_calculate_velocity (&priv->vel_y,
2461 if (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ) {
2462 gdouble dist = event->x - priv->x;
2464 hildon_pannable_area_calculate_velocity (&priv->vel_x,
2476 hildon_pannable_area_motion_event_scroll (area, x, y);
2478 if (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ)
2480 if (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT)
2488 } else if (priv->child) {
2492 pos_x = priv->cx + (event->x - priv->ix);
2493 pos_y = priv->cy + (event->y - priv->iy);
2495 in = (((0 <= pos_x)&&(priv->child_width >= pos_x)) &&
2496 ((0 <= pos_y)&&(priv->child_height >= pos_y)));
2498 if (((!priv->last_in)&&in)||((priv->last_in)&&(!in))) {
2500 synth_crossing (priv->child, pos_x, pos_y, event->x_root,
2501 event->y_root, event->time, in);
2507 priv->last_time = event->time;
2508 priv->last_type = 2;
2511 /* Send motion notify to child */
2512 event = (GdkEventMotion *) gdk_event_copy ((GdkEvent *) event);
2513 event->x = priv->cx + (event->x - priv->ix);
2514 event->y = priv->cy + (event->y - priv->iy);
2515 event->window = g_object_ref (priv->child);
2516 gdk_event_put ((GdkEvent *) event);
2517 gdk_event_free ((GdkEvent *) event);
2520 gdk_window_get_pointer (widget->window, NULL, NULL, 0);
2526 hildon_pannable_leave_notify_event (GtkWidget *widget,
2527 GdkEventCrossing *event)
2529 HildonPannableArea *area = HILDON_PANNABLE_AREA (widget);
2530 HildonPannableAreaPrivate *priv = area->priv;
2532 if ((priv->child)&&(priv->last_in)) {
2533 priv->last_in = FALSE;
2535 synth_crossing (priv->child, 0, 0, event->x_root,
2536 event->y_root, event->time, FALSE);
2543 hildon_pannable_area_button_release_cb (GtkWidget * widget,
2544 GdkEventButton * event)
2546 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
2550 if (((event->time == priv->last_time) && (priv->last_type == 3))
2551 || (gtk_bin_get_child (GTK_BIN (widget)) == NULL)
2552 || (!priv->button_pressed) || (!priv->enabled) || (event->button != 1))
2555 priv->scroll_indicator_event_interrupt = 0;
2556 priv->scroll_delay_counter = priv->scrollbar_fade_delay;
2558 /* move all the way to the last position */
2559 if (priv->motion_event_scroll_timeout) {
2560 g_source_remove (priv->motion_event_scroll_timeout);
2561 hildon_pannable_area_motion_event_scroll_timeout (HILDON_PANNABLE_AREA (widget));
2566 if (priv->last_type == 2) {
2567 gdouble delta = event->time - priv->last_time;
2569 if (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT) {
2570 gdouble dist = event->y - priv->y;
2572 if (ABS (dist) >= 1.0) {
2573 hildon_pannable_area_calculate_velocity (&priv->vel_y,
2581 priv->motion_y = dist;
2582 hildon_pannable_area_motion_event_scroll_timeout (HILDON_PANNABLE_AREA (widget));
2584 if (delta >= CURSOR_STOPPED_TIMEOUT) {
2590 if (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ) {
2591 gdouble dist = event->x - priv->x;
2593 if (ABS (dist) >= 1.0) {
2594 hildon_pannable_area_calculate_velocity (&priv->vel_x,
2601 priv->motion_x = dist;
2602 hildon_pannable_area_motion_event_scroll_timeout (HILDON_PANNABLE_AREA (widget));
2604 if (delta >= CURSOR_STOPPED_TIMEOUT) {
2611 if ((ABS (priv->vel_y) > priv->vmin)||
2612 (ABS (priv->vel_x) > priv->vmin)) {
2613 priv->scroll_indicator_alpha = 1.0;
2616 hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget),
2617 priv->scroll_indicator_alpha);
2619 priv->button_pressed = FALSE;
2621 if (priv->mode == HILDON_PANNABLE_AREA_MODE_AUTO ||
2622 priv->mode == HILDON_PANNABLE_AREA_MODE_ACCEL) {
2624 /* If overshoot has been initiated with a finger down, on release set max speed */
2625 if (priv->overshot_dist_y != 0) {
2626 priv->overshooting_y = priv->bounce_steps; /* Hack to stop a bounce in the finger down case */
2627 priv->vel_y = priv->vmax_overshooting;
2630 if (priv->overshot_dist_x != 0) {
2631 priv->overshooting_x = priv->bounce_steps; /* Hack to stop a bounce in the finger down case */
2632 priv->vel_x = priv->vmax_overshooting;
2635 if ((ABS (priv->vel_y) >= priv->vmin) ||
2636 (ABS (priv->vel_x) >= priv->vmin)) {
2638 if (ABS (priv->vel_x) > MAX_SPEED_THRESHOLD)
2639 priv->vel_x = (priv->vel_x > 0) ? priv->vmax : -priv->vmax;
2641 if (ABS (priv->vel_y) > MAX_SPEED_THRESHOLD)
2642 priv->vel_y = (priv->vel_y > 0) ? priv->vmax : -priv->vmax;
2645 priv->idle_id = gdk_threads_add_timeout ((gint) (1000.0 / (gdouble) priv->sps),
2647 hildon_pannable_area_timeout, widget);
2651 priv->last_time = event->time;
2652 priv->last_type = 3;
2655 priv->moved = FALSE;
2660 hildon_pannable_area_get_topmost (gtk_bin_get_child (GTK_BIN (widget))->window,
2661 event->x, event->y, &x, &y, GDK_BUTTON_RELEASE_MASK);
2663 event = (GdkEventButton *) gdk_event_copy ((GdkEvent *) event);
2667 /* Leave the widget if we've moved - This doesn't break selection,
2668 * but stops buttons from being clicked.
2670 if ((child != priv->child) || (priv->moved)) {
2671 /* Send synthetic leave event */
2672 synth_crossing (priv->child, x, y, event->x_root,
2673 event->y_root, event->time, FALSE);
2674 /* Send synthetic button release event */
2675 ((GdkEventAny *) event)->window = g_object_ref (priv->child);
2676 gdk_event_put ((GdkEvent *) event);
2678 /* Send synthetic button release event */
2679 ((GdkEventAny *) event)->window = g_object_ref (child);
2680 gdk_event_put ((GdkEvent *) event);
2681 /* Send synthetic leave event */
2682 synth_crossing (priv->child, x, y, event->x_root,
2683 event->y_root, event->time, FALSE);
2685 g_object_remove_weak_pointer ((GObject *) priv->child,
2686 (gpointer) & priv->child);
2688 priv->moved = FALSE;
2689 gdk_event_free ((GdkEvent *) event);
2694 /* utility event handler */
2696 hildon_pannable_area_scroll_cb (GtkWidget *widget,
2697 GdkEventScroll *event)
2699 GtkAdjustment *adj = NULL;
2700 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
2702 if ((!priv->enabled) ||
2703 (gtk_bin_get_child (GTK_BIN (widget)) == NULL))
2706 priv->scroll_indicator_event_interrupt = 0;
2707 priv->scroll_delay_counter = priv->scrollbar_fade_delay + 20;
2709 hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget), 1.0);
2711 /* Stop inertial scrolling */
2712 if (priv->idle_id) {
2715 priv->overshooting_x = 0;
2716 priv->overshooting_y = 0;
2718 if ((priv->overshot_dist_x>0)||(priv->overshot_dist_y>0)) {
2719 priv->overshot_dist_x = 0;
2720 priv->overshot_dist_y = 0;
2722 gtk_widget_queue_resize (GTK_WIDGET (widget));
2725 g_source_remove (priv->idle_id);
2729 if (event->direction == GDK_SCROLL_UP || event->direction == GDK_SCROLL_DOWN)
2730 adj = priv->vadjust;
2732 adj = priv->hadjust;
2736 gdouble delta, new_value;
2738 /* from gtkrange.c calculate delta*/
2739 delta = pow (adj->page_size, 2.0 / 3.0);
2741 if (event->direction == GDK_SCROLL_UP ||
2742 event->direction == GDK_SCROLL_LEFT)
2745 new_value = CLAMP (adj->value + delta, adj->lower, adj->upper - adj->page_size);
2747 gtk_adjustment_set_value (adj, new_value);
2754 hildon_pannable_area_child_mapped (GtkWidget *widget,
2758 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (user_data)->priv;
2760 if (priv->event_window != NULL && priv->enabled)
2761 gdk_window_raise (priv->event_window);
2765 hildon_pannable_area_add (GtkContainer *container, GtkWidget *child)
2767 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (container)->priv;
2769 g_return_if_fail (gtk_bin_get_child (GTK_BIN (container)) == NULL);
2771 gtk_widget_set_parent (child, GTK_WIDGET (container));
2772 GTK_BIN (container)->child = child;
2774 g_signal_connect_after (child, "map-event",
2775 G_CALLBACK (hildon_pannable_area_child_mapped),
2778 if (!gtk_widget_set_scroll_adjustments (child, priv->hadjust, priv->vadjust)) {
2779 g_warning ("%s: cannot add non scrollable widget, "
2780 "wrap it in a viewport", __FUNCTION__);
2785 hildon_pannable_area_remove (GtkContainer *container, GtkWidget *child)
2787 g_return_if_fail (HILDON_IS_PANNABLE_AREA (container));
2788 g_return_if_fail (child != NULL);
2789 g_return_if_fail (gtk_bin_get_child (GTK_BIN (container)) == child);
2791 gtk_widget_set_scroll_adjustments (child, NULL, NULL);
2793 g_signal_handlers_disconnect_by_func (child,
2794 hildon_pannable_area_child_mapped,
2797 /* chain parent class handler to remove child */
2798 GTK_CONTAINER_CLASS (hildon_pannable_area_parent_class)->remove (container, child);
2802 * This method calculates a factor necessary to determine the initial distance
2803 * to jump in hildon_pannable_area_scroll_to(). For fixed time and frames per
2804 * second, we know in how many frames 'n' we need to reach the destination
2805 * point. We know that, for a distance d,
2807 * d = d_0 + d_1 + ... + d_n
2809 * where d_i is the distance travelled in the i-th frame and decel_factor is
2810 * the deceleration factor. This can be rewritten as
2812 * d = d_0 + (d_0 * decel_factor) + ... + (d_n-1 * decel_factor),
2814 * since the distance travelled on each frame is the distance travelled in the
2815 * previous frame reduced by the deceleration factor. Reducing this and
2816 * factoring d_0 out, we get
2818 * d = d_0 (1 + decel_factor + ... + decel_factor^(n-1)).
2820 * Since the sum is independent of the distance to be travelled, we can define
2823 * vel_factor = 1 + decel_factor + ... + decel_factor^(n-1).
2825 * That's the gem we calculate in this method.
2828 hildon_pannable_calculate_vel_factor (HildonPannableArea * self)
2830 HildonPannableAreaPrivate *priv = self->priv;
2835 n = ceil (priv->sps * priv->scroll_time);
2837 for (i = 1; i < n && fct_i >= RATIO_TOLERANCE; i++) {
2838 fct_i *= priv->decel;
2842 priv->vel_factor = fct;
2846 * hildon_pannable_area_new:
2848 * Create a new pannable area widget
2850 * Returns: the newly created #HildonPannableArea
2856 hildon_pannable_area_new (void)
2858 return g_object_new (HILDON_TYPE_PANNABLE_AREA, NULL);
2862 * hildon_pannable_area_new_full:
2863 * @mode: #HildonPannableAreaMode
2864 * @enabled: Value for the enabled property
2865 * @vel_min: Value for the velocity-min property
2866 * @vel_max: Value for the velocity-max property
2867 * @decel: Value for the deceleration property
2868 * @sps: Value for the sps property
2870 * Create a new #HildonPannableArea widget and set various properties
2872 * returns: the newly create #HildonPannableArea
2878 hildon_pannable_area_new_full (gint mode, gboolean enabled,
2879 gdouble vel_min, gdouble vel_max,
2880 gdouble decel, guint sps)
2882 return g_object_new (HILDON_TYPE_PANNABLE_AREA,
2885 "velocity_min", vel_min,
2886 "velocity_max", vel_max,
2887 "deceleration", decel, "sps", sps, NULL);
2891 * hildon_pannable_area_add_with_viewport:
2892 * @area: A #HildonPannableArea
2893 * @child: Child widget to add to the viewport
2895 * Convenience function used to add a child to a #GtkViewport, and add the
2896 * viewport to the scrolled window.
2902 hildon_pannable_area_add_with_viewport (HildonPannableArea * area,
2906 GtkWidget *viewport;
2908 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
2909 g_return_if_fail (GTK_IS_WIDGET (child));
2910 g_return_if_fail (child->parent == NULL);
2912 bin = GTK_BIN (area);
2914 if (bin->child != NULL)
2916 g_return_if_fail (GTK_IS_VIEWPORT (bin->child));
2917 g_return_if_fail (GTK_BIN (bin->child)->child == NULL);
2919 viewport = bin->child;
2923 HildonPannableAreaPrivate *priv = area->priv;
2925 viewport = gtk_viewport_new (priv->hadjust,
2927 gtk_viewport_set_shadow_type (GTK_VIEWPORT (viewport), GTK_SHADOW_NONE);
2928 gtk_container_add (GTK_CONTAINER (area), viewport);
2931 gtk_widget_show (viewport);
2932 gtk_container_add (GTK_CONTAINER (viewport), child);
2936 * hildon_pannable_area_scroll_to:
2937 * @area: A #HildonPannableArea.
2938 * @x: The x coordinate of the destination point or -1 to ignore this axis.
2939 * @y: The y coordinate of the destination point or -1 to ignore this axis.
2941 * Smoothly scrolls @area to ensure that (@x, @y) is a visible point
2942 * on the widget. To move in only one coordinate, you must set the other one
2943 * to -1. Notice that, in %HILDON_PANNABLE_AREA_MODE_PUSH mode, this function
2944 * works just like hildon_pannable_area_jump_to().
2946 * This function is useful if you need to present the user with a particular
2947 * element inside a scrollable widget, like #GtkTreeView. For instance,
2948 * the following example shows how to scroll inside a #GtkTreeView to
2949 * make visible an item, indicated by the #GtkTreeIter @iter.
2953 * GtkTreePath *path;
2954 * GdkRectangle *rect;
2956 * path = gtk_tree_model_get_path (model, &iter);
2957 * gtk_tree_view_get_background_area (GTK_TREE_VIEW (treeview),
2958 * path, NULL, &rect);
2959 * gtk_tree_view_convert_bin_window_to_tree_coords (GTK_TREE_VIEW (treeview),
2960 * 0, rect.y, NULL, &y);
2961 * hildon_pannable_area_scroll_to (panarea, -1, y);
2962 * gtk_tree_path_free (path);
2966 * If you want to present a child widget in simpler scenarios,
2967 * use hildon_pannable_area_scroll_to_child() instead.
2969 * There is a precondition to this function: the widget must be
2970 * already realized. Check the hildon_pannable_area_jump_to_child() for
2971 * more tips regarding how to call this function during
2977 hildon_pannable_area_scroll_to (HildonPannableArea *area,
2978 const gint x, const gint y)
2980 HildonPannableAreaPrivate *priv;
2982 gint dist_x, dist_y;
2983 gboolean hscroll_visible, vscroll_visible;
2985 g_return_if_fail (GTK_WIDGET_REALIZED (area));
2986 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
2990 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
2991 priv->vadjust->page_size);
2992 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
2993 priv->hadjust->page_size);
2995 if (((!vscroll_visible)&&(!hscroll_visible)) ||
2996 (x == -1 && y == -1)) {
3000 if (priv->mode == HILDON_PANNABLE_AREA_MODE_PUSH)
3001 hildon_pannable_area_jump_to (area, x, y);
3003 width = priv->hadjust->upper - priv->hadjust->lower;
3004 height = priv->vadjust->upper - priv->vadjust->lower;
3006 g_return_if_fail (x < width || y < height);
3008 if ((x > -1)&&(hscroll_visible)) {
3009 priv->scroll_to_x = x - priv->hadjust->page_size/2;
3010 dist_x = priv->scroll_to_x - priv->hadjust->value;
3012 priv->scroll_to_x = -1;
3014 priv->vel_x = - dist_x/priv->vel_factor;
3017 priv->scroll_to_x = -1;
3020 if ((y > -1)&&(vscroll_visible)) {
3021 priv->scroll_to_y = y - priv->vadjust->page_size/2;
3022 dist_y = priv->scroll_to_y - priv->vadjust->value;
3024 priv->scroll_to_y = -1;
3026 priv->vel_y = - dist_y/priv->vel_factor;
3029 priv->scroll_to_y = y;
3032 if ((priv->scroll_to_y == -1) && (priv->scroll_to_y == -1)) {
3036 hildon_pannable_area_launch_fade_timeout (area, 1.0);
3039 priv->idle_id = gdk_threads_add_timeout ((gint) (1000.0 / (gdouble) priv->sps),
3041 hildon_pannable_area_timeout, area);
3045 * hildon_pannable_area_jump_to:
3046 * @area: A #HildonPannableArea.
3047 * @x: The x coordinate of the destination point or -1 to ignore this axis.
3048 * @y: The y coordinate of the destination point or -1 to ignore this axis.
3050 * Jumps the position of @area to ensure that (@x, @y) is a visible
3051 * point in the widget. In order to move in only one coordinate, you
3052 * must set the other one to -1. See hildon_pannable_area_scroll_to()
3053 * function for an example of how to calculate the position of
3054 * children in scrollable widgets like #GtkTreeview.
3056 * There is a precondition to this function: the widget must be
3057 * already realized. Check the hildon_pannable_area_jump_to_child() for
3058 * more tips regarding how to call this function during
3064 hildon_pannable_area_jump_to (HildonPannableArea *area,
3065 const gint x, const gint y)
3067 HildonPannableAreaPrivate *priv;
3071 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3072 g_return_if_fail (GTK_WIDGET_REALIZED (area));
3073 g_return_if_fail (x >= -1 && y >= -1);
3075 if (x == -1 && y == -1) {
3081 width = priv->hadjust->upper - priv->hadjust->lower;
3082 height = priv->vadjust->upper - priv->vadjust->lower;
3084 g_return_if_fail (x < width || y < height);
3086 hv = priv->hadjust->value;
3087 vv = priv->vadjust->value;
3090 gdouble jump_to = x - priv->hadjust->page_size/2;
3092 priv->hadjust->value = CLAMP (jump_to,
3093 priv->hadjust->lower,
3094 priv->hadjust->upper -
3095 priv->hadjust->page_size);
3099 gdouble jump_to = y - priv->vadjust->page_size/2;
3101 priv->vadjust->value = CLAMP (jump_to,
3102 priv->vadjust->lower,
3103 priv->vadjust->upper -
3104 priv->vadjust->page_size);
3107 if (hv != priv->hadjust->value)
3108 gtk_adjustment_value_changed (priv->hadjust);
3110 if (vv != priv->vadjust->value)
3111 gtk_adjustment_value_changed (priv->vadjust);
3113 priv->scroll_indicator_alpha = 1.0;
3115 if (priv->scroll_indicator_timeout) {
3116 g_source_remove (priv->scroll_indicator_timeout);
3117 priv->scroll_indicator_timeout = 0;
3120 if (priv->idle_id) {
3123 priv->overshooting_x = 0;
3124 priv->overshooting_y = 0;
3126 if ((priv->overshot_dist_x>0)||(priv->overshot_dist_y>0)) {
3127 priv->overshot_dist_x = 0;
3128 priv->overshot_dist_y = 0;
3130 gtk_widget_queue_resize (GTK_WIDGET (area));
3133 g_source_remove (priv->idle_id);
3139 * hildon_pannable_area_scroll_to_child:
3140 * @area: A #HildonPannableArea.
3141 * @child: A #GtkWidget, descendant of @area.
3143 * Smoothly scrolls until @child is visible inside @area. @child must
3144 * be a descendant of @area. If you need to scroll inside a scrollable
3145 * widget, e.g., #GtkTreeview, see hildon_pannable_area_scroll_to().
3147 * There is a precondition to this function: the widget must be
3148 * already realized. Check the hildon_pannable_area_jump_to_child() for
3149 * more tips regarding how to call this function during
3155 hildon_pannable_area_scroll_to_child (HildonPannableArea *area, GtkWidget *child)
3157 GtkWidget *bin_child;
3160 g_return_if_fail (GTK_WIDGET_REALIZED (area));
3161 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3162 g_return_if_fail (GTK_IS_WIDGET (child));
3163 g_return_if_fail (gtk_widget_is_ancestor (child, GTK_WIDGET (area)));
3165 if (GTK_BIN (area)->child == NULL)
3168 /* We need to get to check the child of the inside the area */
3169 bin_child = GTK_BIN (area)->child;
3171 /* we check if we added a viewport */
3172 if (GTK_IS_VIEWPORT (bin_child)) {
3173 bin_child = GTK_BIN (bin_child)->child;
3176 if (gtk_widget_translate_coordinates (child, bin_child, 0, 0, &x, &y))
3177 hildon_pannable_area_scroll_to (area, x, y);
3181 * hildon_pannable_area_jump_to_child:
3182 * @area: A #HildonPannableArea.
3183 * @child: A #GtkWidget, descendant of @area.
3185 * Jumps to make sure @child is visible inside @area. @child must
3186 * be a descendant of @area. If you want to move inside a scrollable
3187 * widget, like, #GtkTreeview, see hildon_pannable_area_scroll_to().
3189 * There is a precondition to this function: the widget must be
3190 * already realized. You can control if the widget is ready with the
3191 * GTK_WIDGET_REALIZED macro. If you want to call this function during
3192 * the initialization process of the widget do it inside a callback to
3193 * the ::realize signal, using g_signal_connect_after() function.
3198 hildon_pannable_area_jump_to_child (HildonPannableArea *area, GtkWidget *child)
3200 GtkWidget *bin_child;
3203 g_return_if_fail (GTK_WIDGET_REALIZED (area));
3204 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3205 g_return_if_fail (GTK_IS_WIDGET (child));
3206 g_return_if_fail (gtk_widget_is_ancestor (child, GTK_WIDGET (area)));
3208 if (gtk_bin_get_child (GTK_BIN (area)) == NULL)
3211 /* We need to get to check the child of the inside the area */
3212 bin_child = gtk_bin_get_child (GTK_BIN (area));
3214 /* we check if we added a viewport */
3215 if (GTK_IS_VIEWPORT (bin_child)) {
3216 bin_child = gtk_bin_get_child (GTK_BIN (bin_child));
3219 if (gtk_widget_translate_coordinates (child, bin_child, 0, 0, &x, &y))
3220 hildon_pannable_area_jump_to (area, x, y);
3224 * hildon_pannable_get_child_widget_at:
3225 * @area: A #HildonPannableArea.
3226 * @x: horizontal coordinate of the point
3227 * @y: vertical coordinate of the point
3229 * Get the widget at the point (x, y) inside the pannable area. In
3230 * case no widget found it returns NULL.
3232 * returns: the #GtkWidget if we find a widget, NULL in any other case
3237 hildon_pannable_get_child_widget_at (HildonPannableArea *area,
3238 gdouble x, gdouble y)
3240 GdkWindow *window = NULL;
3241 GtkWidget *child_widget = NULL;
3243 window = hildon_pannable_area_get_topmost
3244 (gtk_bin_get_child (GTK_BIN (area))->window,
3245 x, y, NULL, NULL, GDK_ALL_EVENTS_MASK);
3247 gdk_window_get_user_data (window, (gpointer) &child_widget);
3249 return child_widget;
3254 * hildon_pannable_area_get_hadjustment:
3255 * @area: A #HildonPannableArea.
3257 * Returns the horizontal adjustment. This adjustment is the internal
3258 * widget adjustment used to control the animations. Do not modify it
3259 * directly to change the position of the pannable, to do that use the
3260 * pannable API. If you modify the object directly it could cause
3261 * artifacts in the animations.
3263 * returns: The horizontal #GtkAdjustment
3268 hildon_pannable_area_get_hadjustment (HildonPannableArea *area)
3271 g_return_val_if_fail (HILDON_IS_PANNABLE_AREA (area), NULL);
3273 return area->priv->hadjust;
3277 * hildon_pannable_area_get_vadjustment:
3278 * @area: A #HildonPannableArea.
3280 * Returns the vertical adjustment. This adjustment is the internal
3281 * widget adjustment used to control the animations. Do not modify it
3282 * directly to change the position of the pannable, to do that use the
3283 * pannable API. If you modify the object directly it could cause
3284 * artifacts in the animations.
3286 * returns: The vertical #GtkAdjustment
3291 hildon_pannable_area_get_vadjustment (HildonPannableArea *area)
3293 g_return_val_if_fail (HILDON_IS_PANNABLE_AREA (area), NULL);
3295 return area->priv->vadjust;
3300 * hildon_pannable_area_get_size_request_policy:
3301 * @area: A #HildonPannableArea.
3303 * This function returns the current size request policy of the
3304 * widget. That policy controls the way the size_request is done in
3305 * the pannable area. Check
3306 * hildon_pannable_area_set_size_request_policy() for a more detailed
3309 * returns: the policy is currently being used in the widget
3310 * #HildonSizeRequestPolicy.
3314 HildonSizeRequestPolicy
3315 hildon_pannable_area_get_size_request_policy (HildonPannableArea *area)
3317 HildonPannableAreaPrivate *priv;
3319 g_return_val_if_fail (HILDON_IS_PANNABLE_AREA (area), FALSE);
3323 return priv->size_request_policy;
3327 * hildon_pannable_area_set_size_request_policy:
3328 * @area: A #HildonPannableArea.
3329 * @size_request_policy: One of the allowed #HildonSizeRequestPolicy
3331 * This function sets the pannable area size request policy. That
3332 * policy controls the way the size_request is done in the pannable
3333 * area. Pannable can use the size request of its children
3334 * (#HILDON_SIZE_REQUEST_CHILDREN) or the minimum size required for
3335 * the area itself (#HILDON_SIZE_REQUEST_MINIMUM), the latter is the
3336 * default. Recall this size depends on the scrolling policy you are
3337 * requesting to the pannable area, if you set #GTK_POLICY_NEVER this
3338 * parameter will not have any effect with
3339 * #HILDON_SIZE_REQUEST_MINIMUM set.
3343 * Deprecated: This method and the policy request is deprecated, DO
3344 * NOT use it in future code, the only policy properly supported in
3345 * gtk+ nowadays is the minimum size. Use #gtk_window_set_default_size
3346 * or #gtk_window_set_geometry_hints with the proper size in your case
3347 * to define the height of your dialogs.
3350 hildon_pannable_area_set_size_request_policy (HildonPannableArea *area,
3351 HildonSizeRequestPolicy size_request_policy)
3353 HildonPannableAreaPrivate *priv;
3355 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3359 if (priv->size_request_policy == size_request_policy)
3362 priv->size_request_policy = size_request_policy;
3364 gtk_widget_queue_resize (GTK_WIDGET (area));
3366 g_object_notify (G_OBJECT (area), "size-request-policy");