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 PANNABLE_MAX_WIDTH 788
61 #define PANNABLE_MAX_HEIGHT 378
63 G_DEFINE_TYPE (HildonPannableArea, hildon_pannable_area, GTK_TYPE_BIN)
65 #define PANNABLE_AREA_PRIVATE(o) \
66 (G_TYPE_INSTANCE_GET_PRIVATE ((o), HILDON_TYPE_PANNABLE_AREA, \
67 HildonPannableAreaPrivate))
69 struct _HildonPannableAreaPrivate {
70 HildonPannableAreaMode mode;
71 HildonMovementMode mov_mode;
72 GdkWindow *event_window;
73 gdouble x; /* Used to store mouse co-ordinates of the first or */
74 gdouble y; /* previous events in a press-motion pair */
75 gdouble ex; /* Used to store mouse co-ordinates of the last */
76 gdouble ey; /* motion event in acceleration mode */
79 guint32 last_time; /* Last event time, to stop infinite loops */
85 gdouble vmax_overshooting;
92 guint panning_threshold;
93 guint scrollbar_fade_delay;
96 guint direction_error_margin;
102 gint ix; /* Initial click mouse co-ordinates */
104 gint cx; /* Initial click child window mouse co-ordinates */
111 gint overshot_dist_x;
112 gint overshot_dist_y;
115 gdouble scroll_indicator_alpha;
116 gint motion_event_scroll_timeout;
117 gint scroll_indicator_timeout;
118 gint scroll_indicator_event_interrupt;
119 gint scroll_delay_counter;
122 gboolean initial_hint;
123 gboolean initial_effect;
124 gboolean low_friction_mode;
127 gboolean size_request_policy;
128 gboolean hscroll_visible;
129 gboolean vscroll_visible;
130 GdkRectangle hscroll_rect;
131 GdkRectangle vscroll_rect;
132 guint indicator_width;
134 GtkAdjustment *hadjust;
135 GtkAdjustment *vadjust;
137 GtkPolicyType vscrollbar_policy;
138 GtkPolicyType hscrollbar_policy;
140 GdkGC *scrollbars_gc;
150 static guint pannable_area_signals [LAST_SIGNAL] = { 0 };
158 PROP_VEL_MAX_OVERSHOOTING,
159 PROP_VELOCITY_FAST_FACTOR,
163 PROP_PANNING_THRESHOLD,
164 PROP_SCROLLBAR_FADE_DELAY,
167 PROP_DIRECTION_ERROR_MARGIN,
168 PROP_VSCROLLBAR_POLICY,
169 PROP_HSCROLLBAR_POLICY,
174 PROP_LOW_FRICTION_MODE,
175 PROP_SIZE_REQUEST_POLICY,
181 static void hildon_pannable_area_class_init (HildonPannableAreaClass * klass);
182 static void hildon_pannable_area_init (HildonPannableArea * area);
183 static void hildon_pannable_area_get_property (GObject * object,
187 static void hildon_pannable_area_set_property (GObject * object,
189 const GValue * value,
191 static void hildon_pannable_area_dispose (GObject * object);
192 static void hildon_pannable_area_realize (GtkWidget * widget);
193 static void hildon_pannable_area_unrealize (GtkWidget * widget);
194 static void hildon_pannable_area_size_request (GtkWidget * widget,
195 GtkRequisition * requisition);
196 static void hildon_pannable_area_size_allocate (GtkWidget * widget,
197 GtkAllocation * allocation);
198 static void hildon_pannable_area_child_allocate_calculate (GtkWidget * widget,
199 GtkAllocation * allocation,
200 GtkAllocation * child_allocation);
201 static void hildon_pannable_area_style_set (GtkWidget * widget,
202 GtkStyle * previous_style);
203 static void hildon_pannable_area_map (GtkWidget * widget);
204 static void hildon_pannable_area_unmap (GtkWidget * widget);
205 static void hildon_pannable_area_grab_notify (GtkWidget *widget,
206 gboolean was_grabbed,
208 #if USE_CAIRO_SCROLLBARS == 1
209 static void rgb_from_gdkcolor (GdkColor *color, gdouble *r, gdouble *g, gdouble *b);
210 #else /* USE_CAIRO_SCROLLBARS */
211 static void tranparency_color (GdkColor *color,
214 gdouble transparency);
215 #endif /* USE_CAIRO_SCROLLBARS */
216 static void hildon_pannable_draw_vscroll (GtkWidget * widget,
217 GdkColor *back_color,
218 GdkColor *scroll_color);
219 static void hildon_pannable_draw_hscroll (GtkWidget * widget,
220 GdkColor *back_color,
221 GdkColor *scroll_color);
222 static void hildon_pannable_area_initial_effect (GtkWidget * widget);
223 static void hildon_pannable_area_redraw (HildonPannableArea * area);
224 static void hildon_pannable_area_launch_fade_timeout (HildonPannableArea * area,
226 static void hildon_pannable_area_adjust_value_changed (HildonPannableArea * area,
228 static void hildon_pannable_area_adjust_changed (HildonPannableArea * area,
230 static gboolean hildon_pannable_area_scroll_indicator_fade(HildonPannableArea * area);
231 static gboolean hildon_pannable_area_expose_event (GtkWidget * widget,
232 GdkEventExpose * event);
233 static GdkWindow * hildon_pannable_area_get_topmost (GdkWindow * window,
235 gint * tx, gint * ty,
237 static void synth_crossing (GdkWindow * child,
239 gint x_root, gint y_root,
240 guint32 time, gboolean in);
241 static gboolean hildon_pannable_area_button_press_cb (GtkWidget * widget,
242 GdkEventButton * event);
243 static void hildon_pannable_area_refresh (HildonPannableArea * area);
244 static gboolean hildon_pannable_area_check_scrollbars (HildonPannableArea * area);
245 static void hildon_pannable_axis_scroll (HildonPannableArea *area,
246 GtkAdjustment *adjust,
254 static void hildon_pannable_area_scroll (HildonPannableArea *area,
255 gdouble x, gdouble y);
256 static gboolean hildon_pannable_area_timeout (HildonPannableArea * area);
257 static void hildon_pannable_area_calculate_velocity (gdouble *vel,
261 gdouble drag_inertia,
264 static gboolean hildon_pannable_area_motion_event_scroll_timeout (HildonPannableArea *area);
265 static void hildon_pannable_area_motion_event_scroll (HildonPannableArea *area,
266 gdouble x, gdouble y);
267 static gboolean hildon_pannable_area_motion_notify_cb (GtkWidget * widget,
268 GdkEventMotion * event);
269 static gboolean hildon_pannable_leave_notify_event (GtkWidget *widget,
270 GdkEventCrossing *event);
271 static gboolean hildon_pannable_area_button_release_cb (GtkWidget * widget,
272 GdkEventButton * event);
273 static gboolean hildon_pannable_area_scroll_cb (GtkWidget *widget,
274 GdkEventScroll *event);
275 static void hildon_pannable_area_child_mapped (GtkWidget *widget,
278 static void hildon_pannable_area_add (GtkContainer *container, GtkWidget *child);
279 static void hildon_pannable_area_remove (GtkContainer *container, GtkWidget *child);
280 static void hildon_pannable_calculate_vel_factor (HildonPannableArea * self);
284 hildon_pannable_area_class_init (HildonPannableAreaClass * klass)
286 GObjectClass *object_class = G_OBJECT_CLASS (klass);
287 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
288 GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
291 g_type_class_add_private (klass, sizeof (HildonPannableAreaPrivate));
293 object_class->get_property = hildon_pannable_area_get_property;
294 object_class->set_property = hildon_pannable_area_set_property;
295 object_class->dispose = hildon_pannable_area_dispose;
297 widget_class->realize = hildon_pannable_area_realize;
298 widget_class->unrealize = hildon_pannable_area_unrealize;
299 widget_class->map = hildon_pannable_area_map;
300 widget_class->unmap = hildon_pannable_area_unmap;
301 widget_class->size_request = hildon_pannable_area_size_request;
302 widget_class->size_allocate = hildon_pannable_area_size_allocate;
303 widget_class->expose_event = hildon_pannable_area_expose_event;
304 widget_class->style_set = hildon_pannable_area_style_set;
305 widget_class->button_press_event = hildon_pannable_area_button_press_cb;
306 widget_class->button_release_event = hildon_pannable_area_button_release_cb;
307 widget_class->motion_notify_event = hildon_pannable_area_motion_notify_cb;
308 widget_class->leave_notify_event = hildon_pannable_leave_notify_event;
309 widget_class->scroll_event = hildon_pannable_area_scroll_cb;
311 container_class->add = hildon_pannable_area_add;
312 container_class->remove = hildon_pannable_area_remove;
314 klass->horizontal_movement = NULL;
315 klass->vertical_movement = NULL;
317 g_object_class_install_property (object_class,
319 g_param_spec_boolean ("enabled",
321 "Enable or disable finger-scroll.",
326 g_object_class_install_property (object_class,
327 PROP_VSCROLLBAR_POLICY,
328 g_param_spec_enum ("vscrollbar_policy",
330 "Visual policy of the vertical scrollbar",
331 GTK_TYPE_POLICY_TYPE,
332 GTK_POLICY_AUTOMATIC,
336 g_object_class_install_property (object_class,
337 PROP_HSCROLLBAR_POLICY,
338 g_param_spec_enum ("hscrollbar_policy",
340 "Visual policy of the horizontal scrollbar",
341 GTK_TYPE_POLICY_TYPE,
342 GTK_POLICY_AUTOMATIC,
346 g_object_class_install_property (object_class,
348 g_param_spec_enum ("mode",
350 "Change the finger-scrolling mode.",
351 HILDON_TYPE_PANNABLE_AREA_MODE,
352 HILDON_PANNABLE_AREA_MODE_AUTO,
356 g_object_class_install_property (object_class,
358 g_param_spec_flags ("mov_mode",
359 "Scroll movement mode",
360 "Controls if the widget can scroll vertically, horizontally or both",
361 HILDON_TYPE_MOVEMENT_MODE,
362 HILDON_MOVEMENT_MODE_VERT,
366 g_object_class_install_property (object_class,
368 g_param_spec_double ("velocity_min",
369 "Minimum scroll velocity",
370 "Minimum distance the child widget should scroll "
371 "per 'frame', in pixels per frame.",
376 g_object_class_install_property (object_class,
378 g_param_spec_double ("velocity_max",
379 "Maximum scroll velocity",
380 "Maximum distance the child widget should scroll "
381 "per 'frame', in pixels per frame.",
386 g_object_class_install_property (object_class,
387 PROP_VEL_MAX_OVERSHOOTING,
388 g_param_spec_double ("velocity_overshooting_max",
389 "Maximum scroll velocity when overshooting",
390 "Maximum distance the child widget should scroll "
391 "per 'frame', in pixels per frame when it overshoots after hitting the edge.",
396 g_object_class_install_property (object_class,
397 PROP_VELOCITY_FAST_FACTOR,
398 g_param_spec_double ("velocity_fast_factor",
399 "Fast velocity factor",
400 "Minimum velocity that is considered 'fast': "
401 "children widgets won't receive button presses. "
402 "Expressed as a fraction of the maximum velocity.",
407 g_object_class_install_property (object_class,
409 g_param_spec_double ("deceleration",
410 "Deceleration multiplier",
411 "The multiplier used when decelerating when in "
412 "acceleration scrolling mode.",
417 g_object_class_install_property (object_class,
419 g_param_spec_double ("drag_inertia",
420 "Inertia of the cursor dragging",
421 "Percentage of the calculated speed in each moment we are are going to use"
422 "to calculate the launch speed, the other part would be the speed"
423 "calculated previously",
428 g_object_class_install_property (object_class,
430 g_param_spec_uint ("sps",
431 "Scrolls per second",
432 "Amount of scroll events to generate per second.",
437 g_object_class_install_property (object_class,
438 PROP_PANNING_THRESHOLD,
439 g_param_spec_uint ("panning_threshold",
440 "Threshold to consider a motion event an scroll",
441 "Amount of pixels to consider a motion event an scroll, if it is less"
442 "it is a click detected incorrectly by the touch screen.",
447 g_object_class_install_property (object_class,
448 PROP_SCROLLBAR_FADE_DELAY,
449 g_param_spec_uint ("scrollbar_fade_delay",
450 "Time before starting to fade the scrollbar",
451 "Time the scrollbar is going to be visible if the widget is not in"
452 "action in miliseconds",
457 g_object_class_install_property (object_class,
459 g_param_spec_uint ("bounce_steps",
461 "Number of steps that is going to be used to bounce when hitting the"
462 "edge, the rubberband effect depends on it",
467 g_object_class_install_property (object_class,
469 g_param_spec_uint ("force",
470 "Multiplier of the calculated speed",
471 "Force applied to the movement, multiplies the calculated speed of the"
472 "user movement the cursor in the screen",
477 g_object_class_install_property (object_class,
478 PROP_DIRECTION_ERROR_MARGIN,
479 g_param_spec_uint ("direction_error_margin",
480 "Margin in the direction detection",
481 "After detecting the direction of the movement (horizontal or"
482 "vertical), we can add this margin of error to allow the movement in"
483 "the other direction even apparently it is not",
488 g_object_class_install_property (object_class,
490 g_param_spec_int ("vovershoot_max",
491 "Vertical overshoot distance",
492 "Space we allow the widget to pass over its vertical limits when"
493 "hitting the edges, set 0 in order to deactivate overshooting.",
498 g_object_class_install_property (object_class,
500 g_param_spec_int ("hovershoot_max",
501 "Horizontal overshoot distance",
502 "Space we allow the widget to pass over its horizontal limits when"
503 "hitting the edges, set 0 in order to deactivate overshooting.",
508 g_object_class_install_property (object_class,
510 g_param_spec_double ("scroll_time",
511 "Time to scroll to a position",
512 "The time to scroll to a position when calling the hildon_pannable_scroll_to function",
517 g_object_class_install_property (object_class,
519 g_param_spec_boolean ("initial-hint",
521 "Whether to hint the user about the pannability of the container.",
526 g_object_class_install_property (object_class,
527 PROP_LOW_FRICTION_MODE,
528 g_param_spec_boolean ("low-friction-mode",
529 "Do not decelerate the initial velocity",
530 "Avoid decelerating the panning movement, like no friction, the widget"
531 "will stop in the edges or if the user clicks.",
536 g_object_class_install_property (object_class,
537 PROP_SIZE_REQUEST_POLICY,
538 g_param_spec_enum ("size-request-policy",
539 "Size Requisition policy",
540 "Controls the size request policy of the widget",
541 HILDON_TYPE_SIZE_REQUEST_POLICY,
542 HILDON_SIZE_REQUEST_MINIMUM,
546 g_object_class_install_property (object_class,
548 g_param_spec_object ("hadjustment",
549 "Horizontal Adjustment",
550 "The GtkAdjustment for the horizontal position",
553 g_object_class_install_property (object_class,
555 g_param_spec_object ("vadjustment",
556 "Vertical Adjustment",
557 "The GtkAdjustment for the vertical position",
561 gtk_widget_class_install_style_property (widget_class,
564 "Width of the scroll indicators",
565 "Pixel width used to draw the scroll indicators.",
569 * HildonPannableArea::horizontal-movement:
570 * @hildonpannable: the object which received the signal
571 * @direction: the direction of the movement #HILDON_MOVEMENT_UP or #HILDON_MOVEMENT_DOWN
572 * @initial_x: the x value of the touched point in the area when the motion started
573 * @initial_y: the y value of the touched point in the area when the motion started
575 * The horizontal-movement signal is emitted when the pannable area
576 * starts a horizontal movement.
580 pannable_area_signals[HORIZONTAL_MOVEMENT] =
581 g_signal_new ("horizontal_movement",
582 G_TYPE_FROM_CLASS (object_class),
583 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
584 G_STRUCT_OFFSET (HildonPannableAreaClass, horizontal_movement),
586 _hildon_marshal_VOID__INT_DOUBLE_DOUBLE,
593 * HildonPannableArea::vertical-movement:
594 * @hildonpannable: the object which received the signal
595 * @direction: the direction of the movement #HILDON_MOVEMENT_LEFT or #HILDON_MOVEMENT_RIGHT
596 * @initial_x: the x value when the motion started
597 * @initial_y: the y value when the motion started
599 * The vertical-movement signal is emitted when the pannable area
600 * starts a vertical movement.
604 pannable_area_signals[VERTICAL_MOVEMENT] =
605 g_signal_new ("vertical_movement",
606 G_TYPE_FROM_CLASS (object_class),
607 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
608 G_STRUCT_OFFSET (HildonPannableAreaClass, vertical_movement),
610 _hildon_marshal_VOID__INT_DOUBLE_DOUBLE,
620 hildon_pannable_area_init (HildonPannableArea * area)
622 HildonPannableAreaPrivate *priv = PANNABLE_AREA_PRIVATE (area);
624 GTK_WIDGET_UNSET_FLAGS (area, GTK_NO_WINDOW);
629 priv->clicked = FALSE;
632 priv->vscroll_visible = TRUE;
633 priv->hscroll_visible = TRUE;
634 priv->indicator_width = 6;
635 priv->overshot_dist_x = 0;
636 priv->overshot_dist_y = 0;
637 priv->overshooting_y = 0;
638 priv->overshooting_x = 0;
642 priv->scroll_indicator_alpha = 0.0;
643 priv->scroll_indicator_timeout = 0;
644 priv->motion_event_scroll_timeout = 0;
645 priv->scroll_indicator_event_interrupt = 0;
646 priv->scroll_delay_counter = priv->scrollbar_fade_delay;
647 priv->scroll_to_x = -1;
648 priv->scroll_to_y = -1;
649 priv->first_drag = TRUE;
650 priv->initial_effect = TRUE;
651 priv->child_width = 0;
652 priv->child_height = 0;
653 priv->last_in = TRUE;
655 hildon_pannable_calculate_vel_factor (area);
657 gtk_widget_add_events (GTK_WIDGET (area), GDK_POINTER_MOTION_HINT_MASK);
660 GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
662 GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
664 g_object_ref_sink (G_OBJECT (priv->hadjust));
665 g_object_ref_sink (G_OBJECT (priv->vadjust));
667 g_signal_connect_swapped (priv->hadjust, "value-changed",
668 G_CALLBACK (hildon_pannable_area_adjust_value_changed), area);
669 g_signal_connect_swapped (priv->vadjust, "value-changed",
670 G_CALLBACK (hildon_pannable_area_adjust_value_changed), area);
671 g_signal_connect_swapped (priv->hadjust, "changed",
672 G_CALLBACK (hildon_pannable_area_adjust_changed), area);
673 g_signal_connect_swapped (priv->vadjust, "changed",
674 G_CALLBACK (hildon_pannable_area_adjust_changed), area);
675 g_signal_connect (area, "grab-notify",
676 G_CALLBACK (hildon_pannable_area_grab_notify), NULL);
680 hildon_pannable_area_get_property (GObject * object,
685 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (object)->priv;
687 switch (property_id) {
689 g_value_set_boolean (value, priv->enabled);
692 g_value_set_enum (value, priv->mode);
694 case PROP_MOVEMENT_MODE:
695 g_value_set_flags (value, priv->mov_mode);
697 case PROP_VELOCITY_MIN:
698 g_value_set_double (value, priv->vmin);
700 case PROP_VELOCITY_MAX:
701 g_value_set_double (value, priv->vmax);
703 case PROP_VEL_MAX_OVERSHOOTING:
704 g_value_set_double (value, priv->vmax_overshooting);
706 case PROP_VELOCITY_FAST_FACTOR:
707 g_value_set_double (value, priv->vfast_factor);
709 case PROP_DECELERATION:
710 g_value_set_double (value, priv->decel);
712 case PROP_DRAG_INERTIA:
713 g_value_set_double (value, priv->drag_inertia);
716 g_value_set_uint (value, priv->sps);
718 case PROP_PANNING_THRESHOLD:
719 g_value_set_uint (value, priv->panning_threshold);
721 case PROP_SCROLLBAR_FADE_DELAY:
722 /* convert to miliseconds */
723 g_value_set_uint (value, priv->scrollbar_fade_delay * SCROLL_FADE_TIMEOUT);
725 case PROP_BOUNCE_STEPS:
726 g_value_set_uint (value, priv->bounce_steps);
729 g_value_set_uint (value, priv->force);
731 case PROP_DIRECTION_ERROR_MARGIN:
732 g_value_set_uint (value, priv->direction_error_margin);
734 case PROP_VSCROLLBAR_POLICY:
735 g_value_set_enum (value, priv->vscrollbar_policy);
737 case PROP_HSCROLLBAR_POLICY:
738 g_value_set_enum (value, priv->hscrollbar_policy);
740 case PROP_VOVERSHOOT_MAX:
741 g_value_set_int (value, priv->vovershoot_max);
743 case PROP_HOVERSHOOT_MAX:
744 g_value_set_int (value, priv->hovershoot_max);
746 case PROP_SCROLL_TIME:
747 g_value_set_double (value, priv->scroll_time);
749 case PROP_INITIAL_HINT:
750 g_value_set_boolean (value, priv->initial_hint);
752 case PROP_LOW_FRICTION_MODE:
753 g_value_set_boolean (value, priv->low_friction_mode);
755 case PROP_SIZE_REQUEST_POLICY:
756 g_value_set_enum (value, priv->size_request_policy);
758 case PROP_HADJUSTMENT:
759 g_value_set_object (value,
760 hildon_pannable_area_get_hadjustment
761 (HILDON_PANNABLE_AREA (object)));
763 case PROP_VADJUSTMENT:
764 g_value_set_object (value,
765 hildon_pannable_area_get_vadjustment
766 (HILDON_PANNABLE_AREA (object)));
769 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
774 hildon_pannable_area_set_property (GObject * object,
776 const GValue * value,
779 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (object)->priv;
782 switch (property_id) {
784 enabled = g_value_get_boolean (value);
786 if ((priv->enabled != enabled) && (GTK_WIDGET_REALIZED (object))) {
788 gdk_window_raise (priv->event_window);
790 gdk_window_lower (priv->event_window);
793 priv->enabled = enabled;
796 priv->mode = g_value_get_enum (value);
798 case PROP_MOVEMENT_MODE:
799 priv->mov_mode = g_value_get_flags (value);
801 case PROP_VELOCITY_MIN:
802 priv->vmin = g_value_get_double (value);
804 case PROP_VELOCITY_MAX:
805 priv->vmax = g_value_get_double (value);
807 case PROP_VEL_MAX_OVERSHOOTING:
808 priv->vmax_overshooting = g_value_get_double (value);
810 case PROP_VELOCITY_FAST_FACTOR:
811 priv->vfast_factor = g_value_get_double (value);
813 case PROP_DECELERATION:
814 hildon_pannable_calculate_vel_factor (HILDON_PANNABLE_AREA (object));
816 priv->decel = g_value_get_double (value);
818 case PROP_DRAG_INERTIA:
819 priv->drag_inertia = g_value_get_double (value);
822 priv->sps = g_value_get_uint (value);
824 case PROP_PANNING_THRESHOLD:
825 priv->panning_threshold = g_value_get_uint (value);
827 case PROP_SCROLLBAR_FADE_DELAY:
828 /* convert to miliseconds */
829 priv->scrollbar_fade_delay = g_value_get_uint (value)/(SCROLL_FADE_TIMEOUT);
831 case PROP_BOUNCE_STEPS:
832 priv->bounce_steps = g_value_get_uint (value);
835 priv->force = g_value_get_uint (value);
837 case PROP_DIRECTION_ERROR_MARGIN:
838 priv->direction_error_margin = g_value_get_uint (value);
840 case PROP_VSCROLLBAR_POLICY:
841 priv->vscrollbar_policy = g_value_get_enum (value);
843 gtk_widget_queue_resize (GTK_WIDGET (object));
845 case PROP_HSCROLLBAR_POLICY:
846 priv->hscrollbar_policy = g_value_get_enum (value);
848 gtk_widget_queue_resize (GTK_WIDGET (object));
850 case PROP_VOVERSHOOT_MAX:
851 priv->vovershoot_max = g_value_get_int (value);
853 case PROP_HOVERSHOOT_MAX:
854 priv->hovershoot_max = g_value_get_int (value);
856 case PROP_SCROLL_TIME:
857 priv->scroll_time = g_value_get_double (value);
859 hildon_pannable_calculate_vel_factor (HILDON_PANNABLE_AREA (object));
861 case PROP_INITIAL_HINT:
862 priv->initial_hint = g_value_get_boolean (value);
864 case PROP_LOW_FRICTION_MODE:
865 priv->low_friction_mode = g_value_get_boolean (value);
867 case PROP_SIZE_REQUEST_POLICY:
868 hildon_pannable_area_set_size_request_policy (HILDON_PANNABLE_AREA (object),
869 g_value_get_enum (value));
873 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
878 hildon_pannable_area_dispose (GObject * object)
880 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (object)->priv;
881 GtkWidget *child = gtk_bin_get_child (GTK_BIN (object));
884 g_source_remove (priv->idle_id);
888 if (priv->scroll_indicator_timeout){
889 g_source_remove (priv->scroll_indicator_timeout);
890 priv->scroll_indicator_timeout = 0;
893 if (priv->motion_event_scroll_timeout){
894 g_source_remove (priv->motion_event_scroll_timeout);
895 priv->motion_event_scroll_timeout = 0;
899 g_signal_handlers_disconnect_by_func (child,
900 hildon_pannable_area_child_mapped,
904 g_signal_handlers_disconnect_by_func (object,
905 hildon_pannable_area_grab_notify,
909 g_signal_handlers_disconnect_by_func (priv->hadjust,
910 hildon_pannable_area_adjust_value_changed,
912 g_signal_handlers_disconnect_by_func (priv->hadjust,
913 hildon_pannable_area_adjust_changed,
915 g_object_unref (priv->hadjust);
916 priv->hadjust = NULL;
920 g_signal_handlers_disconnect_by_func (priv->vadjust,
921 hildon_pannable_area_adjust_value_changed,
923 g_signal_handlers_disconnect_by_func (priv->vadjust,
924 hildon_pannable_area_adjust_changed,
926 g_object_unref (priv->vadjust);
927 priv->vadjust = NULL;
930 if (G_OBJECT_CLASS (hildon_pannable_area_parent_class)->dispose)
931 G_OBJECT_CLASS (hildon_pannable_area_parent_class)->dispose (object);
935 hildon_pannable_area_realize (GtkWidget * widget)
937 GdkWindowAttr attributes;
938 gint attributes_mask;
940 HildonPannableAreaPrivate *priv;
942 priv = HILDON_PANNABLE_AREA (widget)->priv;
944 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
946 border_width = GTK_CONTAINER (widget)->border_width;
948 attributes.x = widget->allocation.x + border_width;
949 attributes.y = widget->allocation.y + border_width;
950 attributes.width = MAX (widget->allocation.width - 2 * border_width, 0);
951 attributes.height = MAX (widget->allocation.height - 2 * border_width, 0);
952 attributes.window_type = GDK_WINDOW_CHILD;
954 /* avoid using the hildon_window */
955 attributes.visual = gtk_widget_get_visual (widget);
956 attributes.colormap = gtk_widget_get_colormap (widget);
957 attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK;
958 attributes.wclass = GDK_INPUT_OUTPUT;
960 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
962 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
963 &attributes, attributes_mask);
964 gdk_window_set_user_data (widget->window, widget);
966 /* create the events window */
969 attributes.event_mask = gtk_widget_get_events (widget)
970 | GDK_BUTTON_MOTION_MASK
971 | GDK_BUTTON_PRESS_MASK
972 | GDK_BUTTON_RELEASE_MASK
974 | GDK_EXPOSURE_MASK | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK;
975 attributes.wclass = GDK_INPUT_ONLY;
977 attributes_mask = GDK_WA_X | GDK_WA_Y;
979 priv->event_window = gdk_window_new (widget->window,
980 &attributes, attributes_mask);
981 gdk_window_set_user_data (priv->event_window, widget);
983 widget->style = gtk_style_attach (widget->style, widget->window);
984 gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
986 priv->scrollbars_gc = gdk_gc_new (GDK_DRAWABLE (widget->window));
987 gdk_gc_copy (priv->scrollbars_gc, widget->style->fg_gc[GTK_STATE_INSENSITIVE]);
991 hildon_pannable_area_unrealize (GtkWidget * widget)
993 HildonPannableAreaPrivate *priv;
995 priv = HILDON_PANNABLE_AREA (widget)->priv;
997 if (priv->event_window != NULL) {
998 gdk_window_set_user_data (priv->event_window, NULL);
999 gdk_window_destroy (priv->event_window);
1000 priv->event_window = NULL;
1003 gdk_gc_unref (priv->scrollbars_gc);
1005 if (GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->unrealize)
1006 (*GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->unrealize)(widget);
1010 hildon_pannable_area_size_request (GtkWidget * widget,
1011 GtkRequisition * requisition)
1013 GtkRequisition child_requisition = {0};
1014 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1015 GtkWidget *child = gtk_bin_get_child (GTK_BIN (widget));
1017 if (child && GTK_WIDGET_VISIBLE (child))
1019 gtk_widget_size_request (child, &child_requisition);
1022 if (priv->hscrollbar_policy == GTK_POLICY_NEVER) {
1023 requisition->width = child_requisition.width;
1025 switch (priv->size_request_policy) {
1026 case HILDON_SIZE_REQUEST_CHILDREN:
1027 requisition->width = MIN (PANNABLE_MAX_WIDTH,
1028 child_requisition.width);
1030 case HILDON_SIZE_REQUEST_MINIMUM:
1032 requisition->width = priv->indicator_width;
1036 if (priv->vscrollbar_policy == GTK_POLICY_NEVER) {
1037 requisition->height = child_requisition.height;
1039 switch (priv->size_request_policy) {
1040 case HILDON_SIZE_REQUEST_CHILDREN:
1041 requisition->height = MIN (PANNABLE_MAX_HEIGHT,
1042 child_requisition.height);
1044 case HILDON_SIZE_REQUEST_MINIMUM:
1046 requisition->height = priv->indicator_width;
1050 requisition->width += 2 * GTK_CONTAINER (widget)->border_width;
1051 requisition->height += 2 * GTK_CONTAINER (widget)->border_width;
1055 hildon_pannable_area_child_allocate_calculate (GtkWidget * widget,
1056 GtkAllocation * allocation,
1057 GtkAllocation * child_allocation)
1060 HildonPannableAreaPrivate *priv;
1062 border_width = GTK_CONTAINER (widget)->border_width;
1064 priv = HILDON_PANNABLE_AREA (widget)->priv;
1066 child_allocation->x = 0;
1067 child_allocation->y = 0;
1068 child_allocation->width = MAX (allocation->width - 2 * border_width -
1069 (priv->vscroll_visible ? priv->vscroll_rect.width : 0), 0);
1070 child_allocation->height = MAX (allocation->height - 2 * border_width -
1071 (priv->hscroll_visible ? priv->hscroll_rect.height : 0), 0);
1073 if (priv->overshot_dist_y > 0) {
1074 child_allocation->y = MIN (child_allocation->y + priv->overshot_dist_y,
1075 child_allocation->height);
1076 child_allocation->height = MAX (child_allocation->height - priv->overshot_dist_y, 0);
1077 } else if (priv->overshot_dist_y < 0) {
1078 child_allocation->height = MAX (child_allocation->height + priv->overshot_dist_y, 0);
1081 if (priv->overshot_dist_x > 0) {
1082 child_allocation->x = MIN (child_allocation->x + priv->overshot_dist_x,
1083 child_allocation->width);
1084 child_allocation->width = MAX (child_allocation->width - priv->overshot_dist_x, 0);
1085 } else if (priv->overshot_dist_x < 0) {
1086 child_allocation->width = MAX (child_allocation->width + priv->overshot_dist_x, 0);
1091 hildon_pannable_area_size_allocate (GtkWidget * widget,
1092 GtkAllocation * allocation)
1094 GtkAllocation child_allocation;
1095 HildonPannableAreaPrivate *priv;
1096 GtkWidget *child = gtk_bin_get_child (GTK_BIN (widget));
1099 border_width = GTK_CONTAINER (widget)->border_width;
1101 widget->allocation = *allocation;
1103 priv = HILDON_PANNABLE_AREA (widget)->priv;
1105 if (GTK_WIDGET_REALIZED (widget)) {
1106 gdk_window_move_resize (widget->window,
1107 allocation->x + border_width,
1108 allocation->y + border_width,
1109 allocation->width - border_width * 2,
1110 allocation->height - border_width * 2);
1111 gdk_window_move_resize (priv->event_window,
1114 allocation->width - border_width * 2,
1115 allocation->height - border_width * 2);
1118 if (child && GTK_WIDGET_VISIBLE (child)) {
1120 hildon_pannable_area_child_allocate_calculate (widget,
1124 gtk_widget_size_allocate (child, &child_allocation);
1126 if (hildon_pannable_area_check_scrollbars (HILDON_PANNABLE_AREA (widget))) {
1127 hildon_pannable_area_child_allocate_calculate (widget,
1131 gtk_widget_size_allocate (child, &child_allocation);
1134 /* we have to do this after child size_allocate because page_size is
1135 * changed when we allocate the size of the children */
1136 if (priv->overshot_dist_y < 0) {
1137 gtk_adjustment_set_value (priv->vadjust, priv->vadjust->upper -
1138 priv->vadjust->page_size);
1141 if (priv->overshot_dist_x < 0) {
1142 gtk_adjustment_set_value (priv->hadjust, priv->hadjust->upper -
1143 priv->hadjust->page_size);
1147 hildon_pannable_area_check_scrollbars (HILDON_PANNABLE_AREA (widget));
1152 hildon_pannable_area_style_set (GtkWidget * widget,
1153 GtkStyle * previous_style)
1155 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1157 GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->
1158 style_set (widget, previous_style);
1160 gtk_widget_style_get (widget, "indicator-width", &priv->indicator_width, NULL);
1164 hildon_pannable_area_map (GtkWidget * widget)
1166 HildonPannableAreaPrivate *priv;
1168 priv = HILDON_PANNABLE_AREA (widget)->priv;
1170 gdk_window_show (widget->window);
1172 if (priv->event_window != NULL && !priv->enabled)
1173 gdk_window_show (priv->event_window);
1175 (*GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->map) (widget);
1177 if (priv->event_window != NULL && priv->enabled)
1178 gdk_window_show (priv->event_window);
1182 hildon_pannable_area_unmap (GtkWidget * widget)
1184 HildonPannableAreaPrivate *priv;
1186 priv = HILDON_PANNABLE_AREA (widget)->priv;
1188 if (priv->event_window != NULL)
1189 gdk_window_hide (priv->event_window);
1191 gdk_window_hide (widget->window);
1193 (*GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->unmap) (widget);
1197 hildon_pannable_area_grab_notify (GtkWidget *widget,
1198 gboolean was_grabbed,
1201 /* an internal widget has grabbed the focus and now has returned it,
1202 we have to do some release actions */
1204 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1206 priv->scroll_indicator_event_interrupt = 0;
1208 if ((!priv->scroll_indicator_timeout)&&(priv->scroll_indicator_alpha)>0.1) {
1209 priv->scroll_delay_counter = priv->scrollbar_fade_delay;
1211 hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget),
1212 priv->scroll_indicator_alpha);
1215 priv->last_type = 3;
1216 priv->moved = FALSE;
1220 #if USE_CAIRO_SCROLLBARS == 1
1223 rgb_from_gdkcolor (GdkColor *color, gdouble *r, gdouble *g, gdouble *b)
1225 *r = (color->red >> 8) / 255.0;
1226 *g = (color->green >> 8) / 255.0;
1227 *b = (color->blue >> 8) / 255.0;
1231 hildon_pannable_draw_vscroll (GtkWidget * widget,
1232 GdkColor *back_color,
1233 GdkColor *scroll_color)
1235 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1238 cairo_pattern_t *pattern;
1240 gint radius = (priv->vscroll_rect.width/2) - 1;
1242 cr = gdk_cairo_create(widget->window);
1244 /* Draw the background */
1245 rgb_from_gdkcolor (back_color, &r, &g, &b);
1246 cairo_set_source_rgb (cr, r, g, b);
1247 cairo_rectangle(cr, priv->vscroll_rect.x, priv->vscroll_rect.y,
1248 priv->vscroll_rect.width,
1249 priv->vscroll_rect.height);
1250 cairo_fill_preserve (cr);
1253 /* Calculate the scroll bar height and position */
1254 y = ((priv->vadjust->value - priv->vadjust->lower) / (priv->vadjust->upper - priv->vadjust->lower)) *
1255 (widget->allocation.height -
1256 (priv->hscroll_visible ? priv->indicator_width : 0));
1257 height = ((((priv->vadjust->value - priv->vadjust->lower) +
1258 priv->vadjust->page_size) /
1259 (priv->vadjust->upper - priv->vadjust->lower)) *
1260 (widget->allocation.height -
1261 (priv->hscroll_visible ? priv->indicator_width : 0))) - y;
1263 /* Set a minimum height */
1264 height = MAX (SCROLL_BAR_MIN_SIZE, height);
1266 /* Check the max y position */
1267 y = MIN (y, widget->allocation.height -
1268 (priv->hscroll_visible ? priv->hscroll_rect.height : 0) -
1271 /* Draw the scrollbar */
1272 rgb_from_gdkcolor (scroll_color, &r, &g, &b);
1274 pattern = cairo_pattern_create_linear(radius+1, y, radius+1,y + height);
1275 cairo_pattern_add_color_stop_rgb(pattern, 0, r, g, b);
1276 cairo_pattern_add_color_stop_rgb(pattern, 1, r/2, g/2, b/2);
1277 cairo_set_source(cr, pattern);
1279 cairo_pattern_destroy(pattern);
1281 cairo_arc(cr, priv->vscroll_rect.x + radius + 1, y + radius + 1, radius, G_PI, 0);
1282 cairo_line_to(cr, priv->vscroll_rect.x + (radius * 2) + 1, y + height - radius);
1283 cairo_arc(cr, priv->vscroll_rect.x + radius + 1, y + height - radius, radius, 0, G_PI);
1284 cairo_line_to(cr, priv->vscroll_rect.x + 1, y + height - radius);
1287 cairo_paint_with_alpha(cr, priv->scroll_indicator_alpha);
1293 hildon_pannable_draw_hscroll (GtkWidget * widget,
1294 GdkColor *back_color,
1295 GdkColor *scroll_color)
1297 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1300 cairo_pattern_t *pattern;
1302 gint radius = (priv->hscroll_rect.height/2) - 1;
1304 cr = gdk_cairo_create(widget->window);
1306 /* Draw the background */
1307 rgb_from_gdkcolor (back_color, &r, &g, &b);
1308 cairo_set_source_rgb (cr, r, g, b);
1309 cairo_rectangle(cr, priv->hscroll_rect.x, priv->hscroll_rect.y,
1310 priv->hscroll_rect.width,
1311 priv->hscroll_rect.height);
1312 cairo_fill_preserve (cr);
1315 /* calculate the scrollbar width and position */
1316 x = ((priv->hadjust->value - priv->hadjust->lower) / (priv->hadjust->upper - priv->hadjust->lower)) *
1317 (widget->allocation.width - (priv->vscroll_visible ? priv->indicator_width : 0));
1318 width =((((priv->hadjust->value - priv->hadjust->lower) +
1319 priv->hadjust->page_size) / (priv->hadjust->upper - priv->hadjust->lower)) *
1320 (widget->allocation.width -
1321 (priv->vscroll_visible ? priv->indicator_width : 0))) - x;
1323 /* Set a minimum width */
1324 width = MAX (SCROLL_BAR_MIN_SIZE, width);
1326 /* Check the max x position */
1327 x = MIN (x, widget->allocation.width -
1328 (priv->vscroll_visible ? priv->vscroll_rect.width : 0) -
1331 /* Draw the scrollbar */
1332 rgb_from_gdkcolor (scroll_color, &r, &g, &b);
1334 pattern = cairo_pattern_create_linear(x, radius+1, x+width, radius+1);
1335 cairo_pattern_add_color_stop_rgb(pattern, 0, r, g, b);
1336 cairo_pattern_add_color_stop_rgb(pattern, 1, r/2, g/2, b/2);
1337 cairo_set_source(cr, pattern);
1339 cairo_pattern_destroy(pattern);
1341 cairo_arc_negative(cr, x + radius + 1, priv->hscroll_rect.y + radius + 1, radius, 3*G_PI_2, G_PI_2);
1342 cairo_line_to(cr, x + width - radius, priv->hscroll_rect.y + (radius * 2) + 1);
1343 cairo_arc_negative(cr, x + width - radius, priv->hscroll_rect.y + radius + 1, radius, G_PI_2, 3*G_PI_2);
1344 cairo_line_to(cr, x + width - radius, priv->hscroll_rect.y + 1);
1347 cairo_paint_with_alpha(cr, priv->scroll_indicator_alpha);
1352 #else /* USE_CAIRO_SCROLLBARS */
1355 tranparency_color (GdkColor *color,
1358 gdouble transparency)
1362 diff = colora.red - colorb.red;
1363 color->red = colora.red-diff*transparency;
1365 diff = colora.green - colorb.green;
1366 color->green = colora.green-diff*transparency;
1368 diff = colora.blue - colorb.blue;
1369 color->blue = colora.blue-diff*transparency;
1373 hildon_pannable_draw_vscroll (GtkWidget *widget,
1374 GdkColor *back_color,
1375 GdkColor *scroll_color)
1377 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1379 GdkColor transp_color;
1380 GdkGC *gc = widget->style->fg_gc[GTK_STATE_INSENSITIVE];
1382 gdk_draw_rectangle (widget->window,
1383 widget->style->bg_gc[GTK_STATE_NORMAL],
1385 priv->vscroll_rect.x, priv->vscroll_rect.y,
1386 priv->vscroll_rect.width,
1387 priv->vscroll_rect.height);
1389 y = ((priv->vadjust->value - priv->vadjust->lower) / (priv->vadjust->upper - priv->vadjust->lower)) *
1390 (widget->allocation.height - (priv->hscroll_visible ? priv->indicator_width : 0));
1391 height = ((((priv->vadjust->value - priv->vadjust->lower) + priv->vadjust->page_size) /
1392 (priv->vadjust->upper - priv->vadjust->lower)) *
1393 (widget->allocation.height -
1394 (priv->hscroll_visible ? priv->indicator_width : 0))) - y;
1396 /* Set a minimum height */
1397 height = MAX (SCROLL_BAR_MIN_SIZE, height);
1399 /* Check the max y position */
1400 y = MIN (y, widget->allocation.height -
1401 (priv->hscroll_visible ? priv->hscroll_rect.height : 0) -
1404 if (priv->scroll_indicator_alpha < 1.0) {
1405 tranparency_color (&transp_color, *back_color, *scroll_color,
1406 priv->scroll_indicator_alpha);
1408 gdk_gc_set_rgb_fg_color (priv->scrollbars_gc, &transp_color);
1410 gc = priv->scrollbars_gc;
1413 gdk_draw_rectangle (widget->window, gc,
1414 TRUE, priv->vscroll_rect.x, y,
1415 priv->vscroll_rect.width, height);
1419 hildon_pannable_draw_hscroll (GtkWidget *widget,
1420 GdkColor *back_color,
1421 GdkColor *scroll_color)
1423 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1425 GdkColor transp_color;
1426 GdkGC *gc = widget->style->fg_gc[GTK_STATE_INSENSITIVE];
1428 gdk_draw_rectangle (widget->window,
1429 widget->style->bg_gc[GTK_STATE_INSENSITIVE],
1431 priv->hscroll_rect.x, priv->hscroll_rect.y,
1432 priv->hscroll_rect.width,
1433 priv->hscroll_rect.height);
1435 /* calculate the scrollbar width and position */
1436 x = ((priv->hadjust->value - priv->hadjust->lower) / (priv->hadjust->upper - priv->hadjust->lower)) *
1437 (widget->allocation.width - (priv->vscroll_visible ? priv->indicator_width : 0));
1438 width =((((priv->hadjust->value - priv->hadjust->lower) +
1439 priv->hadjust->page_size) / (priv->hadjust->upper - priv->hadjust->lower)) *
1440 (widget->allocation.width -
1441 (priv->vscroll_visible ? priv->indicator_width : 0))) - x;
1443 /* Set a minimum width */
1444 width = MAX (SCROLL_BAR_MIN_SIZE, width);
1446 /* Check the max x position */
1447 x = MIN (x, widget->allocation.width -
1448 (priv->vscroll_visible ? priv->vscroll_rect.width : 0) -
1451 if (priv->scroll_indicator_alpha < 1.0) {
1452 tranparency_color (&transp_color, *back_color, *scroll_color,
1453 priv->scroll_indicator_alpha);
1455 gdk_gc_set_rgb_fg_color (priv->scrollbars_gc, &transp_color);
1457 gc = priv->scrollbars_gc;
1460 gdk_draw_rectangle (widget->window, gc,
1461 TRUE, x, priv->hscroll_rect.y, width,
1462 priv->hscroll_rect.height);
1465 #endif /* USE_CAIRO_SCROLLBARS */
1468 hildon_pannable_area_initial_effect (GtkWidget * widget)
1470 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1471 gboolean hscroll_visible, vscroll_visible;
1473 if (priv->initial_hint) {
1475 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
1476 priv->vadjust->page_size);
1477 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
1478 priv->hadjust->page_size);
1480 if (priv->vscroll_visible || priv->hscroll_visible) {
1482 priv->scroll_indicator_event_interrupt = 0;
1483 priv->scroll_delay_counter = priv->scrollbar_fade_delay;
1485 hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget), 1.0);
1491 hildon_pannable_area_launch_fade_timeout (HildonPannableArea * area,
1494 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (area)->priv;
1496 priv->scroll_indicator_alpha = alpha;
1498 if (!priv->scroll_indicator_timeout)
1499 priv->scroll_indicator_timeout =
1500 gdk_threads_add_timeout (SCROLL_FADE_TIMEOUT,
1501 (GSourceFunc) hildon_pannable_area_scroll_indicator_fade,
1506 hildon_pannable_area_adjust_changed (HildonPannableArea * area,
1509 if (GTK_WIDGET_REALIZED (area))
1510 hildon_pannable_area_refresh (area);
1514 hildon_pannable_area_adjust_value_changed (HildonPannableArea * area,
1517 if (GTK_WIDGET_REALIZED (area)) {
1518 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (area)->priv;
1520 hildon_pannable_area_redraw (area);
1522 if ((priv->vscroll_visible) || (priv->hscroll_visible)) {
1523 priv->scroll_indicator_event_interrupt = 0;
1524 priv->scroll_delay_counter = priv->scrollbar_fade_delay;
1526 hildon_pannable_area_launch_fade_timeout (area, 1.0);
1532 hildon_pannable_area_redraw (HildonPannableArea * area)
1534 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (area)->priv;
1536 /* Redraw scroll indicators */
1537 if (GTK_WIDGET_DRAWABLE (area)) {
1538 if (priv->hscroll_visible) {
1539 gdk_window_invalidate_rect (GTK_WIDGET (area)->window,
1540 &priv->hscroll_rect, FALSE);
1543 if (priv->vscroll_visible) {
1544 gdk_window_invalidate_rect (GTK_WIDGET (area)->window,
1545 &priv->vscroll_rect, FALSE);
1551 hildon_pannable_area_scroll_indicator_fade(HildonPannableArea * area)
1553 HildonPannableAreaPrivate *priv = area->priv;
1555 /* if moving do not fade out */
1556 if (((ABS (priv->vel_y)>1.0)||
1557 (ABS (priv->vel_x)>1.0))&&(!priv->clicked)) {
1562 if (priv->scroll_indicator_event_interrupt) {
1563 /* Stop a fade out, and fade back in */
1564 if (priv->scroll_indicator_alpha > 0.9) {
1565 priv->scroll_indicator_alpha = 1.0;
1566 priv->scroll_indicator_timeout = 0;
1570 priv->scroll_indicator_alpha += 0.2;
1571 hildon_pannable_area_redraw (area);
1577 if ((priv->scroll_indicator_alpha > 0.9) &&
1578 (priv->scroll_delay_counter > 0)) {
1579 priv->scroll_delay_counter--;
1584 if (!priv->scroll_indicator_event_interrupt) {
1585 /* Continue fade out */
1586 if (priv->scroll_indicator_alpha < 0.1) {
1587 priv->scroll_indicator_timeout = 0;
1588 priv->scroll_indicator_alpha = 0.0;
1592 priv->scroll_indicator_alpha -= 0.2;
1593 hildon_pannable_area_redraw (area);
1603 hildon_pannable_area_expose_event (GtkWidget * widget,
1604 GdkEventExpose * event)
1607 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1608 #if USE_CAIRO_SCROLLBARS == 1
1609 GdkColor back_color = widget->style->bg[GTK_STATE_NORMAL];
1610 GdkColor scroll_color = widget->style->base[GTK_STATE_SELECTED];
1611 #else /* USE_CAIRO_SCROLLBARS */
1612 GdkColor back_color = widget->style->bg[GTK_STATE_NORMAL];
1613 GdkColor scroll_color = widget->style->fg[GTK_STATE_INSENSITIVE];
1616 if (gtk_bin_get_child (GTK_BIN (widget))) {
1618 if (priv->scroll_indicator_alpha > 0.1) {
1619 if (priv->vscroll_visible) {
1620 hildon_pannable_draw_vscroll (widget, &back_color, &scroll_color);
1622 if (priv->hscroll_visible) {
1623 hildon_pannable_draw_hscroll (widget, &back_color, &scroll_color);
1627 /* draw overshooting rectangles */
1628 if (priv->overshot_dist_y > 0) {
1629 gint overshot_height;
1631 overshot_height = MIN (priv->overshot_dist_y, widget->allocation.height -
1632 (priv->hscroll_visible ? priv->hscroll_rect.height : 0));
1634 gdk_draw_rectangle (widget->window,
1635 widget->style->bg_gc[GTK_STATE_NORMAL],
1639 widget->allocation.width -
1640 (priv->vscroll_visible ? priv->vscroll_rect.width : 0),
1642 } else if (priv->overshot_dist_y < 0) {
1643 gint overshot_height;
1647 MAX (priv->overshot_dist_y,
1648 -(widget->allocation.height -
1649 (priv->hscroll_visible ? priv->hscroll_rect.height : 0)));
1651 overshot_y = MAX (widget->allocation.height +
1653 (priv->hscroll_visible ? priv->hscroll_rect.height : 0), 0);
1655 gdk_draw_rectangle (widget->window,
1656 widget->style->bg_gc[GTK_STATE_NORMAL],
1660 widget->allocation.width -
1661 priv->vscroll_rect.width,
1665 if (priv->overshot_dist_x > 0) {
1666 gint overshot_width;
1668 overshot_width = MIN (priv->overshot_dist_x, widget->allocation.width -
1669 (priv->vscroll_visible ? priv->vscroll_rect.width : 0));
1671 gdk_draw_rectangle (widget->window,
1672 widget->style->bg_gc[GTK_STATE_NORMAL],
1677 widget->allocation.height -
1678 (priv->hscroll_visible ? priv->hscroll_rect.height : 0));
1679 } else if (priv->overshot_dist_x < 0) {
1680 gint overshot_width;
1684 MAX (priv->overshot_dist_x,
1685 -(widget->allocation.width -
1686 (priv->vscroll_visible ? priv->vscroll_rect.width : 0)));
1688 overshot_x = MAX (widget->allocation.width +
1690 (priv->vscroll_visible ? priv->vscroll_rect.width : 0), 0);
1692 gdk_draw_rectangle (widget->window,
1693 widget->style->bg_gc[GTK_STATE_NORMAL],
1698 widget->allocation.height -
1699 priv->hscroll_rect.height);
1704 if (G_UNLIKELY (priv->initial_effect)) {
1706 hildon_pannable_area_initial_effect (widget);
1708 priv->initial_effect = FALSE;
1711 return GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->expose_event (widget, event);
1715 hildon_pannable_area_get_topmost (GdkWindow * window,
1717 gint * tx, gint * ty,
1720 /* Find the GdkWindow at the given point, by recursing from a given
1721 * parent GdkWindow. Optionally return the co-ordinates transformed
1722 * relative to the child window.
1725 GList *c, *children;
1726 GdkWindow *selected_window = NULL;
1728 gdk_drawable_get_size (GDK_DRAWABLE (window), &width, &height);
1729 if ((x < 0) || (x >= width) || (y < 0) || (y >= height))
1732 children = gdk_window_peek_children (window);
1739 selected_window = window;
1742 for (c = children; c; c = c->next) {
1743 GdkWindow *child = (GdkWindow *) c->data;
1746 gdk_drawable_get_size (GDK_DRAWABLE (child), &width, &height);
1747 gdk_window_get_position (child, &wx, &wy);
1749 if ((x >= wx) && (x < (wx + width)) && (y >= wy) && (y < (wy + height)) &&
1750 (gdk_window_is_visible (child))) {
1752 if (gdk_window_peek_children (child)) {
1753 selected_window = hildon_pannable_area_get_topmost (child, x-wx, y-wy,
1755 if (!selected_window) {
1760 selected_window = child;
1763 if ((gdk_window_get_events (child)&mask)) {
1768 selected_window = child;
1774 return selected_window;
1778 synth_crossing (GdkWindow * child,
1780 gint x_root, gint y_root,
1781 guint32 time, gboolean in)
1783 GdkEventCrossing *crossing_event;
1784 GdkEventType type = in ? GDK_ENTER_NOTIFY : GDK_LEAVE_NOTIFY;
1786 /* Send synthetic enter event */
1787 crossing_event = (GdkEventCrossing *) gdk_event_new (type);
1788 ((GdkEventAny *) crossing_event)->type = type;
1789 ((GdkEventAny *) crossing_event)->window = g_object_ref (child);
1790 ((GdkEventAny *) crossing_event)->send_event = FALSE;
1791 crossing_event->subwindow = g_object_ref (child);
1792 crossing_event->time = time;
1793 crossing_event->x = x;
1794 crossing_event->y = y;
1795 crossing_event->x_root = x_root;
1796 crossing_event->y_root = y_root;
1797 crossing_event->mode = GDK_CROSSING_NORMAL;
1798 crossing_event->detail = GDK_NOTIFY_UNKNOWN;
1799 crossing_event->focus = FALSE;
1800 crossing_event->state = 0;
1801 gdk_event_put ((GdkEvent *) crossing_event);
1802 gdk_event_free ((GdkEvent *) crossing_event);
1806 hildon_pannable_area_button_press_cb (GtkWidget * widget,
1807 GdkEventButton * event)
1810 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1812 if ((!priv->enabled) || (event->button != 1) ||
1813 ((event->time == priv->last_time) &&
1814 (priv->last_type == 1)) || (gtk_bin_get_child (GTK_BIN (widget)) == NULL))
1817 priv->scroll_indicator_event_interrupt = 1;
1819 hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget),
1820 priv->scroll_indicator_alpha);
1822 priv->last_time = event->time;
1823 priv->last_type = 1;
1825 priv->scroll_to_x = -1;
1826 priv->scroll_to_y = -1;
1828 if (priv->clicked && priv->child) {
1829 /* Widget stole focus on last click, send crossing-out event */
1830 synth_crossing (priv->child, 0, 0, event->x_root, event->y_root,
1831 event->time, FALSE);
1839 /* Don't allow a click if we're still moving fast */
1840 if ((ABS (priv->vel_x) <= (priv->vmax * priv->vfast_factor)) &&
1841 (ABS (priv->vel_y) <= (priv->vmax * priv->vfast_factor)))
1843 hildon_pannable_area_get_topmost (gtk_bin_get_child (GTK_BIN (widget))->window,
1844 event->x, event->y, &x, &y, GDK_BUTTON_PRESS_MASK);
1848 priv->clicked = TRUE;
1850 /* Stop scrolling on mouse-down (so you can flick, then hold to stop) */
1856 gdk_drawable_get_size (priv->child, &priv->child_width,
1857 &priv->child_height);
1858 priv->last_in = TRUE;
1860 g_object_add_weak_pointer ((GObject *) priv->child,
1861 (gpointer) & priv->child);
1863 event = (GdkEventButton *) gdk_event_copy ((GdkEvent *) event);
1869 synth_crossing (priv->child, x, y, event->x_root,
1870 event->y_root, event->time, TRUE);
1872 /* Send synthetic click (button press/release) event */
1873 ((GdkEventAny *) event)->window = g_object_ref (priv->child);
1875 gdk_event_put ((GdkEvent *) event);
1876 gdk_event_free ((GdkEvent *) event);
1884 hildon_pannable_area_check_scrollbars (HildonPannableArea * area)
1886 HildonPannableAreaPrivate *priv = area->priv;
1887 gboolean prev_hscroll_visible, prev_vscroll_visible;
1889 prev_hscroll_visible = priv->hscroll_visible;
1890 prev_vscroll_visible = priv->vscroll_visible;
1892 if (!gtk_bin_get_child (GTK_BIN (area))) {
1893 priv->vscroll_visible = FALSE;
1894 priv->hscroll_visible = FALSE;
1896 switch (priv->hscrollbar_policy) {
1897 case GTK_POLICY_ALWAYS:
1898 priv->hscroll_visible = TRUE;
1900 case GTK_POLICY_NEVER:
1901 priv->hscroll_visible = FALSE;
1904 priv->hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
1905 priv->hadjust->page_size);
1908 switch (priv->vscrollbar_policy) {
1909 case GTK_POLICY_ALWAYS:
1910 priv->vscroll_visible = TRUE;
1912 case GTK_POLICY_NEVER:
1913 priv->vscroll_visible = FALSE;
1916 priv->vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
1917 priv->vadjust->page_size);
1920 /* Store the vscroll/hscroll areas for redrawing */
1921 if (priv->vscroll_visible) {
1922 GtkAllocation *allocation = >K_WIDGET (area)->allocation;
1923 priv->vscroll_rect.x = allocation->width - priv->indicator_width;
1924 priv->vscroll_rect.y = 0;
1925 priv->vscroll_rect.width = priv->indicator_width;
1926 priv->vscroll_rect.height = allocation->height -
1927 (priv->hscroll_visible ? priv->indicator_width : 0);
1929 if (priv->hscroll_visible) {
1930 GtkAllocation *allocation = >K_WIDGET (area)->allocation;
1931 priv->hscroll_rect.y = allocation->height - priv->indicator_width;
1932 priv->hscroll_rect.x = 0;
1933 priv->hscroll_rect.height = priv->indicator_width;
1934 priv->hscroll_rect.width = allocation->width -
1935 (priv->vscroll_visible ? priv->indicator_width : 0);
1939 return ((priv->hscroll_visible != prev_hscroll_visible) ||
1940 (priv->vscroll_visible != prev_vscroll_visible));
1944 hildon_pannable_area_refresh (HildonPannableArea * area)
1946 if (GTK_WIDGET_DRAWABLE (area) &&
1947 hildon_pannable_area_check_scrollbars (area)) {
1948 gtk_widget_queue_resize (GTK_WIDGET (area));
1950 hildon_pannable_area_redraw (area);
1954 /* Scroll by a particular amount (in pixels). Optionally, return if
1955 * the scroll on a particular axis was successful.
1958 hildon_pannable_axis_scroll (HildonPannableArea *area,
1959 GtkAdjustment *adjust,
1963 gint *overshot_dist,
1969 HildonPannableAreaPrivate *priv = area->priv;
1971 dist = gtk_adjustment_get_value (adjust) - inc;
1974 * We use overshot_dist to define the distance of the current overshoot,
1975 * and overshooting to define the direction/whether or not we are overshot
1977 if (!(*overshooting)) {
1979 /* Initiation of the overshoot happens when the finger is released
1980 * and the current position of the pannable contents are out of range
1982 if (dist < adjust->lower) {
1985 dist = adjust->lower;
1987 if (overshoot_max!=0) {
1990 *overshot_dist = CLAMP (*overshot_dist + *vel, 0, overshoot_max);
1991 *vel = MIN (priv->vmax_overshooting, *vel);
1992 gtk_widget_queue_resize (GTK_WIDGET (area));
1996 } else if (dist > adjust->upper - adjust->page_size) {
1999 dist = adjust->upper - adjust->page_size;
2001 if (overshoot_max!=0) {
2004 *overshot_dist = CLAMP (*overshot_dist + *vel, -overshoot_max, 0);
2005 *vel = MAX (-priv->vmax_overshooting, *vel);
2006 gtk_widget_queue_resize (GTK_WIDGET (area));
2011 if ((*scroll_to) != -1) {
2012 if (((inc < 0)&&(*scroll_to <= dist))||
2013 ((inc > 0)&&(*scroll_to >= dist))) {
2021 gtk_adjustment_set_value (adjust, dist);
2023 if (!priv->clicked) {
2025 /* When the overshoot has started we continue for
2026 * PROP_BOUNCE_STEPS more steps into the overshoot before we
2027 * reverse direction. The deceleration factor is calculated
2028 * based on the percentage distance from the first item with
2029 * each iteration, therefore always returning us to the
2030 * top/bottom most element
2032 if (*overshot_dist > 0) {
2034 if ((*overshooting < priv->bounce_steps) && (*vel > 0)) {
2036 *vel = (((gdouble)*overshot_dist)/overshoot_max) * (*vel);
2037 } else if ((*overshooting >= priv->bounce_steps) && (*vel > 0)) {
2039 } else if ((*overshooting > 1) && (*vel < 0)) {
2040 /* we add the MIN in order to avoid very small speeds */
2041 *vel = MIN ((((gdouble)*overshot_dist)*0.4) * -1, -2.0);
2044 *overshot_dist = CLAMP (*overshot_dist + *vel, 0, overshoot_max);
2046 gtk_widget_queue_resize (GTK_WIDGET (area));
2048 } else if (*overshot_dist < 0) {
2050 if ((*overshooting < priv->bounce_steps) && (*vel < 0)) {
2052 *vel = (((gdouble)*overshot_dist)/overshoot_max) * (*vel) * -1;
2053 } else if ((*overshooting >= priv->bounce_steps) && (*vel < 0)) {
2055 } else if ((*overshooting > 1) && (*vel > 0)) {
2056 /* we add the MAX in order to avoid very small speeds */
2057 *vel = MAX ((((gdouble)*overshot_dist)*0.4) * -1, 2.0);
2060 *overshot_dist = CLAMP (*overshot_dist + (*vel), -overshoot_max, 0);
2062 gtk_widget_queue_resize (GTK_WIDGET (area));
2067 gtk_widget_queue_resize (GTK_WIDGET (area));
2071 gint overshot_dist_old = *overshot_dist;
2073 if (*overshot_dist > 0) {
2074 *overshot_dist = CLAMP ((*overshot_dist) + inc, 0, overshoot_max);
2075 } else if (*overshot_dist < 0) {
2076 *overshot_dist = CLAMP ((*overshot_dist) + inc, -1 * overshoot_max, 0);
2079 gtk_adjustment_set_value (adjust, dist);
2082 if (*overshot_dist != overshot_dist_old)
2083 gtk_widget_queue_resize (GTK_WIDGET (area));
2089 hildon_pannable_area_scroll (HildonPannableArea *area,
2090 gdouble x, gdouble y)
2093 HildonPannableAreaPrivate *priv = area->priv;
2094 gboolean hscroll_visible, vscroll_visible;
2096 if (gtk_bin_get_child (GTK_BIN (area)) == NULL)
2099 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
2100 priv->vadjust->page_size);
2101 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
2102 priv->hadjust->page_size);
2107 if (vscroll_visible) {
2108 hildon_pannable_axis_scroll (area, priv->vadjust, &priv->vel_y, y,
2109 &priv->overshooting_y, &priv->overshot_dist_y,
2110 &priv->scroll_to_y, priv->vovershoot_max, &sy);
2115 if (hscroll_visible) {
2116 hildon_pannable_axis_scroll (area, priv->hadjust, &priv->vel_x, x,
2117 &priv->overshooting_x, &priv->overshot_dist_x,
2118 &priv->scroll_to_x, priv->hovershoot_max, &sx);
2123 /* If the scroll on a particular axis wasn't succesful, reset the
2124 * initial scroll position to the new mouse co-ordinate. This means
2125 * when you get to the top of the page, dragging down works immediately.
2127 if (priv->mode == HILDON_PANNABLE_AREA_MODE_ACCEL) {
2139 hildon_pannable_area_timeout (HildonPannableArea * area)
2141 HildonPannableAreaPrivate *priv = area->priv;
2143 if ((!priv->enabled) || (priv->mode == HILDON_PANNABLE_AREA_MODE_PUSH)) {
2149 if (!priv->clicked) {
2150 /* Decelerate gradually when pointer is raised */
2151 if ((!priv->overshot_dist_y) &&
2152 (!priv->overshot_dist_x)) {
2154 /* in case we move to a specific point do not decelerate when arriving */
2155 if ((priv->scroll_to_x != -1)||(priv->scroll_to_y != -1)) {
2157 if (ABS (priv->vel_x) >= 1.5) {
2158 priv->vel_x *= priv->decel;
2161 if (ABS (priv->vel_y) >= 1.5) {
2162 priv->vel_y *= priv->decel;
2166 if ((!priv->low_friction_mode) ||
2167 ((priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ) &&
2168 (ABS (priv->vel_x) < 0.8*priv->vmax)))
2169 priv->vel_x *= priv->decel;
2171 if ((!priv->low_friction_mode) ||
2172 ((priv->mov_mode&HILDON_MOVEMENT_MODE_VERT) &&
2173 (ABS (priv->vel_y) < 0.8*priv->vmax)))
2174 priv->vel_y *= priv->decel;
2176 if ((ABS (priv->vel_x) < 1.0) && (ABS (priv->vel_y) < 1.0)) {
2185 } else if (priv->mode == HILDON_PANNABLE_AREA_MODE_AUTO) {
2191 hildon_pannable_area_scroll (area, priv->vel_x, priv->vel_y);
2197 hildon_pannable_area_calculate_velocity (gdouble *vel,
2201 gdouble drag_inertia,
2207 if (ABS (dist) >= RATIO_TOLERANCE) {
2208 rawvel = (dist / ABS (delta)) * force;
2209 *vel = *vel * (1 - drag_inertia) +
2210 rawvel * drag_inertia;
2211 *vel = *vel > 0 ? MIN (*vel, vmax)
2212 : MAX (*vel, -1 * vmax);
2217 hildon_pannable_area_motion_event_scroll_timeout (HildonPannableArea *area)
2219 HildonPannableAreaPrivate *priv = area->priv;
2221 if ((priv->motion_x != 0)||(priv->motion_y != 0))
2222 hildon_pannable_area_scroll (area, priv->motion_x, priv->motion_y);
2224 priv->motion_event_scroll_timeout = 0;
2230 hildon_pannable_area_motion_event_scroll (HildonPannableArea *area,
2231 gdouble x, gdouble y)
2233 HildonPannableAreaPrivate *priv = area->priv;
2235 if (priv->motion_event_scroll_timeout) {
2237 priv->motion_x += x;
2238 priv->motion_y += y;
2242 /* we do not delay the first event but the next ones */
2243 hildon_pannable_area_scroll (area, x, y);
2248 priv->motion_event_scroll_timeout = gdk_threads_add_timeout
2249 ((gint) (1000.0 / (gdouble) MOTION_EVENTS_PER_SECOND),
2250 (GSourceFunc) hildon_pannable_area_motion_event_scroll_timeout, area);
2255 hildon_pannable_area_motion_notify_cb (GtkWidget * widget,
2256 GdkEventMotion * event)
2258 HildonPannableArea *area = HILDON_PANNABLE_AREA (widget);
2259 HildonPannableAreaPrivate *priv = area->priv;
2263 if (gtk_bin_get_child (GTK_BIN (widget)) == NULL)
2266 if ((!priv->enabled) || (!priv->clicked) ||
2267 ((event->time == priv->last_time) && (priv->last_type == 2))) {
2268 gdk_window_get_pointer (widget->window, NULL, NULL, 0);
2272 if (priv->last_type == 1) {
2273 priv->first_drag = TRUE;
2276 x = event->x - priv->x;
2277 y = event->y - priv->y;
2279 if (priv->first_drag && (!priv->moved) &&
2280 ((ABS (x) > (priv->panning_threshold))
2281 || (ABS (y) > (priv->panning_threshold)))) {
2286 if (priv->first_drag) {
2287 gboolean vscroll_visible;
2288 gboolean hscroll_visible;
2290 if (ABS (priv->iy - event->y) >=
2291 ABS (priv->ix - event->x)) {
2293 g_signal_emit (area,
2294 pannable_area_signals[VERTICAL_MOVEMENT],
2295 0, (priv->iy > event->y) ?
2296 HILDON_MOVEMENT_UP :
2297 HILDON_MOVEMENT_DOWN,
2298 (gdouble)priv->ix, (gdouble)priv->iy);
2300 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
2301 priv->vadjust->page_size);
2303 if (!((vscroll_visible)&&
2304 (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT))) {
2306 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
2307 priv->hadjust->page_size);
2309 /* even in case we do not have to move we check if this
2310 could be a fake horizontal movement */
2311 if (!((hscroll_visible)&&
2312 (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ)) ||
2313 (ABS (priv->iy - event->y) -
2314 ABS (priv->ix - event->x) >= priv->direction_error_margin))
2315 priv->moved = FALSE;
2319 g_signal_emit (area,
2320 pannable_area_signals[HORIZONTAL_MOVEMENT],
2321 0, (priv->ix > event->x) ?
2322 HILDON_MOVEMENT_LEFT :
2323 HILDON_MOVEMENT_RIGHT,
2324 (gdouble)priv->ix, (gdouble)priv->iy);
2326 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
2327 priv->hadjust->page_size);
2329 if (!((hscroll_visible)&&
2330 (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ))) {
2332 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
2333 priv->vadjust->page_size);
2335 /* even in case we do not have to move we check if this
2336 could be a fake vertical movement */
2337 if (!((vscroll_visible) &&
2338 (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT)) ||
2339 (ABS (priv->ix - event->x) -
2340 ABS (priv->iy - event->y) >= priv->direction_error_margin))
2341 priv->moved = FALSE;
2345 if ((priv->moved)&&(priv->child)) {
2348 pos_x = priv->cx + (event->x - priv->ix);
2349 pos_y = priv->cy + (event->y - priv->iy);
2351 synth_crossing (priv->child, pos_x, pos_y, event->x_root,
2352 event->y_root, event->time, FALSE);
2356 priv->first_drag = FALSE;
2358 if ((priv->mode != HILDON_PANNABLE_AREA_MODE_PUSH) &&
2359 (priv->mode != HILDON_PANNABLE_AREA_MODE_AUTO)) {
2362 priv->idle_id = gdk_threads_add_timeout ((gint)
2363 (1000.0 / (gdouble) priv->sps),
2365 hildon_pannable_area_timeout, area);
2370 switch (priv->mode) {
2371 case HILDON_PANNABLE_AREA_MODE_PUSH:
2372 /* Scroll by the amount of pixels the cursor has moved
2373 * since the last motion event.
2375 hildon_pannable_area_motion_event_scroll (area, x, y);
2379 case HILDON_PANNABLE_AREA_MODE_ACCEL:
2380 /* Set acceleration relative to the initial click */
2381 priv->ex = event->x;
2382 priv->ey = event->y;
2383 priv->vel_x = ((x > 0) ? 1 : -1) *
2385 (gdouble) widget->allocation.width) *
2386 (priv->vmax - priv->vmin)) + priv->vmin);
2387 priv->vel_y = ((y > 0) ? 1 : -1) *
2389 (gdouble) widget->allocation.height) *
2390 (priv->vmax - priv->vmin)) + priv->vmin);
2392 case HILDON_PANNABLE_AREA_MODE_AUTO:
2394 delta = event->time - priv->last_time;
2396 if (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT) {
2397 gdouble dist = event->y - priv->y;
2399 hildon_pannable_area_calculate_velocity (&priv->vel_y,
2411 if (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ) {
2412 gdouble dist = event->x - priv->x;
2414 hildon_pannable_area_calculate_velocity (&priv->vel_x,
2426 hildon_pannable_area_motion_event_scroll (area, x, y);
2428 if (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ)
2430 if (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT)
2438 } else if (priv->child) {
2442 pos_x = priv->cx + (event->x - priv->ix);
2443 pos_y = priv->cy + (event->y - priv->iy);
2445 in = (((0 <= pos_x)&&(priv->child_width >= pos_x)) &&
2446 ((0 <= pos_y)&&(priv->child_height >= pos_y)));
2448 if (((!priv->last_in)&&in)||((priv->last_in)&&(!in))) {
2450 synth_crossing (priv->child, pos_x, pos_y, event->x_root,
2451 event->y_root, event->time, in);
2457 priv->last_time = event->time;
2458 priv->last_type = 2;
2461 /* Send motion notify to child */
2462 event = (GdkEventMotion *) gdk_event_copy ((GdkEvent *) event);
2463 event->x = priv->cx + (event->x - priv->ix);
2464 event->y = priv->cy + (event->y - priv->iy);
2465 event->window = g_object_ref (priv->child);
2466 gdk_event_put ((GdkEvent *) event);
2467 gdk_event_free ((GdkEvent *) event);
2470 gdk_window_get_pointer (widget->window, NULL, NULL, 0);
2476 hildon_pannable_leave_notify_event (GtkWidget *widget,
2477 GdkEventCrossing *event)
2479 HildonPannableArea *area = HILDON_PANNABLE_AREA (widget);
2480 HildonPannableAreaPrivate *priv = area->priv;
2482 if ((priv->child)&&(priv->last_in)) {
2483 priv->last_in = FALSE;
2485 synth_crossing (priv->child, 0, 0, event->x_root,
2486 event->y_root, event->time, FALSE);
2493 hildon_pannable_area_button_release_cb (GtkWidget * widget,
2494 GdkEventButton * event)
2496 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
2500 if (((event->time == priv->last_time) && (priv->last_type == 3))
2501 || (gtk_bin_get_child (GTK_BIN (widget)) == NULL)
2502 || (!priv->clicked) || (!priv->enabled) || (event->button != 1))
2505 priv->scroll_indicator_event_interrupt = 0;
2506 priv->scroll_delay_counter = priv->scrollbar_fade_delay;
2508 /* move all the way to the last position */
2509 if (priv->motion_event_scroll_timeout) {
2510 g_source_remove (priv->motion_event_scroll_timeout);
2511 hildon_pannable_area_motion_event_scroll_timeout (HILDON_PANNABLE_AREA (widget));
2516 if (priv->last_type == 2) {
2517 gdouble delta = event->time - priv->last_time;
2519 if (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT) {
2520 gdouble dist = event->y - priv->y;
2522 if (ABS (dist) >= 1.0) {
2523 hildon_pannable_area_calculate_velocity (&priv->vel_y,
2531 priv->motion_y = dist;
2532 hildon_pannable_area_motion_event_scroll_timeout (HILDON_PANNABLE_AREA (widget));
2534 if (delta >= CURSOR_STOPPED_TIMEOUT) {
2540 if (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ) {
2541 gdouble dist = event->x - priv->x;
2543 if (ABS (dist) >= 1.0) {
2544 hildon_pannable_area_calculate_velocity (&priv->vel_x,
2551 priv->motion_x = dist;
2552 hildon_pannable_area_motion_event_scroll_timeout (HILDON_PANNABLE_AREA (widget));
2554 if (delta >= CURSOR_STOPPED_TIMEOUT) {
2561 if ((ABS (priv->vel_y) > 1.0)||
2562 (ABS (priv->vel_x) > 1.0)) {
2563 priv->scroll_indicator_alpha = 1.0;
2566 hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget),
2567 priv->scroll_indicator_alpha);
2569 priv->clicked = FALSE;
2571 if (priv->mode == HILDON_PANNABLE_AREA_MODE_AUTO ||
2572 priv->mode == HILDON_PANNABLE_AREA_MODE_ACCEL) {
2574 /* If overshoot has been initiated with a finger down, on release set max speed */
2575 if (priv->overshot_dist_y != 0) {
2576 priv->overshooting_y = priv->bounce_steps; /* Hack to stop a bounce in the finger down case */
2577 priv->vel_y = priv->vmax_overshooting;
2580 if (priv->overshot_dist_x != 0) {
2581 priv->overshooting_x = priv->bounce_steps; /* Hack to stop a bounce in the finger down case */
2582 priv->vel_x = priv->vmax_overshooting;
2585 if ((ABS (priv->vel_y) >= priv->vmin) ||
2586 (ABS (priv->vel_x) >= priv->vmin)) {
2589 priv->idle_id = gdk_threads_add_timeout ((gint) (1000.0 / (gdouble) priv->sps),
2591 hildon_pannable_area_timeout, widget);
2595 priv->last_time = event->time;
2596 priv->last_type = 3;
2599 priv->moved = FALSE;
2604 hildon_pannable_area_get_topmost (gtk_bin_get_child (GTK_BIN (widget))->window,
2605 event->x, event->y, &x, &y, GDK_BUTTON_RELEASE_MASK);
2607 event = (GdkEventButton *) gdk_event_copy ((GdkEvent *) event);
2611 /* Leave the widget if we've moved - This doesn't break selection,
2612 * but stops buttons from being clicked.
2614 if ((child != priv->child) || (priv->moved)) {
2615 /* Send synthetic leave event */
2616 synth_crossing (priv->child, x, y, event->x_root,
2617 event->y_root, event->time, FALSE);
2618 /* Send synthetic button release event */
2619 ((GdkEventAny *) event)->window = g_object_ref (priv->child);
2620 gdk_event_put ((GdkEvent *) event);
2622 /* Send synthetic button release event */
2623 ((GdkEventAny *) event)->window = g_object_ref (child);
2624 gdk_event_put ((GdkEvent *) event);
2625 /* Send synthetic leave event */
2626 synth_crossing (priv->child, x, y, event->x_root,
2627 event->y_root, event->time, FALSE);
2629 g_object_remove_weak_pointer ((GObject *) priv->child,
2630 (gpointer) & priv->child);
2632 priv->moved = FALSE;
2633 gdk_event_free ((GdkEvent *) event);
2638 /* utility event handler */
2640 hildon_pannable_area_scroll_cb (GtkWidget *widget,
2641 GdkEventScroll *event)
2643 GtkAdjustment *adj = NULL;
2644 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
2646 if ((!priv->enabled) ||
2647 (gtk_bin_get_child (GTK_BIN (widget)) == NULL))
2650 priv->scroll_indicator_event_interrupt = 0;
2651 priv->scroll_delay_counter = priv->scrollbar_fade_delay + 20;
2653 hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget), 1.0);
2655 /* Stop inertial scrolling */
2656 if (priv->idle_id) {
2659 priv->overshooting_x = 0;
2660 priv->overshooting_y = 0;
2662 if ((priv->overshot_dist_x>0)||(priv->overshot_dist_y>0)) {
2663 priv->overshot_dist_x = 0;
2664 priv->overshot_dist_y = 0;
2666 gtk_widget_queue_resize (GTK_WIDGET (widget));
2669 g_source_remove (priv->idle_id);
2673 if (event->direction == GDK_SCROLL_UP || event->direction == GDK_SCROLL_DOWN)
2674 adj = priv->vadjust;
2676 adj = priv->hadjust;
2680 gdouble delta, new_value;
2682 /* from gtkrange.c calculate delta*/
2683 delta = pow (adj->page_size, 2.0 / 3.0);
2685 if (event->direction == GDK_SCROLL_UP ||
2686 event->direction == GDK_SCROLL_LEFT)
2689 new_value = CLAMP (adj->value + delta, adj->lower, adj->upper - adj->page_size);
2691 gtk_adjustment_set_value (adj, new_value);
2698 hildon_pannable_area_child_mapped (GtkWidget *widget,
2702 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (user_data)->priv;
2704 if (priv->event_window != NULL && priv->enabled)
2705 gdk_window_raise (priv->event_window);
2709 hildon_pannable_area_add (GtkContainer *container, GtkWidget *child)
2711 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (container)->priv;
2713 g_return_if_fail (gtk_bin_get_child (GTK_BIN (container)) == NULL);
2715 gtk_widget_set_parent (child, GTK_WIDGET (container));
2716 GTK_BIN (container)->child = child;
2718 g_signal_connect_after (child, "map-event",
2719 G_CALLBACK (hildon_pannable_area_child_mapped),
2722 if (!gtk_widget_set_scroll_adjustments (child, priv->hadjust, priv->vadjust)) {
2723 g_warning ("%s: cannot add non scrollable widget, "
2724 "wrap it in a viewport", __FUNCTION__);
2729 hildon_pannable_area_remove (GtkContainer *container, GtkWidget *child)
2731 g_return_if_fail (HILDON_IS_PANNABLE_AREA (container));
2732 g_return_if_fail (child != NULL);
2733 g_return_if_fail (gtk_bin_get_child (GTK_BIN (container)) == child);
2735 gtk_widget_set_scroll_adjustments (child, NULL, NULL);
2737 g_signal_handlers_disconnect_by_func (child,
2738 hildon_pannable_area_child_mapped,
2741 /* chain parent class handler to remove child */
2742 GTK_CONTAINER_CLASS (hildon_pannable_area_parent_class)->remove (container, child);
2746 hildon_pannable_calculate_vel_factor (HildonPannableArea * self)
2748 HildonPannableAreaPrivate *priv = self->priv;
2753 n = ceil (priv->sps * priv->scroll_time);
2755 for (i = 0; i < n && fct_i >= RATIO_TOLERANCE; i++) {
2756 fct_i *= priv->decel;
2760 priv->vel_factor = fct;
2764 * hildon_pannable_area_new:
2766 * Create a new pannable area widget
2768 * Returns: the newly created #HildonPannableArea
2774 hildon_pannable_area_new (void)
2776 return g_object_new (HILDON_TYPE_PANNABLE_AREA, NULL);
2780 * hildon_pannable_area_new_full:
2781 * @mode: #HildonPannableAreaMode
2782 * @enabled: Value for the enabled property
2783 * @vel_min: Value for the velocity-min property
2784 * @vel_max: Value for the velocity-max property
2785 * @decel: Value for the deceleration property
2786 * @sps: Value for the sps property
2788 * Create a new #HildonPannableArea widget and set various properties
2790 * returns: the newly create #HildonPannableArea
2796 hildon_pannable_area_new_full (gint mode, gboolean enabled,
2797 gdouble vel_min, gdouble vel_max,
2798 gdouble decel, guint sps)
2800 return g_object_new (HILDON_TYPE_PANNABLE_AREA,
2803 "velocity_min", vel_min,
2804 "velocity_max", vel_max,
2805 "deceleration", decel, "sps", sps, NULL);
2809 * hildon_pannable_area_add_with_viewport:
2810 * @area: A #HildonPannableArea
2811 * @child: Child widget to add to the viewport
2813 * Convenience function used to add a child to a #GtkViewport, and add the
2814 * viewport to the scrolled window.
2820 hildon_pannable_area_add_with_viewport (HildonPannableArea * area,
2824 GtkWidget *viewport;
2826 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
2827 g_return_if_fail (GTK_IS_WIDGET (child));
2828 g_return_if_fail (child->parent == NULL);
2830 bin = GTK_BIN (area);
2832 if (bin->child != NULL)
2834 g_return_if_fail (GTK_IS_VIEWPORT (bin->child));
2835 g_return_if_fail (GTK_BIN (bin->child)->child == NULL);
2837 viewport = bin->child;
2841 HildonPannableAreaPrivate *priv = area->priv;
2843 viewport = gtk_viewport_new (priv->hadjust,
2845 gtk_viewport_set_shadow_type (GTK_VIEWPORT (viewport), GTK_SHADOW_NONE);
2846 gtk_container_add (GTK_CONTAINER (area), viewport);
2849 gtk_widget_show (viewport);
2850 gtk_container_add (GTK_CONTAINER (viewport), child);
2854 * hildon_pannable_area_scroll_to:
2855 * @area: A #HildonPannableArea.
2856 * @x: The x coordinate of the destination point or -1 to ignore this axis.
2857 * @y: The y coordinate of the destination point or -1 to ignore this axis.
2859 * Smoothly scrolls @area to ensure that (@x, @y) is a visible point
2860 * on the widget. To move in only one coordinate, you must set the other one
2861 * to -1. Notice that, in %HILDON_PANNABLE_AREA_MODE_PUSH mode, this function
2862 * works just like hildon_pannable_area_jump_to().
2864 * This function is useful if you need to present the user with a particular
2865 * element inside a scrollable widget, like #GtkTreeView. For instance,
2866 * the following example shows how to scroll inside a #GtkTreeView to
2867 * make visible an item, indicated by the #GtkTreeIter @iter.
2871 * GtkTreePath *path;
2872 * GdkRectangle *rect;
2874 * path = gtk_tree_model_get_path (model, &iter);
2875 * gtk_tree_view_get_background_area (GTK_TREE_VIEW (treeview),
2876 * path, NULL, &rect);
2877 * gtk_tree_view_convert_bin_window_to_tree_coords (GTK_TREE_VIEW (treeview),
2878 * 0, rect.y, NULL, &y);
2879 * hildon_pannable_area_scroll_to (panarea, -1, y);
2880 * gtk_tree_path_free (path);
2884 * If you want to present a child widget in simpler scenarios,
2885 * use hildon_pannable_area_scroll_to_child() instead.
2887 * There is a precondition to this function: the widget must be
2888 * already realized. Check the hildon_pannable_area_jump_to_child() for
2889 * more tips regarding how to call this function during
2895 hildon_pannable_area_scroll_to (HildonPannableArea *area,
2896 const gint x, const gint y)
2898 HildonPannableAreaPrivate *priv;
2900 gint dist_x, dist_y;
2901 gboolean hscroll_visible, vscroll_visible;
2903 g_return_if_fail (GTK_WIDGET_REALIZED (area));
2904 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
2908 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
2909 priv->vadjust->page_size);
2910 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
2911 priv->hadjust->page_size);
2913 if (((!vscroll_visible)&&(!hscroll_visible)) ||
2914 (x == -1 && y == -1)) {
2918 if (priv->mode == HILDON_PANNABLE_AREA_MODE_PUSH)
2919 hildon_pannable_area_jump_to (area, x, y);
2921 width = priv->hadjust->upper - priv->hadjust->lower;
2922 height = priv->vadjust->upper - priv->vadjust->lower;
2924 g_return_if_fail (x < width || y < height);
2926 if ((x > -1)&&(hscroll_visible)) {
2927 priv->scroll_to_x = x - priv->hadjust->page_size/2;
2928 dist_x = priv->scroll_to_x - priv->hadjust->value;
2930 priv->scroll_to_x = -1;
2932 priv->vel_x = - dist_x/priv->vel_factor;
2935 priv->scroll_to_x = -1;
2938 if ((y > -1)&&(vscroll_visible)) {
2939 priv->scroll_to_y = y - priv->vadjust->page_size/2;
2940 dist_y = priv->scroll_to_y - priv->vadjust->value;
2942 priv->scroll_to_y = -1;
2944 priv->vel_y = - dist_y/priv->vel_factor;
2947 priv->scroll_to_y = y;
2950 if ((priv->scroll_to_y == -1) && (priv->scroll_to_y == -1)) {
2954 hildon_pannable_area_launch_fade_timeout (area, 1.0);
2957 priv->idle_id = gdk_threads_add_timeout ((gint) (1000.0 / (gdouble) priv->sps),
2959 hildon_pannable_area_timeout, area);
2963 * hildon_pannable_area_jump_to:
2964 * @area: A #HildonPannableArea.
2965 * @x: The x coordinate of the destination point or -1 to ignore this axis.
2966 * @y: The y coordinate of the destination point or -1 to ignore this axis.
2968 * Jumps the position of @area to ensure that (@x, @y) is a visible
2969 * point in the widget. In order to move in only one coordinate, you
2970 * must set the other one to -1. See hildon_pannable_area_scroll_to()
2971 * function for an example of how to calculate the position of
2972 * children in scrollable widgets like #GtkTreeview.
2974 * There is a precondition to this function: the widget must be
2975 * already realized. Check the hildon_pannable_area_jump_to_child() for
2976 * more tips regarding how to call this function during
2982 hildon_pannable_area_jump_to (HildonPannableArea *area,
2983 const gint x, const gint y)
2985 HildonPannableAreaPrivate *priv;
2988 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
2989 g_return_if_fail (GTK_WIDGET_REALIZED (area));
2990 g_return_if_fail (x >= -1 && y >= -1);
2992 if (x == -1 && y == -1) {
2998 width = priv->hadjust->upper - priv->hadjust->lower;
2999 height = priv->vadjust->upper - priv->vadjust->lower;
3001 g_return_if_fail (x < width || y < height);
3004 gdouble jump_to = x - priv->hadjust->page_size/2;
3006 if (jump_to > priv->hadjust->upper - priv->hadjust->page_size) {
3007 jump_to = priv->hadjust->upper - priv->hadjust->page_size;
3010 gtk_adjustment_set_value (priv->hadjust, jump_to);
3014 gdouble jump_to = y - priv->vadjust->page_size/2;
3016 if (jump_to > priv->vadjust->upper - priv->vadjust->page_size) {
3017 jump_to = priv->vadjust->upper - priv->vadjust->page_size;
3020 gtk_adjustment_set_value (priv->vadjust, jump_to);
3023 priv->scroll_indicator_alpha = 1.0;
3025 if (priv->scroll_indicator_timeout) {
3026 g_source_remove (priv->scroll_indicator_timeout);
3027 priv->scroll_indicator_timeout = 0;
3030 if (priv->idle_id) {
3033 priv->overshooting_x = 0;
3034 priv->overshooting_y = 0;
3036 if ((priv->overshot_dist_x>0)||(priv->overshot_dist_y>0)) {
3037 priv->overshot_dist_x = 0;
3038 priv->overshot_dist_y = 0;
3040 gtk_widget_queue_resize (GTK_WIDGET (area));
3043 g_source_remove (priv->idle_id);
3049 * hildon_pannable_area_scroll_to_child:
3050 * @area: A #HildonPannableArea.
3051 * @child: A #GtkWidget, descendant of @area.
3053 * Smoothly scrolls until @child is visible inside @area. @child must
3054 * be a descendant of @area. If you need to scroll inside a scrollable
3055 * widget, e.g., #GtkTreeview, see hildon_pannable_area_scroll_to().
3057 * There is a precondition to this function: the widget must be
3058 * already realized. Check the hildon_pannable_area_jump_to_child() for
3059 * more tips regarding how to call this function during
3065 hildon_pannable_area_scroll_to_child (HildonPannableArea *area, GtkWidget *child)
3067 GtkWidget *bin_child;
3070 g_return_if_fail (GTK_WIDGET_REALIZED (area));
3071 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3072 g_return_if_fail (GTK_IS_WIDGET (child));
3073 g_return_if_fail (gtk_widget_is_ancestor (child, GTK_WIDGET (area)));
3075 if (GTK_BIN (area)->child == NULL)
3078 /* We need to get to check the child of the inside the area */
3079 bin_child = GTK_BIN (area)->child;
3081 /* we check if we added a viewport */
3082 if (GTK_IS_VIEWPORT (bin_child)) {
3083 bin_child = GTK_BIN (bin_child)->child;
3086 if (gtk_widget_translate_coordinates (child, bin_child, 0, 0, &x, &y))
3087 hildon_pannable_area_scroll_to (area, x, y);
3091 * hildon_pannable_area_jump_to_child:
3092 * @area: A #HildonPannableArea.
3093 * @child: A #GtkWidget, descendant of @area.
3095 * Jumps to make sure @child is visible inside @area. @child must
3096 * be a descendant of @area. If you want to move inside a scrollable
3097 * widget, like, #GtkTreeview, see hildon_pannable_area_scroll_to().
3099 * There is a precondition to this function: the widget must be
3100 * already realized. You can control if the widget is ready with the
3101 * GTK_WIDGET_REALIZED macro. If you want to call this function during
3102 * the initialization process of the widget do it inside a callback to
3103 * the ::realize signal, using g_signal_connect_after() function.
3108 hildon_pannable_area_jump_to_child (HildonPannableArea *area, GtkWidget *child)
3110 GtkWidget *bin_child;
3113 g_return_if_fail (GTK_WIDGET_REALIZED (area));
3114 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3115 g_return_if_fail (GTK_IS_WIDGET (child));
3116 g_return_if_fail (gtk_widget_is_ancestor (child, GTK_WIDGET (area)));
3118 if (gtk_bin_get_child (GTK_BIN (area)) == NULL)
3121 /* We need to get to check the child of the inside the area */
3122 bin_child = gtk_bin_get_child (GTK_BIN (area));
3124 /* we check if we added a viewport */
3125 if (GTK_IS_VIEWPORT (bin_child)) {
3126 bin_child = gtk_bin_get_child (GTK_BIN (bin_child));
3129 if (gtk_widget_translate_coordinates (child, bin_child, 0, 0, &x, &y))
3130 hildon_pannable_area_jump_to (area, x, y);
3134 * hildon_pannable_get_child_widget_at:
3135 * @area: A #HildonPannableArea.
3136 * @x: horizontal coordinate of the point
3137 * @y: vertical coordinate of the point
3139 * Get the widget at the point (x, y) inside the pannable area. In
3140 * case no widget found it returns NULL.
3142 * returns: the #GtkWidget if we find a widget, NULL in any other case
3147 hildon_pannable_get_child_widget_at (HildonPannableArea *area,
3148 gdouble x, gdouble y)
3150 GdkWindow *window = NULL;
3151 GtkWidget *child_widget = NULL;
3153 window = hildon_pannable_area_get_topmost
3154 (gtk_bin_get_child (GTK_BIN (area))->window,
3155 x, y, NULL, NULL, GDK_ALL_EVENTS_MASK);
3157 gdk_window_get_user_data (window, (gpointer) &child_widget);
3159 return child_widget;
3164 * hildon_pannable_area_get_hadjustment:
3165 * @area: A #HildonPannableArea.
3167 * Returns the horizontal adjustment. This adjustment is the internal
3168 * widget adjustment used to control the animations. Do not modify it
3169 * directly to change the position of the pannable, to do that use the
3170 * pannable API. If you modify the object directly it could cause
3171 * artifacts in the animations.
3173 * returns: The horizontal #GtkAdjustment
3178 hildon_pannable_area_get_hadjustment (HildonPannableArea *area)
3181 g_return_val_if_fail (HILDON_IS_PANNABLE_AREA (area), NULL);
3183 return area->priv->hadjust;
3187 * hildon_pannable_area_get_vadjustment:
3188 * @area: A #HildonPannableArea.
3190 * Returns the vertical adjustment. This adjustment is the internal
3191 * widget adjustment used to control the animations. Do not modify it
3192 * directly to change the position of the pannable, to do that use the
3193 * pannable API. If you modify the object directly it could cause
3194 * artifacts in the animations.
3196 * returns: The vertical #GtkAdjustment
3201 hildon_pannable_area_get_vadjustment (HildonPannableArea *area)
3203 g_return_val_if_fail (HILDON_IS_PANNABLE_AREA (area), NULL);
3205 return area->priv->vadjust;
3210 * hildon_pannable_area_get_size_request_policy:
3211 * @area: A #HildonPannableArea.
3213 * This function returns the current size request policy of the
3214 * widget. That policy controls the way the size_request is done in
3215 * the pannable area. Check
3216 * hildon_pannable_area_set_size_request_policy() for a more detailed
3219 * returns: the policy is currently being used in the widget
3220 * #HildonSizeRequestPolicy.
3224 HildonSizeRequestPolicy
3225 hildon_pannable_area_get_size_request_policy (HildonPannableArea *area)
3227 HildonPannableAreaPrivate *priv;
3229 g_return_val_if_fail (HILDON_IS_PANNABLE_AREA (area), FALSE);
3233 return priv->size_request_policy;
3237 * hildon_pannable_area_set_size_request_policy:
3238 * @area: A #HildonPannableArea.
3239 * @size_request_policy: One of the allowed #HildonSizeRequestPolicy
3241 * This function sets the pannable area size request policy. That
3242 * policy controls the way the size_request is done in the pannable
3243 * area. Pannable can use the size request of its children
3244 * (#HILDON_SIZE_REQUEST_CHILDREN) or the minimum size required for
3245 * the area itself (#HILDON_SIZE_REQUEST_MINIMUM), the latter is the
3246 * default. Recall this size depends on the scrolling policy you are
3247 * requesting to the pannable area, if you set #GTK_POLICY_NEVER this
3248 * parameter will not have any effect with
3249 * #HILDON_SIZE_REQUEST_MINIMUM set.
3253 * Deprecated: This method and the policy request is deprecated, DO
3254 * NOT use it in future code, the only policy properly supported in
3255 * gtk+ nowadays is the minimum size. Use #gtk_window_set_default_size
3256 * or #gtk_window_set_geometry_hints with the proper size in your case
3257 * to define the height of your dialogs.
3260 hildon_pannable_area_set_size_request_policy (HildonPannableArea *area,
3261 HildonSizeRequestPolicy size_request_policy)
3263 HildonPannableAreaPrivate *priv;
3265 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3269 if (priv->size_request_policy == size_request_policy)
3272 priv->size_request_policy = size_request_policy;
3274 gtk_widget_queue_resize (GTK_WIDGET (area));
3276 g_object_notify (G_OBJECT (area), "size-request-policy");