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
42 #if USE_CAIRO_SCROLLBARS == 1
47 #include "hildon-pannable-area.h"
48 #include "hildon-marshalers.h"
49 #include "hildon-enum-types.h"
51 #define USE_CAIRO_SCROLLBARS 0
53 #define SCROLL_BAR_MIN_SIZE 5
54 #define RATIO_TOLERANCE 0.000001
55 #define SCROLL_FADE_TIMEOUT 100
56 #define MOTION_EVENTS_PER_SECOND 25
57 #define CURSOR_STOPPED_TIMEOUT 80
59 G_DEFINE_TYPE (HildonPannableArea, hildon_pannable_area, GTK_TYPE_BIN)
61 #define PANNABLE_AREA_PRIVATE(o) \
62 (G_TYPE_INSTANCE_GET_PRIVATE ((o), HILDON_TYPE_PANNABLE_AREA, \
63 HildonPannableAreaPrivate))
65 struct _HildonPannableAreaPrivate {
66 HildonPannableAreaMode mode;
67 HildonMovementMode mov_mode;
68 GdkWindow *event_window;
69 gdouble x; /* Used to store mouse co-ordinates of the first or */
70 gdouble y; /* previous events in a press-motion pair */
71 gdouble ex; /* Used to store mouse co-ordinates of the last */
72 gdouble ey; /* motion event in acceleration mode */
75 guint32 last_time; /* Last event time, to stop infinite loops */
81 gdouble vmax_overshooting;
88 guint panning_threshold;
89 guint scrollbar_fade_delay;
92 guint direction_error_margin;
98 gint ix; /* Initial click mouse co-ordinates */
100 gint cx; /* Initial click child window mouse co-ordinates */
107 gint overshot_dist_x;
108 gint overshot_dist_y;
111 gdouble scroll_indicator_alpha;
112 gint motion_event_scroll_timeout;
113 gint scroll_indicator_timeout;
114 gint scroll_indicator_event_interrupt;
115 gint scroll_delay_counter;
118 gboolean initial_hint;
119 gboolean initial_effect;
120 gboolean low_friction_mode;
123 gboolean size_request_policy;
124 gboolean hscroll_visible;
125 gboolean vscroll_visible;
126 GdkRectangle hscroll_rect;
127 GdkRectangle vscroll_rect;
130 GtkAdjustment *hadjust;
131 GtkAdjustment *vadjust;
133 GtkPolicyType vscrollbar_policy;
134 GtkPolicyType hscrollbar_policy;
136 GdkGC *scrollbars_gc;
146 static guint pannable_area_signals [LAST_SIGNAL] = { 0 };
154 PROP_VEL_MAX_OVERSHOOTING,
155 PROP_VELOCITY_FAST_FACTOR,
159 PROP_PANNING_THRESHOLD,
160 PROP_SCROLLBAR_FADE_DELAY,
163 PROP_DIRECTION_ERROR_MARGIN,
164 PROP_VSCROLLBAR_POLICY,
165 PROP_HSCROLLBAR_POLICY,
170 PROP_LOW_FRICTION_MODE,
171 PROP_SIZE_REQUEST_POLICY,
177 static void hildon_pannable_area_class_init (HildonPannableAreaClass * klass);
178 static void hildon_pannable_area_init (HildonPannableArea * area);
179 static void hildon_pannable_area_get_property (GObject * object,
183 static void hildon_pannable_area_set_property (GObject * object,
185 const GValue * value,
187 static void hildon_pannable_area_dispose (GObject * object);
188 static void hildon_pannable_area_realize (GtkWidget * widget);
189 static void hildon_pannable_area_unrealize (GtkWidget * widget);
190 static void hildon_pannable_area_size_request (GtkWidget * widget,
191 GtkRequisition * requisition);
192 static void hildon_pannable_area_size_allocate (GtkWidget * widget,
193 GtkAllocation * allocation);
194 static void hildon_pannable_area_child_allocate_calculate (GtkWidget * widget,
195 GtkAllocation * allocation,
196 GtkAllocation * child_allocation);
197 static void hildon_pannable_area_style_set (GtkWidget * widget,
198 GtkStyle * previous_style);
199 static void hildon_pannable_area_map (GtkWidget * widget);
200 static void hildon_pannable_area_unmap (GtkWidget * widget);
201 static void hildon_pannable_area_grab_notify (GtkWidget *widget,
202 gboolean was_grabbed,
204 #if USE_CAIRO_SCROLLBARS == 1
205 static void rgb_from_gdkcolor (GdkColor *color, gdouble *r, gdouble *g, gdouble *b);
206 #else /* USE_CAIRO_SCROLLBARS */
207 static void tranparency_color (GdkColor *color,
210 gdouble transparency);
211 #endif /* USE_CAIRO_SCROLLBARS */
212 static void hildon_pannable_draw_vscroll (GtkWidget * widget,
213 GdkColor *back_color,
214 GdkColor *scroll_color);
215 static void hildon_pannable_draw_hscroll (GtkWidget * widget,
216 GdkColor *back_color,
217 GdkColor *scroll_color);
218 static void hildon_pannable_area_initial_effect (GtkWidget * widget);
219 static void hildon_pannable_area_redraw (HildonPannableArea * area);
220 static void hildon_pannable_area_launch_fade_timeout (HildonPannableArea * area,
222 static void hildon_pannable_area_adjust_value_changed (HildonPannableArea * area,
224 static void hildon_pannable_area_adjust_changed (HildonPannableArea * area,
226 static gboolean hildon_pannable_area_scroll_indicator_fade(HildonPannableArea * area);
227 static gboolean hildon_pannable_area_expose_event (GtkWidget * widget,
228 GdkEventExpose * event);
229 static GdkWindow * hildon_pannable_area_get_topmost (GdkWindow * window,
231 gint * tx, gint * ty,
233 static void synth_crossing (GdkWindow * child,
235 gint x_root, gint y_root,
236 guint32 time, gboolean in);
237 static gboolean hildon_pannable_area_button_press_cb (GtkWidget * widget,
238 GdkEventButton * event);
239 static void hildon_pannable_area_refresh (HildonPannableArea * area);
240 static gboolean hildon_pannable_area_check_scrollbars (HildonPannableArea * area);
241 static void hildon_pannable_axis_scroll (HildonPannableArea *area,
242 GtkAdjustment *adjust,
250 static void hildon_pannable_area_scroll (HildonPannableArea *area,
251 gdouble x, gdouble y);
252 static gboolean hildon_pannable_area_timeout (HildonPannableArea * area);
253 static void hildon_pannable_area_calculate_velocity (gdouble *vel,
257 gdouble drag_inertia,
260 static gboolean hildon_pannable_area_motion_event_scroll_timeout (HildonPannableArea *area);
261 static void hildon_pannable_area_motion_event_scroll (HildonPannableArea *area,
262 gdouble x, gdouble y);
263 static gboolean hildon_pannable_area_motion_notify_cb (GtkWidget * widget,
264 GdkEventMotion * event);
265 static gboolean hildon_pannable_leave_notify_event (GtkWidget *widget,
266 GdkEventCrossing *event);
267 static gboolean hildon_pannable_area_button_release_cb (GtkWidget * widget,
268 GdkEventButton * event);
269 static gboolean hildon_pannable_area_scroll_cb (GtkWidget *widget,
270 GdkEventScroll *event);
271 static void hildon_pannable_area_child_mapped (GtkWidget *widget,
274 static void hildon_pannable_area_add (GtkContainer *container, GtkWidget *child);
275 static void hildon_pannable_area_remove (GtkContainer *container, GtkWidget *child);
276 static void hildon_pannable_calculate_vel_factor (HildonPannableArea * self);
280 hildon_pannable_area_class_init (HildonPannableAreaClass * klass)
282 GObjectClass *object_class = G_OBJECT_CLASS (klass);
283 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
284 GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
287 g_type_class_add_private (klass, sizeof (HildonPannableAreaPrivate));
289 object_class->get_property = hildon_pannable_area_get_property;
290 object_class->set_property = hildon_pannable_area_set_property;
291 object_class->dispose = hildon_pannable_area_dispose;
293 widget_class->realize = hildon_pannable_area_realize;
294 widget_class->unrealize = hildon_pannable_area_unrealize;
295 widget_class->map = hildon_pannable_area_map;
296 widget_class->unmap = hildon_pannable_area_unmap;
297 widget_class->size_request = hildon_pannable_area_size_request;
298 widget_class->size_allocate = hildon_pannable_area_size_allocate;
299 widget_class->expose_event = hildon_pannable_area_expose_event;
300 widget_class->style_set = hildon_pannable_area_style_set;
301 widget_class->button_press_event = hildon_pannable_area_button_press_cb;
302 widget_class->button_release_event = hildon_pannable_area_button_release_cb;
303 widget_class->motion_notify_event = hildon_pannable_area_motion_notify_cb;
304 widget_class->leave_notify_event = hildon_pannable_leave_notify_event;
305 widget_class->scroll_event = hildon_pannable_area_scroll_cb;
307 container_class->add = hildon_pannable_area_add;
308 container_class->remove = hildon_pannable_area_remove;
310 klass->horizontal_movement = NULL;
311 klass->vertical_movement = NULL;
313 g_object_class_install_property (object_class,
315 g_param_spec_boolean ("enabled",
317 "Enable or disable finger-scroll.",
322 g_object_class_install_property (object_class,
323 PROP_VSCROLLBAR_POLICY,
324 g_param_spec_enum ("vscrollbar_policy",
326 "Visual policy of the vertical scrollbar",
327 GTK_TYPE_POLICY_TYPE,
328 GTK_POLICY_AUTOMATIC,
332 g_object_class_install_property (object_class,
333 PROP_HSCROLLBAR_POLICY,
334 g_param_spec_enum ("hscrollbar_policy",
336 "Visual policy of the horizontal scrollbar",
337 GTK_TYPE_POLICY_TYPE,
338 GTK_POLICY_AUTOMATIC,
342 g_object_class_install_property (object_class,
344 g_param_spec_enum ("mode",
346 "Change the finger-scrolling mode.",
347 HILDON_TYPE_PANNABLE_AREA_MODE,
348 HILDON_PANNABLE_AREA_MODE_AUTO,
352 g_object_class_install_property (object_class,
354 g_param_spec_flags ("mov_mode",
355 "Scroll movement mode",
356 "Controls if the widget can scroll vertically, horizontally or both",
357 HILDON_TYPE_MOVEMENT_MODE,
358 HILDON_MOVEMENT_MODE_VERT,
362 g_object_class_install_property (object_class,
364 g_param_spec_double ("velocity_min",
365 "Minimum scroll velocity",
366 "Minimum distance the child widget should scroll "
367 "per 'frame', in pixels per frame.",
372 g_object_class_install_property (object_class,
374 g_param_spec_double ("velocity_max",
375 "Maximum scroll velocity",
376 "Maximum distance the child widget should scroll "
377 "per 'frame', in pixels per frame.",
382 g_object_class_install_property (object_class,
383 PROP_VEL_MAX_OVERSHOOTING,
384 g_param_spec_double ("velocity_overshooting_max",
385 "Maximum scroll velocity when overshooting",
386 "Maximum distance the child widget should scroll "
387 "per 'frame', in pixels per frame when it overshoots after hitting the edge.",
392 g_object_class_install_property (object_class,
393 PROP_VELOCITY_FAST_FACTOR,
394 g_param_spec_double ("velocity_fast_factor",
395 "Fast velocity factor",
396 "Minimum velocity that is considered 'fast': "
397 "children widgets won't receive button presses. "
398 "Expressed as a fraction of the maximum velocity.",
403 g_object_class_install_property (object_class,
405 g_param_spec_double ("deceleration",
406 "Deceleration multiplier",
407 "The multiplier used when decelerating when in "
408 "acceleration scrolling mode.",
413 g_object_class_install_property (object_class,
415 g_param_spec_double ("drag_inertia",
416 "Inertia of the cursor dragging",
417 "Percentage of the calculated speed in each moment we are are going to use"
418 "to calculate the launch speed, the other part would be the speed"
419 "calculated previously",
424 g_object_class_install_property (object_class,
426 g_param_spec_uint ("sps",
427 "Scrolls per second",
428 "Amount of scroll events to generate per second.",
433 g_object_class_install_property (object_class,
434 PROP_PANNING_THRESHOLD,
435 g_param_spec_uint ("panning_threshold",
436 "Threshold to consider a motion event an scroll",
437 "Amount of pixels to consider a motion event an scroll, if it is less"
438 "it is a click detected incorrectly by the touch screen.",
443 g_object_class_install_property (object_class,
444 PROP_SCROLLBAR_FADE_DELAY,
445 g_param_spec_uint ("scrollbar_fade_delay",
446 "Time before starting to fade the scrollbar",
447 "Time the scrollbar is going to be visible if the widget is not in"
448 "action in miliseconds",
453 g_object_class_install_property (object_class,
455 g_param_spec_uint ("bounce_steps",
457 "Number of steps that is going to be used to bounce when hitting the"
458 "edge, the rubberband effect depends on it",
463 g_object_class_install_property (object_class,
465 g_param_spec_uint ("force",
466 "Multiplier of the calculated speed",
467 "Force applied to the movement, multiplies the calculated speed of the"
468 "user movement the cursor in the screen",
473 g_object_class_install_property (object_class,
474 PROP_DIRECTION_ERROR_MARGIN,
475 g_param_spec_uint ("direction_error_margin",
476 "Margin in the direction detection",
477 "After detecting the direction of the movement (horizontal or"
478 "vertical), we can add this margin of error to allow the movement in"
479 "the other direction even apparently it is not",
484 g_object_class_install_property (object_class,
486 g_param_spec_int ("vovershoot_max",
487 "Vertical overshoot distance",
488 "Space we allow the widget to pass over its vertical limits when"
489 "hitting the edges, set 0 in order to deactivate overshooting.",
494 g_object_class_install_property (object_class,
496 g_param_spec_int ("hovershoot_max",
497 "Horizontal overshoot distance",
498 "Space we allow the widget to pass over its horizontal limits when"
499 "hitting the edges, set 0 in order to deactivate overshooting.",
504 g_object_class_install_property (object_class,
506 g_param_spec_double ("scroll_time",
507 "Time to scroll to a position",
508 "The time to scroll to a position when calling the hildon_pannable_scroll_to function",
513 g_object_class_install_property (object_class,
515 g_param_spec_boolean ("initial-hint",
517 "Whether to hint the user about the pannability of the container.",
522 g_object_class_install_property (object_class,
523 PROP_LOW_FRICTION_MODE,
524 g_param_spec_boolean ("low-friction-mode",
525 "Do not decelerate the initial velocity",
526 "Avoid decelerating the panning movement, like no friction, the widget"
527 "will stop in the edges or if the user clicks.",
532 g_object_class_install_property (object_class,
533 PROP_SIZE_REQUEST_POLICY,
534 g_param_spec_enum ("size-request-policy",
535 "Size Requisition policy",
536 "Controls the size request policy of the widget",
537 HILDON_TYPE_SIZE_REQUEST_POLICY,
538 HILDON_SIZE_REQUEST_MINIMUM,
542 g_object_class_install_property (object_class,
544 g_param_spec_object ("hadjustment",
545 "Horizontal Adjustment",
546 "The GtkAdjustment for the horizontal position",
549 g_object_class_install_property (object_class,
551 g_param_spec_object ("vadjustment",
552 "Vertical Adjustment",
553 "The GtkAdjustment for the vertical position",
557 gtk_widget_class_install_style_property (widget_class,
560 "Width of the scroll indicators",
561 "Pixel width used to draw the scroll indicators.",
565 pannable_area_signals[HORIZONTAL_MOVEMENT] =
566 g_signal_new ("horizontal_movement",
567 G_TYPE_FROM_CLASS (object_class),
568 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
569 G_STRUCT_OFFSET (HildonPannableAreaClass, horizontal_movement),
571 _hildon_marshal_VOID__INT_DOUBLE_DOUBLE,
577 pannable_area_signals[VERTICAL_MOVEMENT] =
578 g_signal_new ("vertical_movement",
579 G_TYPE_FROM_CLASS (object_class),
580 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
581 G_STRUCT_OFFSET (HildonPannableAreaClass, vertical_movement),
583 _hildon_marshal_VOID__INT_DOUBLE_DOUBLE,
593 hildon_pannable_area_init (HildonPannableArea * area)
595 HildonPannableAreaPrivate *priv = PANNABLE_AREA_PRIVATE (area);
597 GTK_WIDGET_UNSET_FLAGS (area, GTK_NO_WINDOW);
602 priv->clicked = FALSE;
605 priv->vscroll_visible = TRUE;
606 priv->hscroll_visible = TRUE;
607 priv->area_width = 6;
608 priv->overshot_dist_x = 0;
609 priv->overshot_dist_y = 0;
610 priv->overshooting_y = 0;
611 priv->overshooting_x = 0;
615 priv->scroll_indicator_alpha = 0.0;
616 priv->scroll_indicator_timeout = 0;
617 priv->motion_event_scroll_timeout = 0;
618 priv->scroll_indicator_event_interrupt = 0;
619 priv->scroll_delay_counter = priv->scrollbar_fade_delay;
620 priv->scroll_to_x = -1;
621 priv->scroll_to_y = -1;
622 priv->first_drag = TRUE;
623 priv->initial_effect = TRUE;
624 priv->child_width = 0;
625 priv->child_height = 0;
626 priv->last_in = TRUE;
628 hildon_pannable_calculate_vel_factor (area);
630 gtk_widget_add_events (GTK_WIDGET (area), GDK_POINTER_MOTION_HINT_MASK);
633 GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
635 GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
637 g_object_ref_sink (G_OBJECT (priv->hadjust));
638 g_object_ref_sink (G_OBJECT (priv->vadjust));
640 g_signal_connect_swapped (priv->hadjust, "value-changed",
641 G_CALLBACK (hildon_pannable_area_adjust_value_changed), area);
642 g_signal_connect_swapped (priv->vadjust, "value-changed",
643 G_CALLBACK (hildon_pannable_area_adjust_value_changed), area);
644 g_signal_connect_swapped (priv->hadjust, "changed",
645 G_CALLBACK (hildon_pannable_area_adjust_changed), area);
646 g_signal_connect_swapped (priv->vadjust, "changed",
647 G_CALLBACK (hildon_pannable_area_adjust_changed), area);
648 g_signal_connect (area, "grab-notify",
649 G_CALLBACK (hildon_pannable_area_grab_notify), NULL);
653 hildon_pannable_area_get_property (GObject * object,
658 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (object)->priv;
660 switch (property_id) {
662 g_value_set_boolean (value, priv->enabled);
665 g_value_set_enum (value, priv->mode);
667 case PROP_MOVEMENT_MODE:
668 g_value_set_flags (value, priv->mov_mode);
670 case PROP_VELOCITY_MIN:
671 g_value_set_double (value, priv->vmin);
673 case PROP_VELOCITY_MAX:
674 g_value_set_double (value, priv->vmax);
676 case PROP_VEL_MAX_OVERSHOOTING:
677 g_value_set_double (value, priv->vmax_overshooting);
679 case PROP_VELOCITY_FAST_FACTOR:
680 g_value_set_double (value, priv->vfast_factor);
682 case PROP_DECELERATION:
683 g_value_set_double (value, priv->decel);
685 case PROP_DRAG_INERTIA:
686 g_value_set_double (value, priv->drag_inertia);
689 g_value_set_uint (value, priv->sps);
691 case PROP_PANNING_THRESHOLD:
692 g_value_set_uint (value, priv->panning_threshold);
694 case PROP_SCROLLBAR_FADE_DELAY:
695 /* convert to miliseconds */
696 g_value_set_uint (value, priv->scrollbar_fade_delay * SCROLL_FADE_TIMEOUT);
698 case PROP_BOUNCE_STEPS:
699 g_value_set_uint (value, priv->bounce_steps);
702 g_value_set_uint (value, priv->force);
704 case PROP_DIRECTION_ERROR_MARGIN:
705 g_value_set_uint (value, priv->direction_error_margin);
707 case PROP_VSCROLLBAR_POLICY:
708 g_value_set_enum (value, priv->vscrollbar_policy);
710 case PROP_HSCROLLBAR_POLICY:
711 g_value_set_enum (value, priv->hscrollbar_policy);
713 case PROP_VOVERSHOOT_MAX:
714 g_value_set_int (value, priv->vovershoot_max);
716 case PROP_HOVERSHOOT_MAX:
717 g_value_set_int (value, priv->hovershoot_max);
719 case PROP_SCROLL_TIME:
720 g_value_set_double (value, priv->scroll_time);
722 case PROP_INITIAL_HINT:
723 g_value_set_boolean (value, priv->initial_hint);
725 case PROP_LOW_FRICTION_MODE:
726 g_value_set_boolean (value, priv->low_friction_mode);
728 case PROP_SIZE_REQUEST_POLICY:
729 g_value_set_enum (value, priv->size_request_policy);
731 case PROP_HADJUSTMENT:
732 g_value_set_object (value,
733 hildon_pannable_area_get_hadjustment
734 (HILDON_PANNABLE_AREA (object)));
736 case PROP_VADJUSTMENT:
737 g_value_set_object (value,
738 hildon_pannable_area_get_vadjustment
739 (HILDON_PANNABLE_AREA (object)));
742 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
747 hildon_pannable_area_set_property (GObject * object,
749 const GValue * value,
752 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (object)->priv;
755 switch (property_id) {
757 enabled = g_value_get_boolean (value);
759 if ((priv->enabled != enabled) && (GTK_WIDGET_REALIZED (object))) {
761 gdk_window_raise (priv->event_window);
763 gdk_window_lower (priv->event_window);
766 priv->enabled = enabled;
769 priv->mode = g_value_get_enum (value);
771 case PROP_MOVEMENT_MODE:
772 priv->mov_mode = g_value_get_flags (value);
774 case PROP_VELOCITY_MIN:
775 priv->vmin = g_value_get_double (value);
777 case PROP_VELOCITY_MAX:
778 priv->vmax = g_value_get_double (value);
780 case PROP_VEL_MAX_OVERSHOOTING:
781 priv->vmax_overshooting = g_value_get_double (value);
783 case PROP_VELOCITY_FAST_FACTOR:
784 priv->vfast_factor = g_value_get_double (value);
786 case PROP_DECELERATION:
787 hildon_pannable_calculate_vel_factor (HILDON_PANNABLE_AREA (object));
789 priv->decel = g_value_get_double (value);
791 case PROP_DRAG_INERTIA:
792 priv->drag_inertia = g_value_get_double (value);
795 priv->sps = g_value_get_uint (value);
797 case PROP_PANNING_THRESHOLD:
798 priv->panning_threshold = g_value_get_uint (value);
800 case PROP_SCROLLBAR_FADE_DELAY:
801 /* convert to miliseconds */
802 priv->scrollbar_fade_delay = g_value_get_uint (value)/(SCROLL_FADE_TIMEOUT);
804 case PROP_BOUNCE_STEPS:
805 priv->bounce_steps = g_value_get_uint (value);
808 priv->force = g_value_get_uint (value);
810 case PROP_DIRECTION_ERROR_MARGIN:
811 priv->direction_error_margin = g_value_get_uint (value);
813 case PROP_VSCROLLBAR_POLICY:
814 priv->vscrollbar_policy = g_value_get_enum (value);
816 gtk_widget_queue_resize (GTK_WIDGET (object));
818 case PROP_HSCROLLBAR_POLICY:
819 priv->hscrollbar_policy = g_value_get_enum (value);
821 gtk_widget_queue_resize (GTK_WIDGET (object));
823 case PROP_VOVERSHOOT_MAX:
824 priv->vovershoot_max = g_value_get_int (value);
826 case PROP_HOVERSHOOT_MAX:
827 priv->hovershoot_max = g_value_get_int (value);
829 case PROP_SCROLL_TIME:
830 priv->scroll_time = g_value_get_double (value);
832 hildon_pannable_calculate_vel_factor (HILDON_PANNABLE_AREA (object));
834 case PROP_INITIAL_HINT:
835 priv->initial_hint = g_value_get_boolean (value);
837 case PROP_LOW_FRICTION_MODE:
838 priv->low_friction_mode = g_value_get_boolean (value);
840 case PROP_SIZE_REQUEST_POLICY:
841 hildon_pannable_area_set_size_request_policy (HILDON_PANNABLE_AREA (object),
842 g_value_get_enum (value));
846 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
851 hildon_pannable_area_dispose (GObject * object)
853 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (object)->priv;
854 GtkWidget *child = gtk_bin_get_child (GTK_BIN (object));
857 g_source_remove (priv->idle_id);
861 if (priv->scroll_indicator_timeout){
862 g_source_remove (priv->scroll_indicator_timeout);
863 priv->scroll_indicator_timeout = 0;
866 if (priv->motion_event_scroll_timeout){
867 g_source_remove (priv->motion_event_scroll_timeout);
868 priv->motion_event_scroll_timeout = 0;
872 g_signal_handlers_disconnect_by_func (child,
873 hildon_pannable_area_child_mapped,
877 g_signal_handlers_disconnect_by_func (object,
878 hildon_pannable_area_grab_notify,
882 g_signal_handlers_disconnect_by_func (priv->hadjust,
883 hildon_pannable_area_adjust_value_changed,
885 g_signal_handlers_disconnect_by_func (priv->hadjust,
886 hildon_pannable_area_adjust_changed,
888 g_object_unref (priv->hadjust);
889 priv->hadjust = NULL;
893 g_signal_handlers_disconnect_by_func (priv->vadjust,
894 hildon_pannable_area_adjust_value_changed,
896 g_signal_handlers_disconnect_by_func (priv->vadjust,
897 hildon_pannable_area_adjust_changed,
899 g_object_unref (priv->vadjust);
900 priv->vadjust = NULL;
903 if (G_OBJECT_CLASS (hildon_pannable_area_parent_class)->dispose)
904 G_OBJECT_CLASS (hildon_pannable_area_parent_class)->dispose (object);
908 hildon_pannable_area_realize (GtkWidget * widget)
910 GdkWindowAttr attributes;
911 gint attributes_mask;
913 HildonPannableAreaPrivate *priv;
915 priv = HILDON_PANNABLE_AREA (widget)->priv;
917 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
919 border_width = GTK_CONTAINER (widget)->border_width;
921 attributes.x = widget->allocation.x + border_width;
922 attributes.y = widget->allocation.y + border_width;
923 attributes.width = MAX (widget->allocation.width - 2 * border_width, 0);
924 attributes.height = MAX (widget->allocation.height - 2 * border_width, 0);
925 attributes.window_type = GDK_WINDOW_CHILD;
927 /* avoid using the hildon_window */
928 attributes.visual = gtk_widget_get_visual (widget);
929 attributes.colormap = gtk_widget_get_colormap (widget);
930 attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK;
931 attributes.wclass = GDK_INPUT_OUTPUT;
933 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
935 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
936 &attributes, attributes_mask);
937 gdk_window_set_user_data (widget->window, widget);
939 /* create the events window */
942 attributes.event_mask = gtk_widget_get_events (widget)
943 | GDK_BUTTON_MOTION_MASK
944 | GDK_BUTTON_PRESS_MASK
945 | GDK_BUTTON_RELEASE_MASK
947 | GDK_EXPOSURE_MASK | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK;
948 attributes.wclass = GDK_INPUT_ONLY;
950 attributes_mask = GDK_WA_X | GDK_WA_Y;
952 priv->event_window = gdk_window_new (widget->window,
953 &attributes, attributes_mask);
954 gdk_window_set_user_data (priv->event_window, widget);
956 widget->style = gtk_style_attach (widget->style, widget->window);
957 gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
959 priv->scrollbars_gc = gdk_gc_new (GDK_DRAWABLE (widget->window));
960 gdk_gc_copy (priv->scrollbars_gc, widget->style->fg_gc[GTK_STATE_INSENSITIVE]);
964 hildon_pannable_area_unrealize (GtkWidget * widget)
966 HildonPannableAreaPrivate *priv;
968 priv = HILDON_PANNABLE_AREA (widget)->priv;
970 if (priv->event_window != NULL) {
971 gdk_window_set_user_data (priv->event_window, NULL);
972 gdk_window_destroy (priv->event_window);
973 priv->event_window = NULL;
976 gdk_gc_unref (priv->scrollbars_gc);
978 if (GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->unrealize)
979 (*GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->unrealize)(widget);
983 hildon_pannable_area_size_request (GtkWidget * widget,
984 GtkRequisition * requisition)
986 GtkRequisition child_requisition = {0};
987 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
988 GtkWidget *child = gtk_bin_get_child (GTK_BIN (widget));
990 if (child && GTK_WIDGET_VISIBLE (child))
992 gtk_widget_size_request (child, &child_requisition);
995 if (priv->hscrollbar_policy == GTK_POLICY_NEVER) {
996 requisition->width = child_requisition.width;
998 switch (priv->size_request_policy) {
999 case HILDON_SIZE_REQUEST_CHILDREN:
1000 requisition->width = child_requisition.width;
1002 case HILDON_SIZE_REQUEST_MINIMUM:
1004 requisition->width = priv->area_width;
1008 if (priv->vscrollbar_policy == GTK_POLICY_NEVER) {
1009 requisition->height = child_requisition.height;
1011 switch (priv->size_request_policy) {
1012 case HILDON_SIZE_REQUEST_CHILDREN:
1013 requisition->height = child_requisition.height;
1015 case HILDON_SIZE_REQUEST_MINIMUM:
1017 requisition->height = priv->area_width;
1021 requisition->width += 2 * GTK_CONTAINER (widget)->border_width;
1022 requisition->height += 2 * GTK_CONTAINER (widget)->border_width;
1026 hildon_pannable_area_child_allocate_calculate (GtkWidget * widget,
1027 GtkAllocation * allocation,
1028 GtkAllocation * child_allocation)
1031 HildonPannableAreaPrivate *priv;
1033 border_width = GTK_CONTAINER (widget)->border_width;
1035 priv = HILDON_PANNABLE_AREA (widget)->priv;
1037 child_allocation->x = 0;
1038 child_allocation->y = 0;
1039 child_allocation->width = MAX (allocation->width - 2 * border_width -
1040 (priv->vscroll_visible ? priv->vscroll_rect.width : 0), 0);
1041 child_allocation->height = MAX (allocation->height - 2 * border_width -
1042 (priv->hscroll_visible ? priv->hscroll_rect.height : 0), 0);
1044 if (priv->overshot_dist_y > 0) {
1045 child_allocation->y = MIN (child_allocation->y + priv->overshot_dist_y,
1046 child_allocation->height);
1047 child_allocation->height = MAX (child_allocation->height - priv->overshot_dist_y, 0);
1048 } else if (priv->overshot_dist_y < 0) {
1049 child_allocation->height = MAX (child_allocation->height + priv->overshot_dist_y, 0);
1052 if (priv->overshot_dist_x > 0) {
1053 child_allocation->x = MIN (child_allocation->x + priv->overshot_dist_x,
1054 child_allocation->width);
1055 child_allocation->width = MAX (child_allocation->width - priv->overshot_dist_x, 0);
1056 } else if (priv->overshot_dist_x < 0) {
1057 child_allocation->width = MAX (child_allocation->width + priv->overshot_dist_x, 0);
1062 hildon_pannable_area_size_allocate (GtkWidget * widget,
1063 GtkAllocation * allocation)
1065 GtkAllocation child_allocation;
1066 HildonPannableAreaPrivate *priv;
1067 GtkWidget *child = gtk_bin_get_child (GTK_BIN (widget));
1070 border_width = GTK_CONTAINER (widget)->border_width;
1072 widget->allocation = *allocation;
1074 priv = HILDON_PANNABLE_AREA (widget)->priv;
1076 if (GTK_WIDGET_REALIZED (widget)) {
1077 gdk_window_move_resize (widget->window,
1078 allocation->x + border_width,
1079 allocation->y + border_width,
1080 allocation->width - border_width * 2,
1081 allocation->height - border_width * 2);
1082 gdk_window_move_resize (priv->event_window,
1085 allocation->width - border_width * 2,
1086 allocation->height - border_width * 2);
1089 if (child && GTK_WIDGET_VISIBLE (child)) {
1091 hildon_pannable_area_child_allocate_calculate (widget,
1095 gtk_widget_size_allocate (child, &child_allocation);
1097 if (hildon_pannable_area_check_scrollbars (HILDON_PANNABLE_AREA (widget))) {
1098 hildon_pannable_area_child_allocate_calculate (widget,
1102 gtk_widget_size_allocate (child, &child_allocation);
1105 /* we have to do this after child size_allocate because page_size is
1106 * changed when we allocate the size of the children */
1107 if (priv->overshot_dist_y < 0) {
1108 gtk_adjustment_set_value (priv->vadjust, priv->vadjust->upper -
1109 priv->vadjust->page_size);
1112 if (priv->overshot_dist_x < 0) {
1113 gtk_adjustment_set_value (priv->hadjust, priv->hadjust->upper -
1114 priv->hadjust->page_size);
1118 hildon_pannable_area_check_scrollbars (HILDON_PANNABLE_AREA (widget));
1123 hildon_pannable_area_style_set (GtkWidget * widget,
1124 GtkStyle * previous_style)
1126 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1128 GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->
1129 style_set (widget, previous_style);
1131 gtk_widget_style_get (widget, "indicator-width", &priv->area_width, NULL);
1135 hildon_pannable_area_map (GtkWidget * widget)
1137 HildonPannableAreaPrivate *priv;
1139 priv = HILDON_PANNABLE_AREA (widget)->priv;
1141 gdk_window_show (widget->window);
1143 if (priv->event_window != NULL && !priv->enabled)
1144 gdk_window_show (priv->event_window);
1146 (*GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->map) (widget);
1148 if (priv->event_window != NULL && priv->enabled)
1149 gdk_window_show (priv->event_window);
1153 hildon_pannable_area_unmap (GtkWidget * widget)
1155 HildonPannableAreaPrivate *priv;
1157 priv = HILDON_PANNABLE_AREA (widget)->priv;
1159 if (priv->event_window != NULL)
1160 gdk_window_hide (priv->event_window);
1162 gdk_window_hide (widget->window);
1164 (*GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->unmap) (widget);
1168 hildon_pannable_area_grab_notify (GtkWidget *widget,
1169 gboolean was_grabbed,
1172 /* an internal widget has grabbed the focus and now has returned it,
1173 we have to do some release actions */
1175 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1177 priv->scroll_indicator_event_interrupt = 0;
1179 if ((!priv->scroll_indicator_timeout)&&(priv->scroll_indicator_alpha)>0.1) {
1180 priv->scroll_delay_counter = priv->scrollbar_fade_delay;
1182 hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget),
1183 priv->scroll_indicator_alpha);
1186 priv->last_type = 3;
1187 priv->moved = FALSE;
1191 #if USE_CAIRO_SCROLLBARS == 1
1194 rgb_from_gdkcolor (GdkColor *color, gdouble *r, gdouble *g, gdouble *b)
1196 *r = (color->red >> 8) / 255.0;
1197 *g = (color->green >> 8) / 255.0;
1198 *b = (color->blue >> 8) / 255.0;
1202 hildon_pannable_draw_vscroll (GtkWidget * widget,
1203 GdkColor *back_color,
1204 GdkColor *scroll_color)
1206 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1209 cairo_pattern_t *pattern;
1211 gint radius = (priv->vscroll_rect.width/2) - 1;
1213 cr = gdk_cairo_create(widget->window);
1215 /* Draw the background */
1216 rgb_from_gdkcolor (back_color, &r, &g, &b);
1217 cairo_set_source_rgb (cr, r, g, b);
1218 cairo_rectangle(cr, priv->vscroll_rect.x, priv->vscroll_rect.y,
1219 priv->vscroll_rect.width,
1220 priv->vscroll_rect.height);
1221 cairo_fill_preserve (cr);
1224 /* Calculate the scroll bar height and position */
1225 y = ((priv->vadjust->value - priv->vadjust->lower) / (priv->vadjust->upper - priv->vadjust->lower)) *
1226 (widget->allocation.height -
1227 (priv->hscroll_visible ? priv->area_width : 0));
1228 height = ((((priv->vadjust->value - priv->vadjust->lower) +
1229 priv->vadjust->page_size) /
1230 (priv->vadjust->upper - priv->vadjust->lower)) *
1231 (widget->allocation.height -
1232 (priv->hscroll_visible ? priv->area_width : 0))) - y;
1234 /* Set a minimum height */
1235 height = MAX (SCROLL_BAR_MIN_SIZE, height);
1237 /* Check the max y position */
1238 y = MIN (y, widget->allocation.height -
1239 (priv->hscroll_visible ? priv->hscroll_rect.height : 0) -
1242 /* Draw the scrollbar */
1243 rgb_from_gdkcolor (scroll_color, &r, &g, &b);
1245 pattern = cairo_pattern_create_linear(radius+1, y, radius+1,y + height);
1246 cairo_pattern_add_color_stop_rgb(pattern, 0, r, g, b);
1247 cairo_pattern_add_color_stop_rgb(pattern, 1, r/2, g/2, b/2);
1248 cairo_set_source(cr, pattern);
1250 cairo_pattern_destroy(pattern);
1252 cairo_arc(cr, priv->vscroll_rect.x + radius + 1, y + radius + 1, radius, G_PI, 0);
1253 cairo_line_to(cr, priv->vscroll_rect.x + (radius * 2) + 1, y + height - radius);
1254 cairo_arc(cr, priv->vscroll_rect.x + radius + 1, y + height - radius, radius, 0, G_PI);
1255 cairo_line_to(cr, priv->vscroll_rect.x + 1, y + height - radius);
1258 cairo_paint_with_alpha(cr, priv->scroll_indicator_alpha);
1264 hildon_pannable_draw_hscroll (GtkWidget * widget,
1265 GdkColor *back_color,
1266 GdkColor *scroll_color)
1268 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1271 cairo_pattern_t *pattern;
1273 gint radius = (priv->hscroll_rect.height/2) - 1;
1275 cr = gdk_cairo_create(widget->window);
1277 /* Draw the background */
1278 rgb_from_gdkcolor (back_color, &r, &g, &b);
1279 cairo_set_source_rgb (cr, r, g, b);
1280 cairo_rectangle(cr, priv->hscroll_rect.x, priv->hscroll_rect.y,
1281 priv->hscroll_rect.width,
1282 priv->hscroll_rect.height);
1283 cairo_fill_preserve (cr);
1286 /* calculate the scrollbar width and position */
1287 x = ((priv->hadjust->value - priv->hadjust->lower) / (priv->hadjust->upper - priv->hadjust->lower)) *
1288 (widget->allocation.width - (priv->vscroll_visible ? priv->area_width : 0));
1289 width =((((priv->hadjust->value - priv->hadjust->lower) +
1290 priv->hadjust->page_size) / (priv->hadjust->upper - priv->hadjust->lower)) *
1291 (widget->allocation.width -
1292 (priv->vscroll_visible ? priv->area_width : 0))) - x;
1294 /* Set a minimum width */
1295 width = MAX (SCROLL_BAR_MIN_SIZE, width);
1297 /* Check the max x position */
1298 x = MIN (x, widget->allocation.width -
1299 (priv->vscroll_visible ? priv->vscroll_rect.width : 0) -
1302 /* Draw the scrollbar */
1303 rgb_from_gdkcolor (scroll_color, &r, &g, &b);
1305 pattern = cairo_pattern_create_linear(x, radius+1, x+width, radius+1);
1306 cairo_pattern_add_color_stop_rgb(pattern, 0, r, g, b);
1307 cairo_pattern_add_color_stop_rgb(pattern, 1, r/2, g/2, b/2);
1308 cairo_set_source(cr, pattern);
1310 cairo_pattern_destroy(pattern);
1312 cairo_arc_negative(cr, x + radius + 1, priv->hscroll_rect.y + radius + 1, radius, 3*G_PI_2, G_PI_2);
1313 cairo_line_to(cr, x + width - radius, priv->hscroll_rect.y + (radius * 2) + 1);
1314 cairo_arc_negative(cr, x + width - radius, priv->hscroll_rect.y + radius + 1, radius, G_PI_2, 3*G_PI_2);
1315 cairo_line_to(cr, x + width - radius, priv->hscroll_rect.y + 1);
1318 cairo_paint_with_alpha(cr, priv->scroll_indicator_alpha);
1323 #else /* USE_CAIRO_SCROLLBARS */
1326 tranparency_color (GdkColor *color,
1329 gdouble transparency)
1333 diff = colora.red - colorb.red;
1334 color->red = colora.red-diff*transparency;
1336 diff = colora.green - colorb.green;
1337 color->green = colora.green-diff*transparency;
1339 diff = colora.blue - colorb.blue;
1340 color->blue = colora.blue-diff*transparency;
1344 hildon_pannable_draw_vscroll (GtkWidget *widget,
1345 GdkColor *back_color,
1346 GdkColor *scroll_color)
1348 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1350 GdkColor transp_color;
1351 GdkGC *gc = widget->style->fg_gc[GTK_STATE_INSENSITIVE];
1353 gdk_draw_rectangle (widget->window,
1354 widget->style->bg_gc[GTK_STATE_NORMAL],
1356 priv->vscroll_rect.x, priv->vscroll_rect.y,
1357 priv->vscroll_rect.width,
1358 priv->vscroll_rect.height);
1360 y = ((priv->vadjust->value - priv->vadjust->lower) / (priv->vadjust->upper - priv->vadjust->lower)) *
1361 (widget->allocation.height - (priv->hscroll_visible ? priv->area_width : 0));
1362 height = ((((priv->vadjust->value - priv->vadjust->lower) + priv->vadjust->page_size) /
1363 (priv->vadjust->upper - priv->vadjust->lower)) *
1364 (widget->allocation.height -
1365 (priv->hscroll_visible ? priv->area_width : 0))) - y;
1367 /* Set a minimum height */
1368 height = MAX (SCROLL_BAR_MIN_SIZE, height);
1370 /* Check the max y position */
1371 y = MIN (y, widget->allocation.height -
1372 (priv->hscroll_visible ? priv->hscroll_rect.height : 0) -
1375 if (priv->scroll_indicator_alpha < 1.0) {
1376 tranparency_color (&transp_color, *back_color, *scroll_color,
1377 priv->scroll_indicator_alpha);
1379 gdk_gc_set_rgb_fg_color (priv->scrollbars_gc, &transp_color);
1381 gc = priv->scrollbars_gc;
1384 gdk_draw_rectangle (widget->window, gc,
1385 TRUE, priv->vscroll_rect.x, y,
1386 priv->vscroll_rect.width, height);
1390 hildon_pannable_draw_hscroll (GtkWidget *widget,
1391 GdkColor *back_color,
1392 GdkColor *scroll_color)
1394 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1396 GdkColor transp_color;
1397 GdkGC *gc = widget->style->fg_gc[GTK_STATE_INSENSITIVE];
1399 gdk_draw_rectangle (widget->window,
1400 widget->style->bg_gc[GTK_STATE_INSENSITIVE],
1402 priv->hscroll_rect.x, priv->hscroll_rect.y,
1403 priv->hscroll_rect.width,
1404 priv->hscroll_rect.height);
1406 /* calculate the scrollbar width and position */
1407 x = ((priv->hadjust->value - priv->hadjust->lower) / (priv->hadjust->upper - priv->hadjust->lower)) *
1408 (widget->allocation.width - (priv->vscroll_visible ? priv->area_width : 0));
1409 width =((((priv->hadjust->value - priv->hadjust->lower) +
1410 priv->hadjust->page_size) / (priv->hadjust->upper - priv->hadjust->lower)) *
1411 (widget->allocation.width -
1412 (priv->vscroll_visible ? priv->area_width : 0))) - x;
1414 /* Set a minimum width */
1415 width = MAX (SCROLL_BAR_MIN_SIZE, width);
1417 /* Check the max x position */
1418 x = MIN (x, widget->allocation.width -
1419 (priv->vscroll_visible ? priv->vscroll_rect.width : 0) -
1422 if (priv->scroll_indicator_alpha < 1.0) {
1423 tranparency_color (&transp_color, *back_color, *scroll_color,
1424 priv->scroll_indicator_alpha);
1426 gdk_gc_set_rgb_fg_color (priv->scrollbars_gc, &transp_color);
1428 gc = priv->scrollbars_gc;
1431 gdk_draw_rectangle (widget->window, gc,
1432 TRUE, x, priv->hscroll_rect.y, width,
1433 priv->hscroll_rect.height);
1436 #endif /* USE_CAIRO_SCROLLBARS */
1439 hildon_pannable_area_initial_effect (GtkWidget * widget)
1441 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1442 gboolean hscroll_visible, vscroll_visible;
1444 if (priv->initial_hint) {
1446 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
1447 priv->vadjust->page_size);
1448 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
1449 priv->hadjust->page_size);
1451 if (priv->vscroll_visible || priv->hscroll_visible) {
1453 priv->scroll_indicator_event_interrupt = 0;
1454 priv->scroll_delay_counter = priv->scrollbar_fade_delay;
1456 hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget), 1.0);
1462 hildon_pannable_area_launch_fade_timeout (HildonPannableArea * area,
1465 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (area)->priv;
1467 priv->scroll_indicator_alpha = alpha;
1469 if (!priv->scroll_indicator_timeout)
1470 priv->scroll_indicator_timeout =
1471 gdk_threads_add_timeout (SCROLL_FADE_TIMEOUT,
1472 (GSourceFunc) hildon_pannable_area_scroll_indicator_fade,
1477 hildon_pannable_area_adjust_changed (HildonPannableArea * area,
1480 if (GTK_WIDGET_REALIZED (area))
1481 hildon_pannable_area_refresh (area);
1485 hildon_pannable_area_adjust_value_changed (HildonPannableArea * area,
1488 if (GTK_WIDGET_REALIZED (area)) {
1489 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (area)->priv;
1491 hildon_pannable_area_redraw (area);
1493 if ((priv->vscroll_visible) || (priv->hscroll_visible)) {
1494 priv->scroll_indicator_event_interrupt = 0;
1495 priv->scroll_delay_counter = priv->scrollbar_fade_delay;
1497 hildon_pannable_area_launch_fade_timeout (area, 1.0);
1503 hildon_pannable_area_redraw (HildonPannableArea * area)
1505 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (area)->priv;
1507 /* Redraw scroll indicators */
1508 if (GTK_WIDGET_DRAWABLE (area)) {
1509 if (priv->hscroll_visible) {
1510 gdk_window_invalidate_rect (GTK_WIDGET (area)->window,
1511 &priv->hscroll_rect, FALSE);
1514 if (priv->vscroll_visible) {
1515 gdk_window_invalidate_rect (GTK_WIDGET (area)->window,
1516 &priv->vscroll_rect, FALSE);
1522 hildon_pannable_area_scroll_indicator_fade(HildonPannableArea * area)
1524 HildonPannableAreaPrivate *priv = area->priv;
1526 /* if moving do not fade out */
1527 if (((ABS (priv->vel_y)>1.0)||
1528 (ABS (priv->vel_x)>1.0))&&(!priv->clicked)) {
1533 if (priv->scroll_indicator_event_interrupt) {
1534 /* Stop a fade out, and fade back in */
1535 if (priv->scroll_indicator_alpha > 0.9) {
1536 priv->scroll_indicator_alpha = 1.0;
1537 priv->scroll_indicator_timeout = 0;
1541 priv->scroll_indicator_alpha += 0.2;
1542 hildon_pannable_area_redraw (area);
1548 if ((priv->scroll_indicator_alpha > 0.9) &&
1549 (priv->scroll_delay_counter > 0)) {
1550 priv->scroll_delay_counter--;
1555 if (!priv->scroll_indicator_event_interrupt) {
1556 /* Continue fade out */
1557 if (priv->scroll_indicator_alpha < 0.1) {
1558 priv->scroll_indicator_timeout = 0;
1559 priv->scroll_indicator_alpha = 0.0;
1563 priv->scroll_indicator_alpha -= 0.2;
1564 hildon_pannable_area_redraw (area);
1574 hildon_pannable_area_expose_event (GtkWidget * widget,
1575 GdkEventExpose * event)
1578 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1579 #if USE_CAIRO_SCROLLBARS == 1
1580 GdkColor back_color = widget->style->bg[GTK_STATE_NORMAL];
1581 GdkColor scroll_color = widget->style->base[GTK_STATE_SELECTED];
1582 #else /* USE_CAIRO_SCROLLBARS */
1583 GdkColor back_color = widget->style->bg[GTK_STATE_NORMAL];
1584 GdkColor scroll_color = widget->style->fg[GTK_STATE_INSENSITIVE];
1587 if (gtk_bin_get_child (GTK_BIN (widget))) {
1589 if (priv->scroll_indicator_alpha > 0.1) {
1590 if (priv->vscroll_visible) {
1591 hildon_pannable_draw_vscroll (widget, &back_color, &scroll_color);
1593 if (priv->hscroll_visible) {
1594 hildon_pannable_draw_hscroll (widget, &back_color, &scroll_color);
1598 /* draw overshooting rectangles */
1599 if (priv->overshot_dist_y > 0) {
1600 gint overshot_height;
1602 overshot_height = MIN (priv->overshot_dist_y, widget->allocation.height -
1603 (priv->hscroll_visible ? priv->hscroll_rect.height : 0));
1605 gdk_draw_rectangle (widget->window,
1606 widget->style->bg_gc[GTK_STATE_NORMAL],
1610 widget->allocation.width -
1611 (priv->vscroll_visible ? priv->vscroll_rect.width : 0),
1613 } else if (priv->overshot_dist_y < 0) {
1614 gint overshot_height;
1618 MAX (priv->overshot_dist_y,
1619 -(widget->allocation.height -
1620 (priv->hscroll_visible ? priv->hscroll_rect.height : 0)));
1622 overshot_y = MAX (widget->allocation.height +
1624 (priv->hscroll_visible ? priv->hscroll_rect.height : 0), 0);
1626 gdk_draw_rectangle (widget->window,
1627 widget->style->bg_gc[GTK_STATE_NORMAL],
1631 widget->allocation.width -
1632 priv->vscroll_rect.width,
1636 if (priv->overshot_dist_x > 0) {
1637 gint overshot_width;
1639 overshot_width = MIN (priv->overshot_dist_x, widget->allocation.width -
1640 (priv->vscroll_visible ? priv->vscroll_rect.width : 0));
1642 gdk_draw_rectangle (widget->window,
1643 widget->style->bg_gc[GTK_STATE_NORMAL],
1648 widget->allocation.height -
1649 (priv->hscroll_visible ? priv->hscroll_rect.height : 0));
1650 } else if (priv->overshot_dist_x < 0) {
1651 gint overshot_width;
1655 MAX (priv->overshot_dist_x,
1656 -(widget->allocation.width -
1657 (priv->vscroll_visible ? priv->vscroll_rect.width : 0)));
1659 overshot_x = MAX (widget->allocation.width +
1661 (priv->vscroll_visible ? priv->vscroll_rect.width : 0), 0);
1663 gdk_draw_rectangle (widget->window,
1664 widget->style->bg_gc[GTK_STATE_NORMAL],
1669 widget->allocation.height -
1670 priv->hscroll_rect.height);
1675 if (G_UNLIKELY (priv->initial_effect)) {
1677 hildon_pannable_area_initial_effect (widget);
1679 priv->initial_effect = FALSE;
1682 return GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->expose_event (widget, event);
1686 hildon_pannable_area_get_topmost (GdkWindow * window,
1688 gint * tx, gint * ty,
1691 /* Find the GdkWindow at the given point, by recursing from a given
1692 * parent GdkWindow. Optionally return the co-ordinates transformed
1693 * relative to the child window.
1696 GList *c, *children;
1697 GdkWindow *selected_window = NULL;
1699 gdk_drawable_get_size (GDK_DRAWABLE (window), &width, &height);
1700 if ((x < 0) || (x >= width) || (y < 0) || (y >= height))
1703 children = gdk_window_peek_children (window);
1710 selected_window = window;
1713 for (c = children; c; c = c->next) {
1714 GdkWindow *child = (GdkWindow *) c->data;
1717 gdk_drawable_get_size (GDK_DRAWABLE (child), &width, &height);
1718 gdk_window_get_position (child, &wx, &wy);
1720 if ((x >= wx) && (x < (wx + width)) && (y >= wy) && (y < (wy + height)) &&
1721 (gdk_window_is_visible (child))) {
1723 if (gdk_window_peek_children (child)) {
1724 selected_window = hildon_pannable_area_get_topmost (child, x-wx, y-wy,
1726 if (!selected_window) {
1731 selected_window = child;
1734 if ((gdk_window_get_events (child)&mask)) {
1739 selected_window = child;
1745 return selected_window;
1749 synth_crossing (GdkWindow * child,
1751 gint x_root, gint y_root,
1752 guint32 time, gboolean in)
1754 GdkEventCrossing *crossing_event;
1755 GdkEventType type = in ? GDK_ENTER_NOTIFY : GDK_LEAVE_NOTIFY;
1757 /* Send synthetic enter event */
1758 crossing_event = (GdkEventCrossing *) gdk_event_new (type);
1759 ((GdkEventAny *) crossing_event)->type = type;
1760 ((GdkEventAny *) crossing_event)->window = g_object_ref (child);
1761 ((GdkEventAny *) crossing_event)->send_event = FALSE;
1762 crossing_event->subwindow = g_object_ref (child);
1763 crossing_event->time = time;
1764 crossing_event->x = x;
1765 crossing_event->y = y;
1766 crossing_event->x_root = x_root;
1767 crossing_event->y_root = y_root;
1768 crossing_event->mode = GDK_CROSSING_NORMAL;
1769 crossing_event->detail = GDK_NOTIFY_UNKNOWN;
1770 crossing_event->focus = FALSE;
1771 crossing_event->state = 0;
1772 gdk_event_put ((GdkEvent *) crossing_event);
1773 gdk_event_free ((GdkEvent *) crossing_event);
1777 hildon_pannable_area_button_press_cb (GtkWidget * widget,
1778 GdkEventButton * event)
1781 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1783 if ((!priv->enabled) || (event->button != 1) ||
1784 ((event->time == priv->last_time) &&
1785 (priv->last_type == 1)) || (gtk_bin_get_child (GTK_BIN (widget)) == NULL))
1788 priv->scroll_indicator_event_interrupt = 1;
1790 hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget),
1791 priv->scroll_indicator_alpha);
1793 priv->last_time = event->time;
1794 priv->last_type = 1;
1796 priv->scroll_to_x = -1;
1797 priv->scroll_to_y = -1;
1799 if (priv->clicked && priv->child) {
1800 /* Widget stole focus on last click, send crossing-out event */
1801 synth_crossing (priv->child, 0, 0, event->x_root, event->y_root,
1802 event->time, FALSE);
1810 /* Don't allow a click if we're still moving fast */
1811 if ((ABS (priv->vel_x) <= (priv->vmax * priv->vfast_factor)) &&
1812 (ABS (priv->vel_y) <= (priv->vmax * priv->vfast_factor)))
1814 hildon_pannable_area_get_topmost (gtk_bin_get_child (GTK_BIN (widget))->window,
1815 event->x, event->y, &x, &y, GDK_BUTTON_PRESS_MASK);
1819 priv->clicked = TRUE;
1821 /* Stop scrolling on mouse-down (so you can flick, then hold to stop) */
1827 gdk_drawable_get_size (priv->child, &priv->child_width,
1828 &priv->child_height);
1829 priv->last_in = TRUE;
1831 g_object_add_weak_pointer ((GObject *) priv->child,
1832 (gpointer) & priv->child);
1834 event = (GdkEventButton *) gdk_event_copy ((GdkEvent *) event);
1840 synth_crossing (priv->child, x, y, event->x_root,
1841 event->y_root, event->time, TRUE);
1843 /* Send synthetic click (button press/release) event */
1844 ((GdkEventAny *) event)->window = g_object_ref (priv->child);
1846 gdk_event_put ((GdkEvent *) event);
1847 gdk_event_free ((GdkEvent *) event);
1855 hildon_pannable_area_check_scrollbars (HildonPannableArea * area)
1857 HildonPannableAreaPrivate *priv = area->priv;
1858 gboolean prev_hscroll_visible, prev_vscroll_visible;
1860 prev_hscroll_visible = priv->hscroll_visible;
1861 prev_vscroll_visible = priv->vscroll_visible;
1863 if (!gtk_bin_get_child (GTK_BIN (area))) {
1864 priv->vscroll_visible = FALSE;
1865 priv->hscroll_visible = FALSE;
1867 switch (priv->hscrollbar_policy) {
1868 case GTK_POLICY_ALWAYS:
1869 priv->hscroll_visible = TRUE;
1871 case GTK_POLICY_NEVER:
1872 priv->hscroll_visible = FALSE;
1875 priv->hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
1876 priv->hadjust->page_size);
1879 switch (priv->vscrollbar_policy) {
1880 case GTK_POLICY_ALWAYS:
1881 priv->vscroll_visible = TRUE;
1883 case GTK_POLICY_NEVER:
1884 priv->vscroll_visible = FALSE;
1887 priv->vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
1888 priv->vadjust->page_size);
1891 /* Store the vscroll/hscroll areas for redrawing */
1892 if (priv->vscroll_visible) {
1893 GtkAllocation *allocation = >K_WIDGET (area)->allocation;
1894 priv->vscroll_rect.x = allocation->width - priv->area_width;
1895 priv->vscroll_rect.y = 0;
1896 priv->vscroll_rect.width = priv->area_width;
1897 priv->vscroll_rect.height = allocation->height -
1898 (priv->hscroll_visible ? priv->area_width : 0);
1900 if (priv->hscroll_visible) {
1901 GtkAllocation *allocation = >K_WIDGET (area)->allocation;
1902 priv->hscroll_rect.y = allocation->height - priv->area_width;
1903 priv->hscroll_rect.x = 0;
1904 priv->hscroll_rect.height = priv->area_width;
1905 priv->hscroll_rect.width = allocation->width -
1906 (priv->vscroll_visible ? priv->area_width : 0);
1910 return ((priv->hscroll_visible != prev_hscroll_visible) ||
1911 (priv->vscroll_visible != prev_vscroll_visible));
1915 hildon_pannable_area_refresh (HildonPannableArea * area)
1917 if (GTK_WIDGET_DRAWABLE (area) &&
1918 hildon_pannable_area_check_scrollbars (area)) {
1919 gtk_widget_queue_resize (GTK_WIDGET (area));
1921 hildon_pannable_area_redraw (area);
1925 /* Scroll by a particular amount (in pixels). Optionally, return if
1926 * the scroll on a particular axis was successful.
1929 hildon_pannable_axis_scroll (HildonPannableArea *area,
1930 GtkAdjustment *adjust,
1934 gint *overshot_dist,
1940 HildonPannableAreaPrivate *priv = area->priv;
1942 dist = gtk_adjustment_get_value (adjust) - inc;
1945 * We use overshot_dist to define the distance of the current overshoot,
1946 * and overshooting to define the direction/whether or not we are overshot
1948 if (!(*overshooting)) {
1950 /* Initiation of the overshoot happens when the finger is released
1951 * and the current position of the pannable contents are out of range
1953 if (dist < adjust->lower) {
1956 dist = adjust->lower;
1958 if (overshoot_max!=0) {
1961 *overshot_dist = CLAMP (*overshot_dist + *vel, 0, overshoot_max);
1962 *vel = MIN (priv->vmax_overshooting, *vel);
1963 gtk_widget_queue_resize (GTK_WIDGET (area));
1967 } else if (dist > adjust->upper - adjust->page_size) {
1970 dist = adjust->upper - adjust->page_size;
1972 if (overshoot_max!=0) {
1975 *overshot_dist = CLAMP (*overshot_dist + *vel, -overshoot_max, 0);
1976 *vel = MAX (-priv->vmax_overshooting, *vel);
1977 gtk_widget_queue_resize (GTK_WIDGET (area));
1982 if ((*scroll_to) != -1) {
1983 if (((inc < 0)&&(*scroll_to <= dist))||
1984 ((inc > 0)&&(*scroll_to >= dist))) {
1992 gtk_adjustment_set_value (adjust, dist);
1994 if (!priv->clicked) {
1996 /* When the overshoot has started we continue for
1997 * PROP_BOUNCE_STEPS more steps into the overshoot before we
1998 * reverse direction. The deceleration factor is calculated
1999 * based on the percentage distance from the first item with
2000 * each iteration, therefore always returning us to the
2001 * top/bottom most element
2003 if (*overshot_dist > 0) {
2005 if ((*overshooting < priv->bounce_steps) && (*vel > 0)) {
2007 *vel = (((gdouble)*overshot_dist)/overshoot_max) * (*vel);
2008 } else if ((*overshooting >= priv->bounce_steps) && (*vel > 0)) {
2010 } else if ((*overshooting > 1) && (*vel < 0)) {
2011 /* we add the MIN in order to avoid very small speeds */
2012 *vel = MIN ((((gdouble)*overshot_dist)*0.4) * -1, -2.0);
2015 *overshot_dist = CLAMP (*overshot_dist + *vel, 0, overshoot_max);
2017 gtk_widget_queue_resize (GTK_WIDGET (area));
2019 } else if (*overshot_dist < 0) {
2021 if ((*overshooting < priv->bounce_steps) && (*vel < 0)) {
2023 *vel = (((gdouble)*overshot_dist)/overshoot_max) * (*vel) * -1;
2024 } else if ((*overshooting >= priv->bounce_steps) && (*vel < 0)) {
2026 } else if ((*overshooting > 1) && (*vel > 0)) {
2027 /* we add the MAX in order to avoid very small speeds */
2028 *vel = MAX ((((gdouble)*overshot_dist)*0.4) * -1, 2.0);
2031 *overshot_dist = CLAMP (*overshot_dist + (*vel), -overshoot_max, 0);
2033 gtk_widget_queue_resize (GTK_WIDGET (area));
2038 gtk_widget_queue_resize (GTK_WIDGET (area));
2042 gint overshot_dist_old = *overshot_dist;
2044 if (*overshot_dist > 0) {
2045 *overshot_dist = CLAMP ((*overshot_dist) + inc, 0, overshoot_max);
2046 } else if (*overshot_dist < 0) {
2047 *overshot_dist = CLAMP ((*overshot_dist) + inc, -1 * overshoot_max, 0);
2050 gtk_adjustment_set_value (adjust, dist);
2053 if (*overshot_dist != overshot_dist_old)
2054 gtk_widget_queue_resize (GTK_WIDGET (area));
2060 hildon_pannable_area_scroll (HildonPannableArea *area,
2061 gdouble x, gdouble y)
2064 HildonPannableAreaPrivate *priv = area->priv;
2065 gboolean hscroll_visible, vscroll_visible;
2067 if (gtk_bin_get_child (GTK_BIN (area)) == NULL)
2070 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
2071 priv->vadjust->page_size);
2072 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
2073 priv->hadjust->page_size);
2078 if (vscroll_visible) {
2079 hildon_pannable_axis_scroll (area, priv->vadjust, &priv->vel_y, y,
2080 &priv->overshooting_y, &priv->overshot_dist_y,
2081 &priv->scroll_to_y, priv->vovershoot_max, &sy);
2086 if (hscroll_visible) {
2087 hildon_pannable_axis_scroll (area, priv->hadjust, &priv->vel_x, x,
2088 &priv->overshooting_x, &priv->overshot_dist_x,
2089 &priv->scroll_to_x, priv->hovershoot_max, &sx);
2094 /* If the scroll on a particular axis wasn't succesful, reset the
2095 * initial scroll position to the new mouse co-ordinate. This means
2096 * when you get to the top of the page, dragging down works immediately.
2098 if (priv->mode == HILDON_PANNABLE_AREA_MODE_ACCEL) {
2110 hildon_pannable_area_timeout (HildonPannableArea * area)
2112 HildonPannableAreaPrivate *priv = area->priv;
2114 if ((!priv->enabled) || (priv->mode == HILDON_PANNABLE_AREA_MODE_PUSH)) {
2120 if (!priv->clicked) {
2121 /* Decelerate gradually when pointer is raised */
2122 if ((!priv->overshot_dist_y) &&
2123 (!priv->overshot_dist_x)) {
2125 /* in case we move to a specific point do not decelerate when arriving */
2126 if ((priv->scroll_to_x != -1)||(priv->scroll_to_y != -1)) {
2128 if (ABS (priv->vel_x) >= 1.5) {
2129 priv->vel_x *= priv->decel;
2132 if (ABS (priv->vel_y) >= 1.5) {
2133 priv->vel_y *= priv->decel;
2137 if ((!priv->low_friction_mode) ||
2138 ((priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ) &&
2139 (ABS (priv->vel_x) < 0.8*priv->vmax)))
2140 priv->vel_x *= priv->decel;
2142 if ((!priv->low_friction_mode) ||
2143 ((priv->mov_mode&HILDON_MOVEMENT_MODE_VERT) &&
2144 (ABS (priv->vel_y) < 0.8*priv->vmax)))
2145 priv->vel_y *= priv->decel;
2147 if ((ABS (priv->vel_x) < 1.0) && (ABS (priv->vel_y) < 1.0)) {
2156 } else if (priv->mode == HILDON_PANNABLE_AREA_MODE_AUTO) {
2162 hildon_pannable_area_scroll (area, priv->vel_x, priv->vel_y);
2168 hildon_pannable_area_calculate_velocity (gdouble *vel,
2172 gdouble drag_inertia,
2178 if (ABS (dist) >= RATIO_TOLERANCE) {
2179 rawvel = (dist / ABS (delta)) * force;
2180 *vel = *vel * (1 - drag_inertia) +
2181 rawvel * drag_inertia;
2182 *vel = *vel > 0 ? MIN (*vel, vmax)
2183 : MAX (*vel, -1 * vmax);
2188 hildon_pannable_area_motion_event_scroll_timeout (HildonPannableArea *area)
2190 HildonPannableAreaPrivate *priv = area->priv;
2192 if ((priv->motion_x != 0)||(priv->motion_y != 0))
2193 hildon_pannable_area_scroll (area, priv->motion_x, priv->motion_y);
2195 priv->motion_event_scroll_timeout = 0;
2201 hildon_pannable_area_motion_event_scroll (HildonPannableArea *area,
2202 gdouble x, gdouble y)
2204 HildonPannableAreaPrivate *priv = area->priv;
2206 if (priv->motion_event_scroll_timeout) {
2208 priv->motion_x += x;
2209 priv->motion_y += y;
2213 /* we do not delay the first event but the next ones */
2214 hildon_pannable_area_scroll (area, x, y);
2219 priv->motion_event_scroll_timeout = gdk_threads_add_timeout
2220 ((gint) (1000.0 / (gdouble) MOTION_EVENTS_PER_SECOND),
2221 (GSourceFunc) hildon_pannable_area_motion_event_scroll_timeout, area);
2226 hildon_pannable_area_motion_notify_cb (GtkWidget * widget,
2227 GdkEventMotion * event)
2229 HildonPannableArea *area = HILDON_PANNABLE_AREA (widget);
2230 HildonPannableAreaPrivate *priv = area->priv;
2234 if (gtk_bin_get_child (GTK_BIN (widget)) == NULL)
2237 if ((!priv->enabled) || (!priv->clicked) ||
2238 ((event->time == priv->last_time) && (priv->last_type == 2))) {
2239 gdk_window_get_pointer (widget->window, NULL, NULL, 0);
2243 if (priv->last_type == 1) {
2244 priv->first_drag = TRUE;
2247 x = event->x - priv->x;
2248 y = event->y - priv->y;
2250 if (priv->first_drag && (!priv->moved) &&
2251 ((ABS (x) > (priv->panning_threshold))
2252 || (ABS (y) > (priv->panning_threshold)))) {
2257 if (priv->first_drag) {
2258 gboolean vscroll_visible;
2259 gboolean hscroll_visible;
2261 if (ABS (priv->iy - event->y) >=
2262 ABS (priv->ix - event->x)) {
2264 g_signal_emit (area,
2265 pannable_area_signals[VERTICAL_MOVEMENT],
2266 0, (priv->iy > event->y) ?
2267 HILDON_MOVEMENT_UP :
2268 HILDON_MOVEMENT_DOWN,
2269 (gdouble)priv->ix, (gdouble)priv->iy);
2271 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
2272 priv->vadjust->page_size);
2274 if (!((vscroll_visible)&&
2275 (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT))) {
2277 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
2278 priv->hadjust->page_size);
2280 /* even in case we do not have to move we check if this
2281 could be a fake horizontal movement */
2282 if (!((hscroll_visible)&&
2283 (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ)) ||
2284 (ABS (priv->iy - event->y) -
2285 ABS (priv->ix - event->x) >= priv->direction_error_margin))
2286 priv->moved = FALSE;
2290 g_signal_emit (area,
2291 pannable_area_signals[HORIZONTAL_MOVEMENT],
2292 0, (priv->ix > event->x) ?
2293 HILDON_MOVEMENT_LEFT :
2294 HILDON_MOVEMENT_RIGHT,
2295 (gdouble)priv->ix, (gdouble)priv->iy);
2297 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
2298 priv->hadjust->page_size);
2300 if (!((hscroll_visible)&&
2301 (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ))) {
2303 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
2304 priv->vadjust->page_size);
2306 /* even in case we do not have to move we check if this
2307 could be a fake vertical movement */
2308 if (!((vscroll_visible) &&
2309 (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT)) ||
2310 (ABS (priv->ix - event->x) -
2311 ABS (priv->iy - event->y) >= priv->direction_error_margin))
2312 priv->moved = FALSE;
2316 if ((priv->moved)&&(priv->child)) {
2319 pos_x = priv->cx + (event->x - priv->ix);
2320 pos_y = priv->cy + (event->y - priv->iy);
2322 synth_crossing (priv->child, pos_x, pos_y, event->x_root,
2323 event->y_root, event->time, FALSE);
2327 priv->first_drag = FALSE;
2329 if ((priv->mode != HILDON_PANNABLE_AREA_MODE_PUSH) &&
2330 (priv->mode != HILDON_PANNABLE_AREA_MODE_AUTO)) {
2333 priv->idle_id = gdk_threads_add_timeout ((gint)
2334 (1000.0 / (gdouble) priv->sps),
2336 hildon_pannable_area_timeout, area);
2341 switch (priv->mode) {
2342 case HILDON_PANNABLE_AREA_MODE_PUSH:
2343 /* Scroll by the amount of pixels the cursor has moved
2344 * since the last motion event.
2346 hildon_pannable_area_motion_event_scroll (area, x, y);
2350 case HILDON_PANNABLE_AREA_MODE_ACCEL:
2351 /* Set acceleration relative to the initial click */
2352 priv->ex = event->x;
2353 priv->ey = event->y;
2354 priv->vel_x = ((x > 0) ? 1 : -1) *
2356 (gdouble) widget->allocation.width) *
2357 (priv->vmax - priv->vmin)) + priv->vmin);
2358 priv->vel_y = ((y > 0) ? 1 : -1) *
2360 (gdouble) widget->allocation.height) *
2361 (priv->vmax - priv->vmin)) + priv->vmin);
2363 case HILDON_PANNABLE_AREA_MODE_AUTO:
2365 delta = event->time - priv->last_time;
2367 if (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT) {
2368 gdouble dist = event->y - priv->y;
2370 hildon_pannable_area_calculate_velocity (&priv->vel_y,
2382 if (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ) {
2383 gdouble dist = event->x - priv->x;
2385 hildon_pannable_area_calculate_velocity (&priv->vel_x,
2397 hildon_pannable_area_motion_event_scroll (area, x, y);
2399 if (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ)
2401 if (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT)
2409 } else if (priv->child) {
2413 pos_x = priv->cx + (event->x - priv->ix);
2414 pos_y = priv->cy + (event->y - priv->iy);
2416 in = (((0 <= pos_x)&&(priv->child_width >= pos_x)) &&
2417 ((0 <= pos_y)&&(priv->child_height >= pos_y)));
2419 if (((!priv->last_in)&&in)||((priv->last_in)&&(!in))) {
2421 synth_crossing (priv->child, pos_x, pos_y, event->x_root,
2422 event->y_root, event->time, in);
2428 priv->last_time = event->time;
2429 priv->last_type = 2;
2432 /* Send motion notify to child */
2433 event = (GdkEventMotion *) gdk_event_copy ((GdkEvent *) event);
2434 event->x = priv->cx + (event->x - priv->ix);
2435 event->y = priv->cy + (event->y - priv->iy);
2436 event->window = g_object_ref (priv->child);
2437 gdk_event_put ((GdkEvent *) event);
2438 gdk_event_free ((GdkEvent *) event);
2441 gdk_window_get_pointer (widget->window, NULL, NULL, 0);
2447 hildon_pannable_leave_notify_event (GtkWidget *widget,
2448 GdkEventCrossing *event)
2450 HildonPannableArea *area = HILDON_PANNABLE_AREA (widget);
2451 HildonPannableAreaPrivate *priv = area->priv;
2453 if ((priv->child)&&(priv->last_in)) {
2454 priv->last_in = FALSE;
2456 synth_crossing (priv->child, 0, 0, event->x_root,
2457 event->y_root, event->time, FALSE);
2464 hildon_pannable_area_button_release_cb (GtkWidget * widget,
2465 GdkEventButton * event)
2467 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
2471 if (((event->time == priv->last_time) && (priv->last_type == 3))
2472 || (gtk_bin_get_child (GTK_BIN (widget)) == NULL)
2473 || (!priv->clicked) || (!priv->enabled) || (event->button != 1))
2476 priv->scroll_indicator_event_interrupt = 0;
2477 priv->scroll_delay_counter = priv->scrollbar_fade_delay;
2479 /* move all the way to the last position */
2480 if (priv->motion_event_scroll_timeout) {
2481 g_source_remove (priv->motion_event_scroll_timeout);
2482 hildon_pannable_area_motion_event_scroll_timeout (HILDON_PANNABLE_AREA (widget));
2487 if (priv->last_type == 2) {
2488 gdouble delta = event->time - priv->last_time;
2490 if (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT) {
2491 gdouble dist = event->y - priv->y;
2493 if (ABS (dist) >= 1.0) {
2494 hildon_pannable_area_calculate_velocity (&priv->vel_y,
2502 priv->motion_y = dist;
2503 hildon_pannable_area_motion_event_scroll_timeout (HILDON_PANNABLE_AREA (widget));
2505 if (delta >= CURSOR_STOPPED_TIMEOUT) {
2511 if (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ) {
2512 gdouble dist = event->x - priv->x;
2514 if (ABS (dist) >= 1.0) {
2515 hildon_pannable_area_calculate_velocity (&priv->vel_x,
2522 priv->motion_x = dist;
2523 hildon_pannable_area_motion_event_scroll_timeout (HILDON_PANNABLE_AREA (widget));
2525 if (delta >= CURSOR_STOPPED_TIMEOUT) {
2532 if ((ABS (priv->vel_y) > 1.0)||
2533 (ABS (priv->vel_x) > 1.0)) {
2534 priv->scroll_indicator_alpha = 1.0;
2537 hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget),
2538 priv->scroll_indicator_alpha);
2540 priv->clicked = FALSE;
2542 if (priv->mode == HILDON_PANNABLE_AREA_MODE_AUTO ||
2543 priv->mode == HILDON_PANNABLE_AREA_MODE_ACCEL) {
2545 /* If overshoot has been initiated with a finger down, on release set max speed */
2546 if (priv->overshot_dist_y != 0) {
2547 priv->overshooting_y = priv->bounce_steps; /* Hack to stop a bounce in the finger down case */
2548 priv->vel_y = priv->vmax_overshooting;
2551 if (priv->overshot_dist_x != 0) {
2552 priv->overshooting_x = priv->bounce_steps; /* Hack to stop a bounce in the finger down case */
2553 priv->vel_x = priv->vmax_overshooting;
2556 if ((ABS (priv->vel_y) >= priv->vmin) ||
2557 (ABS (priv->vel_x) >= priv->vmin)) {
2560 priv->idle_id = gdk_threads_add_timeout ((gint) (1000.0 / (gdouble) priv->sps),
2562 hildon_pannable_area_timeout, widget);
2566 priv->last_time = event->time;
2567 priv->last_type = 3;
2570 priv->moved = FALSE;
2575 hildon_pannable_area_get_topmost (gtk_bin_get_child (GTK_BIN (widget))->window,
2576 event->x, event->y, &x, &y, GDK_BUTTON_RELEASE_MASK);
2578 event = (GdkEventButton *) gdk_event_copy ((GdkEvent *) event);
2582 /* Leave the widget if we've moved - This doesn't break selection,
2583 * but stops buttons from being clicked.
2585 if ((child != priv->child) || (priv->moved)) {
2586 /* Send synthetic leave event */
2587 synth_crossing (priv->child, x, y, event->x_root,
2588 event->y_root, event->time, FALSE);
2589 /* Send synthetic button release event */
2590 ((GdkEventAny *) event)->window = g_object_ref (priv->child);
2591 gdk_event_put ((GdkEvent *) event);
2593 /* Send synthetic button release event */
2594 ((GdkEventAny *) event)->window = g_object_ref (child);
2595 gdk_event_put ((GdkEvent *) event);
2596 /* Send synthetic leave event */
2597 synth_crossing (priv->child, x, y, event->x_root,
2598 event->y_root, event->time, FALSE);
2600 g_object_remove_weak_pointer ((GObject *) priv->child,
2601 (gpointer) & priv->child);
2603 priv->moved = FALSE;
2604 gdk_event_free ((GdkEvent *) event);
2609 /* utility event handler */
2611 hildon_pannable_area_scroll_cb (GtkWidget *widget,
2612 GdkEventScroll *event)
2614 GtkAdjustment *adj = NULL;
2615 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
2617 if ((!priv->enabled) ||
2618 (gtk_bin_get_child (GTK_BIN (widget)) == NULL))
2621 priv->scroll_indicator_event_interrupt = 0;
2622 priv->scroll_delay_counter = priv->scrollbar_fade_delay + 20;
2624 hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget), 1.0);
2626 /* Stop inertial scrolling */
2627 if (priv->idle_id) {
2630 priv->overshooting_x = 0;
2631 priv->overshooting_y = 0;
2633 if ((priv->overshot_dist_x>0)||(priv->overshot_dist_y>0)) {
2634 priv->overshot_dist_x = 0;
2635 priv->overshot_dist_y = 0;
2637 gtk_widget_queue_resize (GTK_WIDGET (widget));
2640 g_source_remove (priv->idle_id);
2644 if (event->direction == GDK_SCROLL_UP || event->direction == GDK_SCROLL_DOWN)
2645 adj = priv->vadjust;
2647 adj = priv->hadjust;
2651 gdouble delta, new_value;
2653 /* from gtkrange.c calculate delta*/
2654 delta = pow (adj->page_size, 2.0 / 3.0);
2656 if (event->direction == GDK_SCROLL_UP ||
2657 event->direction == GDK_SCROLL_LEFT)
2660 new_value = CLAMP (adj->value + delta, adj->lower, adj->upper - adj->page_size);
2662 gtk_adjustment_set_value (adj, new_value);
2669 hildon_pannable_area_child_mapped (GtkWidget *widget,
2673 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (user_data)->priv;
2675 if (priv->event_window != NULL && priv->enabled)
2676 gdk_window_raise (priv->event_window);
2680 hildon_pannable_area_add (GtkContainer *container, GtkWidget *child)
2682 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (container)->priv;
2684 g_return_if_fail (gtk_bin_get_child (GTK_BIN (container)) == NULL);
2686 gtk_widget_set_parent (child, GTK_WIDGET (container));
2687 GTK_BIN (container)->child = child;
2689 g_signal_connect_after (child, "map-event",
2690 G_CALLBACK (hildon_pannable_area_child_mapped),
2693 if (!gtk_widget_set_scroll_adjustments (child, priv->hadjust, priv->vadjust)) {
2694 g_warning ("%s: cannot add non scrollable widget, "
2695 "wrap it in a viewport", __FUNCTION__);
2700 hildon_pannable_area_remove (GtkContainer *container, GtkWidget *child)
2702 g_return_if_fail (HILDON_IS_PANNABLE_AREA (container));
2703 g_return_if_fail (child != NULL);
2704 g_return_if_fail (gtk_bin_get_child (GTK_BIN (container)) == child);
2706 gtk_widget_set_scroll_adjustments (child, NULL, NULL);
2708 g_signal_handlers_disconnect_by_func (child,
2709 hildon_pannable_area_child_mapped,
2712 /* chain parent class handler to remove child */
2713 GTK_CONTAINER_CLASS (hildon_pannable_area_parent_class)->remove (container, child);
2717 hildon_pannable_calculate_vel_factor (HildonPannableArea * self)
2719 HildonPannableAreaPrivate *priv = self->priv;
2724 n = ceil (priv->sps * priv->scroll_time);
2726 for (i = 0; i < n && fct_i >= RATIO_TOLERANCE; i++) {
2727 fct_i *= priv->decel;
2731 priv->vel_factor = fct;
2735 * hildon_pannable_area_new:
2737 * Create a new pannable area widget
2739 * Returns: the newly created #HildonPannableArea
2745 hildon_pannable_area_new (void)
2747 return g_object_new (HILDON_TYPE_PANNABLE_AREA, NULL);
2751 * hildon_pannable_area_new_full:
2752 * @mode: #HildonPannableAreaMode
2753 * @enabled: Value for the enabled property
2754 * @vel_min: Value for the velocity-min property
2755 * @vel_max: Value for the velocity-max property
2756 * @decel: Value for the deceleration property
2757 * @sps: Value for the sps property
2759 * Create a new #HildonPannableArea widget and set various properties
2761 * returns: the newly create #HildonPannableArea
2767 hildon_pannable_area_new_full (gint mode, gboolean enabled,
2768 gdouble vel_min, gdouble vel_max,
2769 gdouble decel, guint sps)
2771 return g_object_new (HILDON_TYPE_PANNABLE_AREA,
2774 "velocity_min", vel_min,
2775 "velocity_max", vel_max,
2776 "deceleration", decel, "sps", sps, NULL);
2780 * hildon_pannable_area_add_with_viewport:
2781 * @area: A #HildonPannableArea
2782 * @child: Child widget to add to the viewport
2784 * Convenience function used to add a child to a #GtkViewport, and add the
2785 * viewport to the scrolled window.
2791 hildon_pannable_area_add_with_viewport (HildonPannableArea * area,
2795 GtkWidget *viewport;
2797 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
2798 g_return_if_fail (GTK_IS_WIDGET (child));
2799 g_return_if_fail (child->parent == NULL);
2801 bin = GTK_BIN (area);
2803 if (bin->child != NULL)
2805 g_return_if_fail (GTK_IS_VIEWPORT (bin->child));
2806 g_return_if_fail (GTK_BIN (bin->child)->child == NULL);
2808 viewport = bin->child;
2812 HildonPannableAreaPrivate *priv = area->priv;
2814 viewport = gtk_viewport_new (priv->hadjust,
2816 gtk_viewport_set_shadow_type (GTK_VIEWPORT (viewport), GTK_SHADOW_NONE);
2817 gtk_container_add (GTK_CONTAINER (area), viewport);
2820 gtk_widget_show (viewport);
2821 gtk_container_add (GTK_CONTAINER (viewport), child);
2825 * hildon_pannable_area_scroll_to:
2826 * @area: A #HildonPannableArea.
2827 * @x: The x coordinate of the destination point or -1 to ignore this axis.
2828 * @y: The y coordinate of the destination point or -1 to ignore this axis.
2830 * Smoothly scrolls @area to ensure that (@x, @y) is a visible point
2831 * on the widget. To move in only one coordinate, you must set the other one
2832 * to -1. Notice that, in %HILDON_PANNABLE_AREA_MODE_PUSH mode, this function
2833 * works just like hildon_pannable_area_jump_to().
2835 * This function is useful if you need to present the user with a particular
2836 * element inside a scrollable widget, like #GtkTreeView. For instance,
2837 * the following example shows how to scroll inside a #GtkTreeView to
2838 * make visible an item, indicated by the #GtkTreeIter @iter.
2842 * GtkTreePath *path;
2843 * GdkRectangle *rect;
2845 * path = gtk_tree_model_get_path (model, &iter);
2846 * gtk_tree_view_get_background_area (GTK_TREE_VIEW (treeview),
2847 * path, NULL, &rect);
2848 * gtk_tree_view_convert_bin_window_to_tree_coords (GTK_TREE_VIEW (treeview),
2849 * 0, rect.y, NULL, &y);
2850 * hildon_pannable_area_scroll_to (panarea, -1, y);
2851 * gtk_tree_path_free (path);
2855 * If you want to present a child widget in simpler scenarios,
2856 * use hildon_pannable_area_scroll_to_child() instead.
2858 * There is a precondition to this function: the widget must be
2859 * already realized. Check the hildon_pannable_area_jump_to_child() for
2860 * more tips regarding how to call this function during
2866 hildon_pannable_area_scroll_to (HildonPannableArea *area,
2867 const gint x, const gint y)
2869 HildonPannableAreaPrivate *priv;
2871 gint dist_x, dist_y;
2872 gboolean hscroll_visible, vscroll_visible;
2874 g_return_if_fail (GTK_WIDGET_REALIZED (area));
2875 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
2879 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
2880 priv->vadjust->page_size);
2881 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
2882 priv->hadjust->page_size);
2884 if (((!vscroll_visible)&&(!hscroll_visible)) ||
2885 (x == -1 && y == -1)) {
2889 if (priv->mode == HILDON_PANNABLE_AREA_MODE_PUSH)
2890 hildon_pannable_area_jump_to (area, x, y);
2892 width = priv->hadjust->upper - priv->hadjust->lower;
2893 height = priv->vadjust->upper - priv->vadjust->lower;
2895 g_return_if_fail (x < width || y < height);
2897 if ((x > -1)&&(hscroll_visible)) {
2898 priv->scroll_to_x = x - priv->hadjust->page_size/2;
2899 dist_x = priv->scroll_to_x - priv->hadjust->value;
2901 priv->scroll_to_x = -1;
2903 priv->vel_x = - dist_x/priv->vel_factor;
2906 priv->scroll_to_x = -1;
2909 if ((y > -1)&&(vscroll_visible)) {
2910 priv->scroll_to_y = y - priv->vadjust->page_size/2;
2911 dist_y = priv->scroll_to_y - priv->vadjust->value;
2913 priv->scroll_to_y = -1;
2915 priv->vel_y = - dist_y/priv->vel_factor;
2918 priv->scroll_to_y = y;
2921 if ((priv->scroll_to_y == -1) && (priv->scroll_to_y == -1)) {
2925 hildon_pannable_area_launch_fade_timeout (area, 1.0);
2928 priv->idle_id = gdk_threads_add_timeout ((gint) (1000.0 / (gdouble) priv->sps),
2930 hildon_pannable_area_timeout, area);
2934 * hildon_pannable_area_jump_to:
2935 * @area: A #HildonPannableArea.
2936 * @x: The x coordinate of the destination point or -1 to ignore this axis.
2937 * @y: The y coordinate of the destination point or -1 to ignore this axis.
2939 * Jumps the position of @area to ensure that (@x, @y) is a visible
2940 * point in the widget. In order to move in only one coordinate, you
2941 * must set the other one to -1. See hildon_pannable_area_scroll_to()
2942 * function for an example of how to calculate the position of
2943 * children in scrollable widgets like #GtkTreeview.
2945 * There is a precondition to this function: the widget must be
2946 * already realized. Check the hildon_pannable_area_jump_to_child() for
2947 * more tips regarding how to call this function during
2953 hildon_pannable_area_jump_to (HildonPannableArea *area,
2954 const gint x, const gint y)
2956 HildonPannableAreaPrivate *priv;
2959 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
2960 g_return_if_fail (GTK_WIDGET_REALIZED (area));
2961 g_return_if_fail (x >= -1 && y >= -1);
2963 if (x == -1 && y == -1) {
2969 width = priv->hadjust->upper - priv->hadjust->lower;
2970 height = priv->vadjust->upper - priv->vadjust->lower;
2972 g_return_if_fail (x < width || y < height);
2975 gdouble jump_to = x - priv->hadjust->page_size/2;
2977 if (jump_to > priv->hadjust->upper - priv->hadjust->page_size) {
2978 jump_to = priv->hadjust->upper - priv->hadjust->page_size;
2981 gtk_adjustment_set_value (priv->hadjust, jump_to);
2985 gdouble jump_to = y - priv->vadjust->page_size/2;
2987 if (jump_to > priv->vadjust->upper - priv->vadjust->page_size) {
2988 jump_to = priv->vadjust->upper - priv->vadjust->page_size;
2991 gtk_adjustment_set_value (priv->vadjust, jump_to);
2994 priv->scroll_indicator_alpha = 1.0;
2996 if (priv->scroll_indicator_timeout) {
2997 g_source_remove (priv->scroll_indicator_timeout);
2998 priv->scroll_indicator_timeout = 0;
3001 if (priv->idle_id) {
3004 priv->overshooting_x = 0;
3005 priv->overshooting_y = 0;
3007 if ((priv->overshot_dist_x>0)||(priv->overshot_dist_y>0)) {
3008 priv->overshot_dist_x = 0;
3009 priv->overshot_dist_y = 0;
3011 gtk_widget_queue_resize (GTK_WIDGET (area));
3014 g_source_remove (priv->idle_id);
3020 * hildon_pannable_area_scroll_to_child:
3021 * @area: A #HildonPannableArea.
3022 * @child: A #GtkWidget, descendant of @area.
3024 * Smoothly scrolls until @child is visible inside @area. @child must
3025 * be a descendant of @area. If you need to scroll inside a scrollable
3026 * widget, e.g., #GtkTreeview, see hildon_pannable_area_scroll_to().
3028 * There is a precondition to this function: the widget must be
3029 * already realized. Check the hildon_pannable_area_jump_to_child() for
3030 * more tips regarding how to call this function during
3036 hildon_pannable_area_scroll_to_child (HildonPannableArea *area, GtkWidget *child)
3038 GtkWidget *bin_child;
3041 g_return_if_fail (GTK_WIDGET_REALIZED (area));
3042 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3043 g_return_if_fail (GTK_IS_WIDGET (child));
3044 g_return_if_fail (gtk_widget_is_ancestor (child, GTK_WIDGET (area)));
3046 if (GTK_BIN (area)->child == NULL)
3049 /* We need to get to check the child of the inside the area */
3050 bin_child = GTK_BIN (area)->child;
3052 /* we check if we added a viewport */
3053 if (GTK_IS_VIEWPORT (bin_child)) {
3054 bin_child = GTK_BIN (bin_child)->child;
3057 if (gtk_widget_translate_coordinates (child, bin_child, 0, 0, &x, &y))
3058 hildon_pannable_area_scroll_to (area, x, y);
3062 * hildon_pannable_area_jump_to_child:
3063 * @area: A #HildonPannableArea.
3064 * @child: A #GtkWidget, descendant of @area.
3066 * Jumps to make sure @child is visible inside @area. @child must
3067 * be a descendant of @area. If you want to move inside a scrollable
3068 * widget, like, #GtkTreeview, see hildon_pannable_area_scroll_to().
3070 * There is a precondition to this function: the widget must be
3071 * already realized. You can control if the widget is ready with the
3072 * GTK_WIDGET_REALIZED macro. If you want to call this function during
3073 * the initialization process of the widget do it inside a callback to
3074 * the ::realize signal, using g_signal_connect_after() function.
3079 hildon_pannable_area_jump_to_child (HildonPannableArea *area, GtkWidget *child)
3081 GtkWidget *bin_child;
3084 g_return_if_fail (GTK_WIDGET_REALIZED (area));
3085 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3086 g_return_if_fail (GTK_IS_WIDGET (child));
3087 g_return_if_fail (gtk_widget_is_ancestor (child, GTK_WIDGET (area)));
3089 if (gtk_bin_get_child (GTK_BIN (area)) == NULL)
3092 /* We need to get to check the child of the inside the area */
3093 bin_child = gtk_bin_get_child (GTK_BIN (area));
3095 /* we check if we added a viewport */
3096 if (GTK_IS_VIEWPORT (bin_child)) {
3097 bin_child = gtk_bin_get_child (GTK_BIN (bin_child));
3100 if (gtk_widget_translate_coordinates (child, bin_child, 0, 0, &x, &y))
3101 hildon_pannable_area_jump_to (area, x, y);
3105 * hildon_pannable_get_child_widget_at:
3106 * @area: A #HildonPannableArea.
3107 * @x: horizontal coordinate of the point
3108 * @y: vertical coordinate of the point
3110 * Get the widget at the point (x, y) inside the pannable area. In
3111 * case no widget found it returns NULL.
3113 * returns: the #GtkWidget if we find a widget, NULL in any other case
3118 hildon_pannable_get_child_widget_at (HildonPannableArea *area,
3119 gdouble x, gdouble y)
3121 GdkWindow *window = NULL;
3122 GtkWidget *child_widget = NULL;
3124 window = hildon_pannable_area_get_topmost
3125 (gtk_bin_get_child (GTK_BIN (area))->window,
3126 x, y, NULL, NULL, GDK_ALL_EVENTS_MASK);
3128 gdk_window_get_user_data (window, (gpointer) &child_widget);
3130 return child_widget;
3135 * hildon_pannable_area_get_hadjustment:
3136 * @area: A #HildonPannableArea.
3138 * Returns the horizontal adjustment. This adjustment is the internal
3139 * widget adjustment used to control the animations. Do not modify it
3140 * directly to change the position of the pannable, to do that use the
3141 * pannable API. If you modify the object directly it could cause
3142 * artifacts in the animations.
3144 * returns: The horizontal #GtkAdjustment
3147 hildon_pannable_area_get_hadjustment (HildonPannableArea *area)
3150 g_return_val_if_fail (HILDON_IS_PANNABLE_AREA (area), NULL);
3152 return area->priv->hadjust;
3156 * hildon_pannable_area_get_vadjustment:
3157 * @area: A #HildonPannableArea.
3159 * Returns the vertical adjustment. This adjustment is the internal
3160 * widget adjustment used to control the animations. Do not modify it
3161 * directly to change the position of the pannable, to do that use the
3162 * pannable API. If you modify the object directly it could cause
3163 * artifacts in the animations.
3165 * returns: The vertical #GtkAdjustment
3168 hildon_pannable_area_get_vadjustment (HildonPannableArea *area)
3170 g_return_val_if_fail (HILDON_IS_PANNABLE_AREA (area), NULL);
3172 return area->priv->vadjust;
3177 * hildon_pannable_area_get_size_request_policy:
3178 * @area: A #HildonPannableArea.
3180 * This function returns the current size request policy of the
3181 * widget. That policy controls the way the size_request is done in
3182 * the pannable area. Check
3183 * hildon_pannable_area_set_size_request_policy() for a more detailed
3186 * returns: the policy is currently being used in the widget
3187 * #HildonSizeRequestPolicy.
3189 HildonSizeRequestPolicy
3190 hildon_pannable_area_get_size_request_policy (HildonPannableArea *area)
3192 HildonPannableAreaPrivate *priv;
3194 g_return_val_if_fail (HILDON_IS_PANNABLE_AREA (area), FALSE);
3198 return priv->size_request_policy;
3202 * hildon_pannable_area_set_size_request_policy:
3203 * @area: A #HildonPannableArea.
3204 * @size_request_policy: One of the allowed #HildonSizeRequestPolicy
3206 * This function sets the pannable area size request policy. That
3207 * policy controls the way the size_request is done in the pannable
3208 * area. Pannable can use the size request of its children
3209 * (#HILDON_SIZE_REQUEST_CHILDREN) or the minimum size required for
3210 * the area itself (#HILDON_SIZE_REQUEST_MINIMUM), the latter is the
3211 * default. Recall this size depends on the scrolling policy you are
3212 * requesting to the pannable area, if you set #GTK_POLICY_NEVER this
3213 * parameter will not have any effect with
3214 * #HILDON_SIZE_REQUEST_MINIMUM set.
3218 hildon_pannable_area_set_size_request_policy (HildonPannableArea *area,
3219 HildonSizeRequestPolicy size_request_policy)
3221 HildonPannableAreaPrivate *priv;
3223 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3227 if (priv->size_request_policy == size_request_policy)
3230 priv->size_request_policy = size_request_policy;
3232 gtk_widget_queue_resize (GTK_WIDGET (area));
3234 g_object_notify (G_OBJECT (area), "size-request-policy");