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 */
78 gboolean button_pressed;
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.",
570 * HildonPannableArea::horizontal-movement:
571 * @hildonpannable: the object which received the signal
572 * @direction: the direction of the movement #HILDON_MOVEMENT_LEFT or #HILDON_MOVEMENT_RIGHT
573 * @initial_x: the x coordinate of the point where the user clicked to start the movement
574 * @initial_y: the y coordinate of the point where the user clicked to start the movement
576 * The horizontal-movement signal is emitted when the pannable area
577 * detects a horizontal movement. The detection does not mean the
578 * widget is going to move (i.e. maybe the children are smaller
579 * horizontally than the screen).
583 pannable_area_signals[HORIZONTAL_MOVEMENT] =
584 g_signal_new ("horizontal_movement",
585 G_TYPE_FROM_CLASS (object_class),
586 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
587 G_STRUCT_OFFSET (HildonPannableAreaClass, horizontal_movement),
589 _hildon_marshal_VOID__INT_DOUBLE_DOUBLE,
596 * HildonPannableArea::vertical-movement:
597 * @hildonpannable: the object which received the signal
598 * @direction: the direction of the movement #HILDON_MOVEMENT_UP or #HILDON_MOVEMENT_DOWN
599 * @initial_x: the x coordinate of the point where the user clicked to start the movement
600 * @initial_y: the y coordinate of the point where the user clicked to start the movement
602 * The vertical-movement signal is emitted when the pannable area
603 * detects a vertical movement. The detection does not mean the
604 * widget is going to move (i.e. maybe the children are smaller
605 * vertically than the screen).
609 pannable_area_signals[VERTICAL_MOVEMENT] =
610 g_signal_new ("vertical_movement",
611 G_TYPE_FROM_CLASS (object_class),
612 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
613 G_STRUCT_OFFSET (HildonPannableAreaClass, vertical_movement),
615 _hildon_marshal_VOID__INT_DOUBLE_DOUBLE,
624 hildon_pannable_area_init (HildonPannableArea * area)
626 HildonPannableAreaPrivate *priv = PANNABLE_AREA_PRIVATE (area);
628 GTK_WIDGET_UNSET_FLAGS (area, GTK_NO_WINDOW);
633 priv->button_pressed = FALSE;
636 priv->vscroll_visible = TRUE;
637 priv->hscroll_visible = TRUE;
638 priv->indicator_width = 6;
639 priv->overshot_dist_x = 0;
640 priv->overshot_dist_y = 0;
641 priv->overshooting_y = 0;
642 priv->overshooting_x = 0;
646 priv->scroll_indicator_alpha = 0.0;
647 priv->scroll_indicator_timeout = 0;
648 priv->motion_event_scroll_timeout = 0;
649 priv->scroll_indicator_event_interrupt = 0;
650 priv->scroll_delay_counter = 0;
651 priv->scrollbar_fade_delay = 0;
652 priv->scroll_to_x = -1;
653 priv->scroll_to_y = -1;
654 priv->first_drag = TRUE;
655 priv->initial_effect = TRUE;
656 priv->child_width = 0;
657 priv->child_height = 0;
658 priv->last_in = TRUE;
660 gtk_widget_add_events (GTK_WIDGET (area), GDK_POINTER_MOTION_HINT_MASK);
663 GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
665 GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
667 g_object_ref_sink (G_OBJECT (priv->hadjust));
668 g_object_ref_sink (G_OBJECT (priv->vadjust));
670 g_signal_connect_swapped (priv->hadjust, "value-changed",
671 G_CALLBACK (hildon_pannable_area_adjust_value_changed), area);
672 g_signal_connect_swapped (priv->vadjust, "value-changed",
673 G_CALLBACK (hildon_pannable_area_adjust_value_changed), area);
674 g_signal_connect_swapped (priv->hadjust, "changed",
675 G_CALLBACK (hildon_pannable_area_adjust_changed), area);
676 g_signal_connect_swapped (priv->vadjust, "changed",
677 G_CALLBACK (hildon_pannable_area_adjust_changed), area);
678 g_signal_connect (area, "grab-notify",
679 G_CALLBACK (hildon_pannable_area_grab_notify), NULL);
683 hildon_pannable_area_get_property (GObject * object,
688 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (object)->priv;
690 switch (property_id) {
692 g_value_set_boolean (value, priv->enabled);
695 g_value_set_enum (value, priv->mode);
697 case PROP_MOVEMENT_MODE:
698 g_value_set_flags (value, priv->mov_mode);
700 case PROP_VELOCITY_MIN:
701 g_value_set_double (value, priv->vmin);
703 case PROP_VELOCITY_MAX:
704 g_value_set_double (value, priv->vmax);
706 case PROP_VEL_MAX_OVERSHOOTING:
707 g_value_set_double (value, priv->vmax_overshooting);
709 case PROP_VELOCITY_FAST_FACTOR:
710 g_value_set_double (value, priv->vfast_factor);
712 case PROP_DECELERATION:
713 g_value_set_double (value, priv->decel);
715 case PROP_DRAG_INERTIA:
716 g_value_set_double (value, priv->drag_inertia);
719 g_value_set_uint (value, priv->sps);
721 case PROP_PANNING_THRESHOLD:
722 g_value_set_uint (value, priv->panning_threshold);
724 case PROP_SCROLLBAR_FADE_DELAY:
725 /* convert to miliseconds */
726 g_value_set_uint (value, priv->scrollbar_fade_delay * SCROLL_FADE_TIMEOUT);
728 case PROP_BOUNCE_STEPS:
729 g_value_set_uint (value, priv->bounce_steps);
732 g_value_set_uint (value, priv->force);
734 case PROP_DIRECTION_ERROR_MARGIN:
735 g_value_set_uint (value, priv->direction_error_margin);
737 case PROP_VSCROLLBAR_POLICY:
738 g_value_set_enum (value, priv->vscrollbar_policy);
740 case PROP_HSCROLLBAR_POLICY:
741 g_value_set_enum (value, priv->hscrollbar_policy);
743 case PROP_VOVERSHOOT_MAX:
744 g_value_set_int (value, priv->vovershoot_max);
746 case PROP_HOVERSHOOT_MAX:
747 g_value_set_int (value, priv->hovershoot_max);
749 case PROP_SCROLL_TIME:
750 g_value_set_double (value, priv->scroll_time);
752 case PROP_INITIAL_HINT:
753 g_value_set_boolean (value, priv->initial_hint);
755 case PROP_LOW_FRICTION_MODE:
756 g_value_set_boolean (value, priv->low_friction_mode);
758 case PROP_SIZE_REQUEST_POLICY:
759 g_value_set_enum (value, priv->size_request_policy);
761 case PROP_HADJUSTMENT:
762 g_value_set_object (value,
763 hildon_pannable_area_get_hadjustment
764 (HILDON_PANNABLE_AREA (object)));
766 case PROP_VADJUSTMENT:
767 g_value_set_object (value,
768 hildon_pannable_area_get_vadjustment
769 (HILDON_PANNABLE_AREA (object)));
772 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
777 hildon_pannable_area_set_property (GObject * object,
779 const GValue * value,
782 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (object)->priv;
785 switch (property_id) {
787 enabled = g_value_get_boolean (value);
789 if ((priv->enabled != enabled) && (GTK_WIDGET_REALIZED (object))) {
791 gdk_window_raise (priv->event_window);
793 gdk_window_lower (priv->event_window);
796 priv->enabled = enabled;
799 priv->mode = g_value_get_enum (value);
801 case PROP_MOVEMENT_MODE:
802 priv->mov_mode = g_value_get_flags (value);
804 case PROP_VELOCITY_MIN:
805 priv->vmin = g_value_get_double (value);
807 case PROP_VELOCITY_MAX:
808 priv->vmax = g_value_get_double (value);
810 case PROP_VEL_MAX_OVERSHOOTING:
811 priv->vmax_overshooting = g_value_get_double (value);
813 case PROP_VELOCITY_FAST_FACTOR:
814 priv->vfast_factor = g_value_get_double (value);
816 case PROP_DECELERATION:
817 priv->decel = g_value_get_double (value);
818 hildon_pannable_calculate_vel_factor (HILDON_PANNABLE_AREA (object));
820 case PROP_DRAG_INERTIA:
821 priv->drag_inertia = g_value_get_double (value);
824 priv->sps = g_value_get_uint (value);
826 case PROP_PANNING_THRESHOLD:
828 GtkSettings *settings = gtk_settings_get_default ();
829 GtkSettingsValue svalue = { NULL, { 0, }, };
831 priv->panning_threshold = g_value_get_uint (value);
833 /* insure gtk dnd is the same we are using, not allowed
834 different thresholds in the same application */
835 svalue.origin = "panning_threshold";
836 g_value_init (&svalue.value, G_TYPE_LONG);
837 g_value_set_long (&svalue.value, priv->panning_threshold);
838 gtk_settings_set_property_value (settings, "gtk-dnd-drag-threshold", &svalue);
839 g_value_unset (&svalue.value);
842 case PROP_SCROLLBAR_FADE_DELAY:
843 /* convert to miliseconds */
844 priv->scrollbar_fade_delay = g_value_get_uint (value)/(SCROLL_FADE_TIMEOUT);
846 case PROP_BOUNCE_STEPS:
847 priv->bounce_steps = g_value_get_uint (value);
850 priv->force = g_value_get_uint (value);
852 case PROP_DIRECTION_ERROR_MARGIN:
853 priv->direction_error_margin = g_value_get_uint (value);
855 case PROP_VSCROLLBAR_POLICY:
856 priv->vscrollbar_policy = g_value_get_enum (value);
858 gtk_widget_queue_resize (GTK_WIDGET (object));
860 case PROP_HSCROLLBAR_POLICY:
861 priv->hscrollbar_policy = g_value_get_enum (value);
863 gtk_widget_queue_resize (GTK_WIDGET (object));
865 case PROP_VOVERSHOOT_MAX:
866 priv->vovershoot_max = g_value_get_int (value);
868 case PROP_HOVERSHOOT_MAX:
869 priv->hovershoot_max = g_value_get_int (value);
871 case PROP_SCROLL_TIME:
872 priv->scroll_time = g_value_get_double (value);
874 hildon_pannable_calculate_vel_factor (HILDON_PANNABLE_AREA (object));
876 case PROP_INITIAL_HINT:
877 priv->initial_hint = g_value_get_boolean (value);
879 case PROP_LOW_FRICTION_MODE:
880 priv->low_friction_mode = g_value_get_boolean (value);
882 case PROP_SIZE_REQUEST_POLICY:
883 hildon_pannable_area_set_size_request_policy (HILDON_PANNABLE_AREA (object),
884 g_value_get_enum (value));
888 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
893 hildon_pannable_area_dispose (GObject * object)
895 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (object)->priv;
896 GtkWidget *child = gtk_bin_get_child (GTK_BIN (object));
899 g_source_remove (priv->idle_id);
903 if (priv->scroll_indicator_timeout){
904 g_source_remove (priv->scroll_indicator_timeout);
905 priv->scroll_indicator_timeout = 0;
908 if (priv->motion_event_scroll_timeout){
909 g_source_remove (priv->motion_event_scroll_timeout);
910 priv->motion_event_scroll_timeout = 0;
914 g_signal_handlers_disconnect_by_func (child,
915 hildon_pannable_area_child_mapped,
919 g_signal_handlers_disconnect_by_func (object,
920 hildon_pannable_area_grab_notify,
924 g_signal_handlers_disconnect_by_func (priv->hadjust,
925 hildon_pannable_area_adjust_value_changed,
927 g_signal_handlers_disconnect_by_func (priv->hadjust,
928 hildon_pannable_area_adjust_changed,
930 g_object_unref (priv->hadjust);
931 priv->hadjust = NULL;
935 g_signal_handlers_disconnect_by_func (priv->vadjust,
936 hildon_pannable_area_adjust_value_changed,
938 g_signal_handlers_disconnect_by_func (priv->vadjust,
939 hildon_pannable_area_adjust_changed,
941 g_object_unref (priv->vadjust);
942 priv->vadjust = NULL;
945 if (G_OBJECT_CLASS (hildon_pannable_area_parent_class)->dispose)
946 G_OBJECT_CLASS (hildon_pannable_area_parent_class)->dispose (object);
950 hildon_pannable_area_realize (GtkWidget * widget)
952 GdkWindowAttr attributes;
953 gint attributes_mask;
955 HildonPannableAreaPrivate *priv;
957 priv = HILDON_PANNABLE_AREA (widget)->priv;
959 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
961 border_width = GTK_CONTAINER (widget)->border_width;
963 attributes.x = widget->allocation.x + border_width;
964 attributes.y = widget->allocation.y + border_width;
965 attributes.width = MAX (widget->allocation.width - 2 * border_width, 0);
966 attributes.height = MAX (widget->allocation.height - 2 * border_width, 0);
967 attributes.window_type = GDK_WINDOW_CHILD;
969 /* avoid using the hildon_window */
970 attributes.visual = gtk_widget_get_visual (widget);
971 attributes.colormap = gtk_widget_get_colormap (widget);
972 attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK;
973 attributes.wclass = GDK_INPUT_OUTPUT;
975 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
977 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
978 &attributes, attributes_mask);
979 gdk_window_set_user_data (widget->window, widget);
981 /* create the events window */
984 attributes.event_mask = gtk_widget_get_events (widget)
985 | GDK_BUTTON_MOTION_MASK
986 | GDK_BUTTON_PRESS_MASK
987 | GDK_BUTTON_RELEASE_MASK
989 | GDK_EXPOSURE_MASK | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK;
990 attributes.wclass = GDK_INPUT_ONLY;
992 attributes_mask = GDK_WA_X | GDK_WA_Y;
994 priv->event_window = gdk_window_new (widget->window,
995 &attributes, attributes_mask);
996 gdk_window_set_user_data (priv->event_window, widget);
998 widget->style = gtk_style_attach (widget->style, widget->window);
999 gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
1001 priv->scrollbars_gc = gdk_gc_new (GDK_DRAWABLE (widget->window));
1002 gdk_gc_copy (priv->scrollbars_gc, widget->style->fg_gc[GTK_STATE_INSENSITIVE]);
1006 hildon_pannable_area_unrealize (GtkWidget * widget)
1008 HildonPannableAreaPrivate *priv;
1010 priv = HILDON_PANNABLE_AREA (widget)->priv;
1012 if (priv->event_window != NULL) {
1013 gdk_window_set_user_data (priv->event_window, NULL);
1014 gdk_window_destroy (priv->event_window);
1015 priv->event_window = NULL;
1018 gdk_gc_unref (priv->scrollbars_gc);
1020 if (GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->unrealize)
1021 (*GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->unrealize)(widget);
1025 hildon_pannable_area_size_request (GtkWidget * widget,
1026 GtkRequisition * requisition)
1028 GtkRequisition child_requisition = {0};
1029 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1030 GtkWidget *child = gtk_bin_get_child (GTK_BIN (widget));
1032 if (child && GTK_WIDGET_VISIBLE (child))
1034 gtk_widget_size_request (child, &child_requisition);
1037 if (priv->hscrollbar_policy == GTK_POLICY_NEVER) {
1038 requisition->width = child_requisition.width;
1040 switch (priv->size_request_policy) {
1041 case HILDON_SIZE_REQUEST_CHILDREN:
1042 requisition->width = MIN (PANNABLE_MAX_WIDTH,
1043 child_requisition.width);
1045 case HILDON_SIZE_REQUEST_MINIMUM:
1047 requisition->width = priv->indicator_width;
1051 if (priv->vscrollbar_policy == GTK_POLICY_NEVER) {
1052 requisition->height = child_requisition.height;
1054 switch (priv->size_request_policy) {
1055 case HILDON_SIZE_REQUEST_CHILDREN:
1056 requisition->height = MIN (PANNABLE_MAX_HEIGHT,
1057 child_requisition.height);
1059 case HILDON_SIZE_REQUEST_MINIMUM:
1061 requisition->height = priv->indicator_width;
1065 requisition->width += 2 * GTK_CONTAINER (widget)->border_width;
1066 requisition->height += 2 * GTK_CONTAINER (widget)->border_width;
1070 hildon_pannable_area_child_allocate_calculate (GtkWidget * widget,
1071 GtkAllocation * allocation,
1072 GtkAllocation * child_allocation)
1075 HildonPannableAreaPrivate *priv;
1077 border_width = GTK_CONTAINER (widget)->border_width;
1079 priv = HILDON_PANNABLE_AREA (widget)->priv;
1081 child_allocation->x = 0;
1082 child_allocation->y = 0;
1083 child_allocation->width = MAX (allocation->width - 2 * border_width -
1084 (priv->vscroll_visible ? priv->vscroll_rect.width : 0), 0);
1085 child_allocation->height = MAX (allocation->height - 2 * border_width -
1086 (priv->hscroll_visible ? priv->hscroll_rect.height : 0), 0);
1088 if (priv->overshot_dist_y > 0) {
1089 child_allocation->y = MIN (child_allocation->y + priv->overshot_dist_y,
1090 child_allocation->height);
1091 child_allocation->height = MAX (child_allocation->height - priv->overshot_dist_y, 0);
1092 } else if (priv->overshot_dist_y < 0) {
1093 child_allocation->height = MAX (child_allocation->height + priv->overshot_dist_y, 0);
1096 if (priv->overshot_dist_x > 0) {
1097 child_allocation->x = MIN (child_allocation->x + priv->overshot_dist_x,
1098 child_allocation->width);
1099 child_allocation->width = MAX (child_allocation->width - priv->overshot_dist_x, 0);
1100 } else if (priv->overshot_dist_x < 0) {
1101 child_allocation->width = MAX (child_allocation->width + priv->overshot_dist_x, 0);
1106 hildon_pannable_area_size_allocate (GtkWidget * widget,
1107 GtkAllocation * allocation)
1109 GtkAllocation child_allocation;
1110 HildonPannableAreaPrivate *priv;
1111 GtkWidget *child = gtk_bin_get_child (GTK_BIN (widget));
1114 border_width = GTK_CONTAINER (widget)->border_width;
1116 widget->allocation = *allocation;
1118 priv = HILDON_PANNABLE_AREA (widget)->priv;
1120 if (GTK_WIDGET_REALIZED (widget)) {
1121 gdk_window_move_resize (widget->window,
1122 allocation->x + border_width,
1123 allocation->y + border_width,
1124 allocation->width - border_width * 2,
1125 allocation->height - border_width * 2);
1126 gdk_window_move_resize (priv->event_window,
1129 allocation->width - border_width * 2,
1130 allocation->height - border_width * 2);
1133 if (child && GTK_WIDGET_VISIBLE (child)) {
1135 hildon_pannable_area_child_allocate_calculate (widget,
1139 gtk_widget_size_allocate (child, &child_allocation);
1141 if (hildon_pannable_area_check_scrollbars (HILDON_PANNABLE_AREA (widget))) {
1142 hildon_pannable_area_child_allocate_calculate (widget,
1146 gtk_widget_size_allocate (child, &child_allocation);
1149 /* we have to do this after child size_allocate because page_size is
1150 * changed when we allocate the size of the children */
1151 if (priv->overshot_dist_y < 0) {
1152 gtk_adjustment_set_value (priv->vadjust, priv->vadjust->upper -
1153 priv->vadjust->page_size);
1156 if (priv->overshot_dist_x < 0) {
1157 gtk_adjustment_set_value (priv->hadjust, priv->hadjust->upper -
1158 priv->hadjust->page_size);
1162 hildon_pannable_area_check_scrollbars (HILDON_PANNABLE_AREA (widget));
1167 hildon_pannable_area_style_set (GtkWidget * widget,
1168 GtkStyle * previous_style)
1170 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1172 GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->
1173 style_set (widget, previous_style);
1175 gtk_widget_style_get (widget, "indicator-width", &priv->indicator_width, NULL);
1179 hildon_pannable_area_map (GtkWidget * widget)
1181 HildonPannableAreaPrivate *priv;
1183 priv = HILDON_PANNABLE_AREA (widget)->priv;
1185 gdk_window_show (widget->window);
1187 if (priv->event_window != NULL && !priv->enabled)
1188 gdk_window_show (priv->event_window);
1190 (*GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->map) (widget);
1192 if (priv->event_window != NULL && priv->enabled)
1193 gdk_window_show (priv->event_window);
1197 hildon_pannable_area_unmap (GtkWidget * widget)
1199 HildonPannableAreaPrivate *priv;
1201 priv = HILDON_PANNABLE_AREA (widget)->priv;
1203 if (priv->event_window != NULL)
1204 gdk_window_hide (priv->event_window);
1206 gdk_window_hide (widget->window);
1208 (*GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->unmap) (widget);
1212 hildon_pannable_area_grab_notify (GtkWidget *widget,
1213 gboolean was_grabbed,
1216 /* an internal widget has grabbed the focus and now has returned it,
1217 we have to do some release actions */
1219 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1221 priv->scroll_indicator_event_interrupt = 0;
1223 if ((!priv->scroll_indicator_timeout)&&(priv->scroll_indicator_alpha)>0.1) {
1224 priv->scroll_delay_counter = priv->scrollbar_fade_delay;
1226 hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget),
1227 priv->scroll_indicator_alpha);
1230 priv->last_type = 3;
1231 priv->moved = FALSE;
1235 #if USE_CAIRO_SCROLLBARS == 1
1238 rgb_from_gdkcolor (GdkColor *color, gdouble *r, gdouble *g, gdouble *b)
1240 *r = (color->red >> 8) / 255.0;
1241 *g = (color->green >> 8) / 255.0;
1242 *b = (color->blue >> 8) / 255.0;
1246 hildon_pannable_draw_vscroll (GtkWidget * widget,
1247 GdkColor *back_color,
1248 GdkColor *scroll_color)
1250 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1253 cairo_pattern_t *pattern;
1255 gint radius = (priv->vscroll_rect.width/2) - 1;
1257 cr = gdk_cairo_create(widget->window);
1259 /* Draw the background */
1260 rgb_from_gdkcolor (back_color, &r, &g, &b);
1261 cairo_set_source_rgb (cr, r, g, b);
1262 cairo_rectangle(cr, priv->vscroll_rect.x, priv->vscroll_rect.y,
1263 priv->vscroll_rect.width,
1264 priv->vscroll_rect.height);
1265 cairo_fill_preserve (cr);
1268 /* Calculate the scroll bar height and position */
1269 y = ((priv->vadjust->value - priv->vadjust->lower) / (priv->vadjust->upper - priv->vadjust->lower)) *
1270 (widget->allocation.height -
1271 (priv->hscroll_visible ? priv->indicator_width : 0));
1272 height = ((((priv->vadjust->value - priv->vadjust->lower) +
1273 priv->vadjust->page_size) /
1274 (priv->vadjust->upper - priv->vadjust->lower)) *
1275 (widget->allocation.height -
1276 (priv->hscroll_visible ? priv->indicator_width : 0))) - y;
1278 /* Set a minimum height */
1279 height = MAX (SCROLL_BAR_MIN_SIZE, height);
1281 /* Check the max y position */
1282 y = MIN (y, widget->allocation.height -
1283 (priv->hscroll_visible ? priv->hscroll_rect.height : 0) -
1286 /* Draw the scrollbar */
1287 rgb_from_gdkcolor (scroll_color, &r, &g, &b);
1289 pattern = cairo_pattern_create_linear(radius+1, y, radius+1,y + height);
1290 cairo_pattern_add_color_stop_rgb(pattern, 0, r, g, b);
1291 cairo_pattern_add_color_stop_rgb(pattern, 1, r/2, g/2, b/2);
1292 cairo_set_source(cr, pattern);
1294 cairo_pattern_destroy(pattern);
1296 cairo_arc(cr, priv->vscroll_rect.x + radius + 1, y + radius + 1, radius, G_PI, 0);
1297 cairo_line_to(cr, priv->vscroll_rect.x + (radius * 2) + 1, y + height - radius);
1298 cairo_arc(cr, priv->vscroll_rect.x + radius + 1, y + height - radius, radius, 0, G_PI);
1299 cairo_line_to(cr, priv->vscroll_rect.x + 1, y + height - radius);
1302 cairo_paint_with_alpha(cr, priv->scroll_indicator_alpha);
1308 hildon_pannable_draw_hscroll (GtkWidget * widget,
1309 GdkColor *back_color,
1310 GdkColor *scroll_color)
1312 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1315 cairo_pattern_t *pattern;
1317 gint radius = (priv->hscroll_rect.height/2) - 1;
1319 cr = gdk_cairo_create(widget->window);
1321 /* Draw the background */
1322 rgb_from_gdkcolor (back_color, &r, &g, &b);
1323 cairo_set_source_rgb (cr, r, g, b);
1324 cairo_rectangle(cr, priv->hscroll_rect.x, priv->hscroll_rect.y,
1325 priv->hscroll_rect.width,
1326 priv->hscroll_rect.height);
1327 cairo_fill_preserve (cr);
1330 /* calculate the scrollbar width and position */
1331 x = ((priv->hadjust->value - priv->hadjust->lower) / (priv->hadjust->upper - priv->hadjust->lower)) *
1332 (widget->allocation.width - (priv->vscroll_visible ? priv->indicator_width : 0));
1333 width =((((priv->hadjust->value - priv->hadjust->lower) +
1334 priv->hadjust->page_size) / (priv->hadjust->upper - priv->hadjust->lower)) *
1335 (widget->allocation.width -
1336 (priv->vscroll_visible ? priv->indicator_width : 0))) - x;
1338 /* Set a minimum width */
1339 width = MAX (SCROLL_BAR_MIN_SIZE, width);
1341 /* Check the max x position */
1342 x = MIN (x, widget->allocation.width -
1343 (priv->vscroll_visible ? priv->vscroll_rect.width : 0) -
1346 /* Draw the scrollbar */
1347 rgb_from_gdkcolor (scroll_color, &r, &g, &b);
1349 pattern = cairo_pattern_create_linear(x, radius+1, x+width, radius+1);
1350 cairo_pattern_add_color_stop_rgb(pattern, 0, r, g, b);
1351 cairo_pattern_add_color_stop_rgb(pattern, 1, r/2, g/2, b/2);
1352 cairo_set_source(cr, pattern);
1354 cairo_pattern_destroy(pattern);
1356 cairo_arc_negative(cr, x + radius + 1, priv->hscroll_rect.y + radius + 1, radius, 3*G_PI_2, G_PI_2);
1357 cairo_line_to(cr, x + width - radius, priv->hscroll_rect.y + (radius * 2) + 1);
1358 cairo_arc_negative(cr, x + width - radius, priv->hscroll_rect.y + radius + 1, radius, G_PI_2, 3*G_PI_2);
1359 cairo_line_to(cr, x + width - radius, priv->hscroll_rect.y + 1);
1362 cairo_paint_with_alpha(cr, priv->scroll_indicator_alpha);
1367 #else /* USE_CAIRO_SCROLLBARS */
1370 tranparency_color (GdkColor *color,
1373 gdouble transparency)
1377 diff = colora.red - colorb.red;
1378 color->red = colora.red-diff*transparency;
1380 diff = colora.green - colorb.green;
1381 color->green = colora.green-diff*transparency;
1383 diff = colora.blue - colorb.blue;
1384 color->blue = colora.blue-diff*transparency;
1388 hildon_pannable_draw_vscroll (GtkWidget *widget,
1389 GdkColor *back_color,
1390 GdkColor *scroll_color)
1392 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1394 GdkColor transp_color;
1395 GdkGC *gc = widget->style->fg_gc[GTK_STATE_INSENSITIVE];
1397 gdk_draw_rectangle (widget->window,
1398 widget->style->bg_gc[GTK_STATE_NORMAL],
1400 priv->vscroll_rect.x, priv->vscroll_rect.y,
1401 priv->vscroll_rect.width,
1402 priv->vscroll_rect.height);
1404 y = ((priv->vadjust->value - priv->vadjust->lower) / (priv->vadjust->upper - priv->vadjust->lower)) *
1405 (widget->allocation.height - (priv->hscroll_visible ? priv->indicator_width : 0));
1406 height = ((((priv->vadjust->value - priv->vadjust->lower) + priv->vadjust->page_size) /
1407 (priv->vadjust->upper - priv->vadjust->lower)) *
1408 (widget->allocation.height -
1409 (priv->hscroll_visible ? priv->indicator_width : 0))) - y;
1411 /* Set a minimum height */
1412 height = MAX (SCROLL_BAR_MIN_SIZE, height);
1414 /* Check the max y position */
1415 y = MIN (y, widget->allocation.height -
1416 (priv->hscroll_visible ? priv->hscroll_rect.height : 0) -
1419 if (priv->scroll_indicator_alpha < 1.0) {
1420 tranparency_color (&transp_color, *back_color, *scroll_color,
1421 priv->scroll_indicator_alpha);
1423 gdk_gc_set_rgb_fg_color (priv->scrollbars_gc, &transp_color);
1425 gc = priv->scrollbars_gc;
1428 gdk_draw_rectangle (widget->window, gc,
1429 TRUE, priv->vscroll_rect.x, y,
1430 priv->vscroll_rect.width, height);
1434 hildon_pannable_draw_hscroll (GtkWidget *widget,
1435 GdkColor *back_color,
1436 GdkColor *scroll_color)
1438 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1440 GdkColor transp_color;
1441 GdkGC *gc = widget->style->fg_gc[GTK_STATE_INSENSITIVE];
1443 gdk_draw_rectangle (widget->window,
1444 widget->style->bg_gc[GTK_STATE_INSENSITIVE],
1446 priv->hscroll_rect.x, priv->hscroll_rect.y,
1447 priv->hscroll_rect.width,
1448 priv->hscroll_rect.height);
1450 /* calculate the scrollbar width and position */
1451 x = ((priv->hadjust->value - priv->hadjust->lower) / (priv->hadjust->upper - priv->hadjust->lower)) *
1452 (widget->allocation.width - (priv->vscroll_visible ? priv->indicator_width : 0));
1453 width =((((priv->hadjust->value - priv->hadjust->lower) +
1454 priv->hadjust->page_size) / (priv->hadjust->upper - priv->hadjust->lower)) *
1455 (widget->allocation.width -
1456 (priv->vscroll_visible ? priv->indicator_width : 0))) - x;
1458 /* Set a minimum width */
1459 width = MAX (SCROLL_BAR_MIN_SIZE, width);
1461 /* Check the max x position */
1462 x = MIN (x, widget->allocation.width -
1463 (priv->vscroll_visible ? priv->vscroll_rect.width : 0) -
1466 if (priv->scroll_indicator_alpha < 1.0) {
1467 tranparency_color (&transp_color, *back_color, *scroll_color,
1468 priv->scroll_indicator_alpha);
1470 gdk_gc_set_rgb_fg_color (priv->scrollbars_gc, &transp_color);
1472 gc = priv->scrollbars_gc;
1475 gdk_draw_rectangle (widget->window, gc,
1476 TRUE, x, priv->hscroll_rect.y, width,
1477 priv->hscroll_rect.height);
1480 #endif /* USE_CAIRO_SCROLLBARS */
1483 hildon_pannable_area_initial_effect (GtkWidget * widget)
1485 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1487 if (priv->vscroll_visible || priv->hscroll_visible) {
1489 priv->scroll_indicator_event_interrupt = 0;
1490 priv->scroll_delay_counter = priv->scrollbar_fade_delay;
1492 hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget), 1.0);
1494 priv->initial_effect = FALSE;
1499 hildon_pannable_area_launch_fade_timeout (HildonPannableArea * area,
1502 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (area)->priv;
1504 priv->scroll_indicator_alpha = alpha;
1506 if (!priv->scroll_indicator_timeout)
1507 priv->scroll_indicator_timeout =
1508 gdk_threads_add_timeout (SCROLL_FADE_TIMEOUT,
1509 (GSourceFunc) hildon_pannable_area_scroll_indicator_fade,
1514 hildon_pannable_area_adjust_changed (HildonPannableArea * area,
1517 if (GTK_WIDGET_REALIZED (area))
1518 hildon_pannable_area_refresh (area);
1522 hildon_pannable_area_adjust_value_changed (HildonPannableArea * area,
1525 if (GTK_WIDGET_REALIZED (area)) {
1526 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (area)->priv;
1528 hildon_pannable_area_redraw (area);
1530 if ((priv->vscroll_visible) || (priv->hscroll_visible)) {
1531 priv->scroll_indicator_event_interrupt = 0;
1532 priv->scroll_delay_counter = priv->scrollbar_fade_delay;
1534 hildon_pannable_area_launch_fade_timeout (area, 1.0);
1540 hildon_pannable_area_redraw (HildonPannableArea * area)
1542 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (area)->priv;
1544 /* Redraw scroll indicators */
1545 if (GTK_WIDGET_DRAWABLE (area)) {
1546 if (priv->hscroll_visible) {
1547 gdk_window_invalidate_rect (GTK_WIDGET (area)->window,
1548 &priv->hscroll_rect, FALSE);
1551 if (priv->vscroll_visible) {
1552 gdk_window_invalidate_rect (GTK_WIDGET (area)->window,
1553 &priv->vscroll_rect, FALSE);
1559 hildon_pannable_area_scroll_indicator_fade(HildonPannableArea * area)
1561 HildonPannableAreaPrivate *priv = area->priv;
1563 /* if moving do not fade out */
1564 if (((ABS (priv->vel_y)>1.0)||
1565 (ABS (priv->vel_x)>1.0))&&(!priv->button_pressed)) {
1570 if (priv->scroll_indicator_event_interrupt) {
1571 /* Stop a fade out, and fade back in */
1572 if (priv->scroll_indicator_alpha > 0.9) {
1573 priv->scroll_indicator_alpha = 1.0;
1574 priv->scroll_indicator_timeout = 0;
1578 priv->scroll_indicator_alpha += 0.2;
1579 hildon_pannable_area_redraw (area);
1585 if ((priv->scroll_indicator_alpha > 0.9) &&
1586 (priv->scroll_delay_counter > 0)) {
1587 priv->scroll_delay_counter--;
1592 if (!priv->scroll_indicator_event_interrupt) {
1593 /* Continue fade out */
1594 if (priv->scroll_indicator_alpha < 0.1) {
1595 priv->scroll_indicator_timeout = 0;
1596 priv->scroll_indicator_alpha = 0.0;
1600 priv->scroll_indicator_alpha -= 0.2;
1601 hildon_pannable_area_redraw (area);
1611 hildon_pannable_area_expose_event (GtkWidget * widget,
1612 GdkEventExpose * event)
1615 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1616 #if USE_CAIRO_SCROLLBARS == 1
1617 GdkColor back_color = widget->style->bg[GTK_STATE_NORMAL];
1618 GdkColor scroll_color = widget->style->base[GTK_STATE_SELECTED];
1619 #else /* USE_CAIRO_SCROLLBARS */
1620 GdkColor back_color = widget->style->bg[GTK_STATE_NORMAL];
1621 GdkColor scroll_color = widget->style->fg[GTK_STATE_INSENSITIVE];
1624 if (G_UNLIKELY ((priv->initial_hint) && (priv->initial_effect))) {
1625 hildon_pannable_area_initial_effect (widget);
1628 if (gtk_bin_get_child (GTK_BIN (widget))) {
1630 if (priv->scroll_indicator_alpha > 0.1) {
1631 if (priv->vscroll_visible) {
1632 hildon_pannable_draw_vscroll (widget, &back_color, &scroll_color);
1634 if (priv->hscroll_visible) {
1635 hildon_pannable_draw_hscroll (widget, &back_color, &scroll_color);
1639 /* draw overshooting rectangles */
1640 if (priv->overshot_dist_y > 0) {
1641 gint overshot_height;
1643 overshot_height = MIN (priv->overshot_dist_y, widget->allocation.height -
1644 (priv->hscroll_visible ? priv->hscroll_rect.height : 0));
1646 gdk_draw_rectangle (widget->window,
1647 widget->style->bg_gc[GTK_STATE_NORMAL],
1651 widget->allocation.width -
1652 (priv->vscroll_visible ? priv->vscroll_rect.width : 0),
1654 } else if (priv->overshot_dist_y < 0) {
1655 gint overshot_height;
1659 MAX (priv->overshot_dist_y,
1660 -(widget->allocation.height -
1661 (priv->hscroll_visible ? priv->hscroll_rect.height : 0)));
1663 overshot_y = MAX (widget->allocation.height +
1665 (priv->hscroll_visible ? priv->hscroll_rect.height : 0), 0);
1667 gdk_draw_rectangle (widget->window,
1668 widget->style->bg_gc[GTK_STATE_NORMAL],
1672 widget->allocation.width -
1673 priv->vscroll_rect.width,
1677 if (priv->overshot_dist_x > 0) {
1678 gint overshot_width;
1680 overshot_width = MIN (priv->overshot_dist_x, widget->allocation.width -
1681 (priv->vscroll_visible ? priv->vscroll_rect.width : 0));
1683 gdk_draw_rectangle (widget->window,
1684 widget->style->bg_gc[GTK_STATE_NORMAL],
1689 widget->allocation.height -
1690 (priv->hscroll_visible ? priv->hscroll_rect.height : 0));
1691 } else if (priv->overshot_dist_x < 0) {
1692 gint overshot_width;
1696 MAX (priv->overshot_dist_x,
1697 -(widget->allocation.width -
1698 (priv->vscroll_visible ? priv->vscroll_rect.width : 0)));
1700 overshot_x = MAX (widget->allocation.width +
1702 (priv->vscroll_visible ? priv->vscroll_rect.width : 0), 0);
1704 gdk_draw_rectangle (widget->window,
1705 widget->style->bg_gc[GTK_STATE_NORMAL],
1710 widget->allocation.height -
1711 priv->hscroll_rect.height);
1716 return GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->expose_event (widget, event);
1720 hildon_pannable_area_get_topmost (GdkWindow * window,
1722 gint * tx, gint * ty,
1725 /* Find the GdkWindow at the given point, by recursing from a given
1726 * parent GdkWindow. Optionally return the co-ordinates transformed
1727 * relative to the child window.
1730 GList *c, *children;
1731 GdkWindow *selected_window = NULL;
1733 gdk_drawable_get_size (GDK_DRAWABLE (window), &width, &height);
1734 if ((x < 0) || (x >= width) || (y < 0) || (y >= height))
1737 children = gdk_window_peek_children (window);
1744 selected_window = window;
1747 for (c = children; c; c = c->next) {
1748 GdkWindow *child = (GdkWindow *) c->data;
1751 gdk_drawable_get_size (GDK_DRAWABLE (child), &width, &height);
1752 gdk_window_get_position (child, &wx, &wy);
1754 if ((x >= wx) && (x < (wx + width)) && (y >= wy) && (y < (wy + height)) &&
1755 (gdk_window_is_visible (child))) {
1757 if (gdk_window_peek_children (child)) {
1758 selected_window = hildon_pannable_area_get_topmost (child, x-wx, y-wy,
1760 if (!selected_window) {
1765 selected_window = child;
1768 if ((gdk_window_get_events (child)&mask)) {
1773 selected_window = child;
1779 return selected_window;
1783 synth_crossing (GdkWindow * child,
1785 gint x_root, gint y_root,
1786 guint32 time, gboolean in)
1788 GdkEventCrossing *crossing_event;
1789 GdkEventType type = in ? GDK_ENTER_NOTIFY : GDK_LEAVE_NOTIFY;
1791 /* Send synthetic enter event */
1792 crossing_event = (GdkEventCrossing *) gdk_event_new (type);
1793 ((GdkEventAny *) crossing_event)->type = type;
1794 ((GdkEventAny *) crossing_event)->window = g_object_ref (child);
1795 ((GdkEventAny *) crossing_event)->send_event = FALSE;
1796 crossing_event->subwindow = g_object_ref (child);
1797 crossing_event->time = time;
1798 crossing_event->x = x;
1799 crossing_event->y = y;
1800 crossing_event->x_root = x_root;
1801 crossing_event->y_root = y_root;
1802 crossing_event->mode = GDK_CROSSING_NORMAL;
1803 crossing_event->detail = GDK_NOTIFY_UNKNOWN;
1804 crossing_event->focus = FALSE;
1805 crossing_event->state = 0;
1806 gdk_event_put ((GdkEvent *) crossing_event);
1807 gdk_event_free ((GdkEvent *) crossing_event);
1811 hildon_pannable_area_button_press_cb (GtkWidget * widget,
1812 GdkEventButton * event)
1815 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1817 if ((!priv->enabled) || (event->button != 1) ||
1818 ((event->time == priv->last_time) &&
1819 (priv->last_type == 1)) || (gtk_bin_get_child (GTK_BIN (widget)) == NULL))
1822 priv->scroll_indicator_event_interrupt = 1;
1824 hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget),
1825 priv->scroll_indicator_alpha);
1827 priv->last_time = event->time;
1828 priv->last_type = 1;
1830 priv->scroll_to_x = -1;
1831 priv->scroll_to_y = -1;
1833 if (priv->button_pressed && priv->child) {
1834 /* Widget stole focus on last click, send crossing-out event */
1835 synth_crossing (priv->child, 0, 0, event->x_root, event->y_root,
1836 event->time, FALSE);
1844 /* Don't allow a click if we're still moving fast */
1845 if ((ABS (priv->vel_x) <= (priv->vmax * priv->vfast_factor)) &&
1846 (ABS (priv->vel_y) <= (priv->vmax * priv->vfast_factor)))
1848 hildon_pannable_area_get_topmost (gtk_bin_get_child (GTK_BIN (widget))->window,
1849 event->x, event->y, &x, &y, GDK_BUTTON_PRESS_MASK);
1853 priv->button_pressed = TRUE;
1855 /* Stop scrolling on mouse-down (so you can flick, then hold to stop) */
1861 gdk_drawable_get_size (priv->child, &priv->child_width,
1862 &priv->child_height);
1863 priv->last_in = TRUE;
1865 g_object_add_weak_pointer ((GObject *) priv->child,
1866 (gpointer) & priv->child);
1868 event = (GdkEventButton *) gdk_event_copy ((GdkEvent *) event);
1874 synth_crossing (priv->child, x, y, event->x_root,
1875 event->y_root, event->time, TRUE);
1877 /* Send synthetic click (button press/release) event */
1878 ((GdkEventAny *) event)->window = g_object_ref (priv->child);
1880 gdk_event_put ((GdkEvent *) event);
1881 gdk_event_free ((GdkEvent *) event);
1889 hildon_pannable_area_check_scrollbars (HildonPannableArea * area)
1891 HildonPannableAreaPrivate *priv = area->priv;
1892 gboolean prev_hscroll_visible, prev_vscroll_visible;
1894 prev_hscroll_visible = priv->hscroll_visible;
1895 prev_vscroll_visible = priv->vscroll_visible;
1897 if (!gtk_bin_get_child (GTK_BIN (area))) {
1898 priv->vscroll_visible = FALSE;
1899 priv->hscroll_visible = FALSE;
1901 switch (priv->hscrollbar_policy) {
1902 case GTK_POLICY_ALWAYS:
1903 priv->hscroll_visible = TRUE;
1905 case GTK_POLICY_NEVER:
1906 priv->hscroll_visible = FALSE;
1909 priv->hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
1910 priv->hadjust->page_size);
1913 switch (priv->vscrollbar_policy) {
1914 case GTK_POLICY_ALWAYS:
1915 priv->vscroll_visible = TRUE;
1917 case GTK_POLICY_NEVER:
1918 priv->vscroll_visible = FALSE;
1921 priv->vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
1922 priv->vadjust->page_size);
1925 /* Store the vscroll/hscroll areas for redrawing */
1926 if (priv->vscroll_visible) {
1927 GtkAllocation *allocation = >K_WIDGET (area)->allocation;
1928 priv->vscroll_rect.x = allocation->width - priv->indicator_width;
1929 priv->vscroll_rect.y = 0;
1930 priv->vscroll_rect.width = priv->indicator_width;
1931 priv->vscroll_rect.height = allocation->height -
1932 (priv->hscroll_visible ? priv->indicator_width : 0);
1934 if (priv->hscroll_visible) {
1935 GtkAllocation *allocation = >K_WIDGET (area)->allocation;
1936 priv->hscroll_rect.y = allocation->height - priv->indicator_width;
1937 priv->hscroll_rect.x = 0;
1938 priv->hscroll_rect.height = priv->indicator_width;
1939 priv->hscroll_rect.width = allocation->width -
1940 (priv->vscroll_visible ? priv->indicator_width : 0);
1944 return ((priv->hscroll_visible != prev_hscroll_visible) ||
1945 (priv->vscroll_visible != prev_vscroll_visible));
1949 hildon_pannable_area_refresh (HildonPannableArea * area)
1951 if (GTK_WIDGET_DRAWABLE (area) &&
1952 hildon_pannable_area_check_scrollbars (area)) {
1953 gtk_widget_queue_resize (GTK_WIDGET (area));
1955 hildon_pannable_area_redraw (area);
1959 /* Scroll by a particular amount (in pixels). Optionally, return if
1960 * the scroll on a particular axis was successful.
1963 hildon_pannable_axis_scroll (HildonPannableArea *area,
1964 GtkAdjustment *adjust,
1968 gint *overshot_dist,
1974 HildonPannableAreaPrivate *priv = area->priv;
1976 dist = gtk_adjustment_get_value (adjust) - inc;
1979 * We use overshot_dist to define the distance of the current overshoot,
1980 * and overshooting to define the direction/whether or not we are overshot
1982 if (!(*overshooting)) {
1984 /* Initiation of the overshoot happens when the finger is released
1985 * and the current position of the pannable contents are out of range
1987 if (dist < adjust->lower) {
1990 dist = adjust->lower;
1992 if (overshoot_max!=0) {
1995 *overshot_dist = CLAMP (*overshot_dist + *vel, 0, overshoot_max);
1996 *vel = MIN (priv->vmax_overshooting, *vel);
1997 gtk_widget_queue_resize (GTK_WIDGET (area));
2001 } else if (dist > adjust->upper - adjust->page_size) {
2004 dist = adjust->upper - adjust->page_size;
2006 if (overshoot_max!=0) {
2009 *overshot_dist = CLAMP (*overshot_dist + *vel, -overshoot_max, 0);
2010 *vel = MAX (-priv->vmax_overshooting, *vel);
2011 gtk_widget_queue_resize (GTK_WIDGET (area));
2016 if ((*scroll_to) != -1) {
2017 if (((inc < 0)&&(*scroll_to <= dist))||
2018 ((inc > 0)&&(*scroll_to >= dist))) {
2026 gtk_adjustment_set_value (adjust, dist);
2028 if (!priv->button_pressed) {
2030 /* When the overshoot has started we continue for
2031 * PROP_BOUNCE_STEPS more steps into the overshoot before we
2032 * reverse direction. The deceleration factor is calculated
2033 * based on the percentage distance from the first item with
2034 * each iteration, therefore always returning us to the
2035 * top/bottom most element
2037 if (*overshot_dist > 0) {
2039 if ((*overshooting < priv->bounce_steps) && (*vel > 0)) {
2041 *vel = (((gdouble)*overshot_dist)/overshoot_max) * (*vel);
2042 } else if ((*overshooting >= priv->bounce_steps) && (*vel > 0)) {
2044 } else if ((*overshooting > 1) && (*vel < 0)) {
2045 /* we add the MIN in order to avoid very small speeds */
2046 *vel = MIN ((((gdouble)*overshot_dist)*0.4) * -1, -2.0);
2049 *overshot_dist = CLAMP (*overshot_dist + *vel, 0, overshoot_max);
2051 gtk_widget_queue_resize (GTK_WIDGET (area));
2053 } else if (*overshot_dist < 0) {
2055 if ((*overshooting < priv->bounce_steps) && (*vel < 0)) {
2057 *vel = (((gdouble)*overshot_dist)/overshoot_max) * (*vel) * -1;
2058 } else if ((*overshooting >= priv->bounce_steps) && (*vel < 0)) {
2060 } else if ((*overshooting > 1) && (*vel > 0)) {
2061 /* we add the MAX in order to avoid very small speeds */
2062 *vel = MAX ((((gdouble)*overshot_dist)*0.4) * -1, 2.0);
2065 *overshot_dist = CLAMP (*overshot_dist + (*vel), -overshoot_max, 0);
2067 gtk_widget_queue_resize (GTK_WIDGET (area));
2072 gtk_widget_queue_resize (GTK_WIDGET (area));
2076 gint overshot_dist_old = *overshot_dist;
2078 if (*overshot_dist > 0) {
2079 *overshot_dist = CLAMP ((*overshot_dist) + inc, 0, overshoot_max);
2080 } else if (*overshot_dist < 0) {
2081 *overshot_dist = CLAMP ((*overshot_dist) + inc, -1 * overshoot_max, 0);
2084 gtk_adjustment_set_value (adjust, dist);
2087 if (*overshot_dist != overshot_dist_old)
2088 gtk_widget_queue_resize (GTK_WIDGET (area));
2094 hildon_pannable_area_scroll (HildonPannableArea *area,
2095 gdouble x, gdouble y)
2098 HildonPannableAreaPrivate *priv = area->priv;
2099 gboolean hscroll_visible, vscroll_visible;
2101 if (gtk_bin_get_child (GTK_BIN (area)) == NULL)
2104 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
2105 priv->vadjust->page_size);
2106 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
2107 priv->hadjust->page_size);
2112 if (vscroll_visible) {
2113 hildon_pannable_axis_scroll (area, priv->vadjust, &priv->vel_y, y,
2114 &priv->overshooting_y, &priv->overshot_dist_y,
2115 &priv->scroll_to_y, priv->vovershoot_max, &sy);
2120 if (hscroll_visible) {
2121 hildon_pannable_axis_scroll (area, priv->hadjust, &priv->vel_x, x,
2122 &priv->overshooting_x, &priv->overshot_dist_x,
2123 &priv->scroll_to_x, priv->hovershoot_max, &sx);
2128 /* If the scroll on a particular axis wasn't succesful, reset the
2129 * initial scroll position to the new mouse co-ordinate. This means
2130 * when you get to the top of the page, dragging down works immediately.
2132 if (priv->mode == HILDON_PANNABLE_AREA_MODE_ACCEL) {
2144 hildon_pannable_area_timeout (HildonPannableArea * area)
2146 HildonPannableAreaPrivate *priv = area->priv;
2148 if ((!priv->enabled) || (priv->mode == HILDON_PANNABLE_AREA_MODE_PUSH)) {
2154 if (!priv->button_pressed) {
2155 /* Decelerate gradually when pointer is raised */
2156 if ((!priv->overshot_dist_y) &&
2157 (!priv->overshot_dist_x)) {
2159 /* in case we move to a specific point do not decelerate when arriving */
2160 if ((priv->scroll_to_x != -1)||(priv->scroll_to_y != -1)) {
2162 if (ABS (priv->vel_x) >= 1.5) {
2163 priv->vel_x *= priv->decel;
2166 if (ABS (priv->vel_y) >= 1.5) {
2167 priv->vel_y *= priv->decel;
2171 if ((!priv->low_friction_mode) ||
2172 ((priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ) &&
2173 (ABS (priv->vel_x) < 0.8*priv->vmax)))
2174 priv->vel_x *= priv->decel;
2176 if ((!priv->low_friction_mode) ||
2177 ((priv->mov_mode&HILDON_MOVEMENT_MODE_VERT) &&
2178 (ABS (priv->vel_y) < 0.8*priv->vmax)))
2179 priv->vel_y *= priv->decel;
2181 if ((ABS (priv->vel_x) < 1.0) && (ABS (priv->vel_y) < 1.0)) {
2190 } else if (priv->mode == HILDON_PANNABLE_AREA_MODE_AUTO) {
2196 hildon_pannable_area_scroll (area, priv->vel_x, priv->vel_y);
2202 hildon_pannable_area_calculate_velocity (gdouble *vel,
2206 gdouble drag_inertia,
2212 if (ABS (dist) >= RATIO_TOLERANCE) {
2213 rawvel = (dist / ABS (delta)) * force;
2214 *vel = *vel * (1 - drag_inertia) +
2215 rawvel * drag_inertia;
2216 *vel = *vel > 0 ? MIN (*vel, vmax)
2217 : MAX (*vel, -1 * vmax);
2222 hildon_pannable_area_motion_event_scroll_timeout (HildonPannableArea *area)
2224 HildonPannableAreaPrivate *priv = area->priv;
2226 if ((priv->motion_x != 0)||(priv->motion_y != 0))
2227 hildon_pannable_area_scroll (area, priv->motion_x, priv->motion_y);
2229 priv->motion_event_scroll_timeout = 0;
2235 hildon_pannable_area_motion_event_scroll (HildonPannableArea *area,
2236 gdouble x, gdouble y)
2238 HildonPannableAreaPrivate *priv = area->priv;
2240 if (priv->motion_event_scroll_timeout) {
2242 priv->motion_x += x;
2243 priv->motion_y += y;
2247 /* we do not delay the first event but the next ones */
2248 hildon_pannable_area_scroll (area, x, y);
2253 priv->motion_event_scroll_timeout = gdk_threads_add_timeout
2254 ((gint) (1000.0 / (gdouble) MOTION_EVENTS_PER_SECOND),
2255 (GSourceFunc) hildon_pannable_area_motion_event_scroll_timeout, area);
2260 hildon_pannable_area_motion_notify_cb (GtkWidget * widget,
2261 GdkEventMotion * event)
2263 HildonPannableArea *area = HILDON_PANNABLE_AREA (widget);
2264 HildonPannableAreaPrivate *priv = area->priv;
2268 if (gtk_bin_get_child (GTK_BIN (widget)) == NULL)
2271 if ((!priv->enabled) || (!priv->button_pressed) ||
2272 ((event->time == priv->last_time) && (priv->last_type == 2))) {
2273 gdk_window_get_pointer (widget->window, NULL, NULL, 0);
2277 if (priv->last_type == 1) {
2278 priv->first_drag = TRUE;
2281 x = event->x - priv->x;
2282 y = event->y - priv->y;
2284 if (priv->first_drag && (!priv->moved) &&
2285 ((ABS (x) > (priv->panning_threshold))
2286 || (ABS (y) > (priv->panning_threshold)))) {
2291 if (priv->first_drag) {
2292 gboolean vscroll_visible;
2293 gboolean hscroll_visible;
2295 if (ABS (priv->iy - event->y) >=
2296 ABS (priv->ix - event->x)) {
2298 g_signal_emit (area,
2299 pannable_area_signals[VERTICAL_MOVEMENT],
2300 0, (priv->iy > event->y) ?
2301 HILDON_MOVEMENT_UP :
2302 HILDON_MOVEMENT_DOWN,
2303 (gdouble)priv->ix, (gdouble)priv->iy);
2305 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
2306 priv->vadjust->page_size);
2308 if (!((vscroll_visible)&&
2309 (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT))) {
2311 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
2312 priv->hadjust->page_size);
2314 /* even in case we do not have to move we check if this
2315 could be a fake horizontal movement */
2316 if (!((hscroll_visible)&&
2317 (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ)) ||
2318 (ABS (priv->iy - event->y) -
2319 ABS (priv->ix - event->x) >= priv->direction_error_margin))
2320 priv->moved = FALSE;
2324 g_signal_emit (area,
2325 pannable_area_signals[HORIZONTAL_MOVEMENT],
2326 0, (priv->ix > event->x) ?
2327 HILDON_MOVEMENT_LEFT :
2328 HILDON_MOVEMENT_RIGHT,
2329 (gdouble)priv->ix, (gdouble)priv->iy);
2331 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
2332 priv->hadjust->page_size);
2334 if (!((hscroll_visible)&&
2335 (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ))) {
2337 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
2338 priv->vadjust->page_size);
2340 /* even in case we do not have to move we check if this
2341 could be a fake vertical movement */
2342 if (!((vscroll_visible) &&
2343 (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT)) ||
2344 (ABS (priv->ix - event->x) -
2345 ABS (priv->iy - event->y) >= priv->direction_error_margin))
2346 priv->moved = FALSE;
2350 if ((priv->moved)&&(priv->child)) {
2353 pos_x = priv->cx + (event->x - priv->ix);
2354 pos_y = priv->cy + (event->y - priv->iy);
2356 synth_crossing (priv->child, pos_x, pos_y, event->x_root,
2357 event->y_root, event->time, FALSE);
2361 priv->first_drag = FALSE;
2363 if ((priv->mode != HILDON_PANNABLE_AREA_MODE_PUSH) &&
2364 (priv->mode != HILDON_PANNABLE_AREA_MODE_AUTO)) {
2367 priv->idle_id = gdk_threads_add_timeout ((gint)
2368 (1000.0 / (gdouble) priv->sps),
2370 hildon_pannable_area_timeout, area);
2375 switch (priv->mode) {
2376 case HILDON_PANNABLE_AREA_MODE_PUSH:
2377 /* Scroll by the amount of pixels the cursor has moved
2378 * since the last motion event.
2380 hildon_pannable_area_motion_event_scroll (area, x, y);
2384 case HILDON_PANNABLE_AREA_MODE_ACCEL:
2385 /* Set acceleration relative to the initial click */
2386 priv->ex = event->x;
2387 priv->ey = event->y;
2388 priv->vel_x = ((x > 0) ? 1 : -1) *
2390 (gdouble) widget->allocation.width) *
2391 (priv->vmax - priv->vmin)) + priv->vmin);
2392 priv->vel_y = ((y > 0) ? 1 : -1) *
2394 (gdouble) widget->allocation.height) *
2395 (priv->vmax - priv->vmin)) + priv->vmin);
2397 case HILDON_PANNABLE_AREA_MODE_AUTO:
2399 delta = event->time - priv->last_time;
2401 if (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT) {
2402 gdouble dist = event->y - priv->y;
2404 hildon_pannable_area_calculate_velocity (&priv->vel_y,
2416 if (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ) {
2417 gdouble dist = event->x - priv->x;
2419 hildon_pannable_area_calculate_velocity (&priv->vel_x,
2431 hildon_pannable_area_motion_event_scroll (area, x, y);
2433 if (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ)
2435 if (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT)
2443 } else if (priv->child) {
2447 pos_x = priv->cx + (event->x - priv->ix);
2448 pos_y = priv->cy + (event->y - priv->iy);
2450 in = (((0 <= pos_x)&&(priv->child_width >= pos_x)) &&
2451 ((0 <= pos_y)&&(priv->child_height >= pos_y)));
2453 if (((!priv->last_in)&&in)||((priv->last_in)&&(!in))) {
2455 synth_crossing (priv->child, pos_x, pos_y, event->x_root,
2456 event->y_root, event->time, in);
2462 priv->last_time = event->time;
2463 priv->last_type = 2;
2466 /* Send motion notify to child */
2467 event = (GdkEventMotion *) gdk_event_copy ((GdkEvent *) event);
2468 event->x = priv->cx + (event->x - priv->ix);
2469 event->y = priv->cy + (event->y - priv->iy);
2470 event->window = g_object_ref (priv->child);
2471 gdk_event_put ((GdkEvent *) event);
2472 gdk_event_free ((GdkEvent *) event);
2475 gdk_window_get_pointer (widget->window, NULL, NULL, 0);
2481 hildon_pannable_leave_notify_event (GtkWidget *widget,
2482 GdkEventCrossing *event)
2484 HildonPannableArea *area = HILDON_PANNABLE_AREA (widget);
2485 HildonPannableAreaPrivate *priv = area->priv;
2487 if ((priv->child)&&(priv->last_in)) {
2488 priv->last_in = FALSE;
2490 synth_crossing (priv->child, 0, 0, event->x_root,
2491 event->y_root, event->time, FALSE);
2498 hildon_pannable_area_button_release_cb (GtkWidget * widget,
2499 GdkEventButton * event)
2501 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
2505 if (((event->time == priv->last_time) && (priv->last_type == 3))
2506 || (gtk_bin_get_child (GTK_BIN (widget)) == NULL)
2507 || (!priv->button_pressed) || (!priv->enabled) || (event->button != 1))
2510 priv->scroll_indicator_event_interrupt = 0;
2511 priv->scroll_delay_counter = priv->scrollbar_fade_delay;
2513 /* move all the way to the last position */
2514 if (priv->motion_event_scroll_timeout) {
2515 g_source_remove (priv->motion_event_scroll_timeout);
2516 hildon_pannable_area_motion_event_scroll_timeout (HILDON_PANNABLE_AREA (widget));
2521 if (priv->last_type == 2) {
2522 gdouble delta = event->time - priv->last_time;
2524 if (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT) {
2525 gdouble dist = event->y - priv->y;
2527 if (ABS (dist) >= 1.0) {
2528 hildon_pannable_area_calculate_velocity (&priv->vel_y,
2536 priv->motion_y = dist;
2537 hildon_pannable_area_motion_event_scroll_timeout (HILDON_PANNABLE_AREA (widget));
2539 if (delta >= CURSOR_STOPPED_TIMEOUT) {
2545 if (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ) {
2546 gdouble dist = event->x - priv->x;
2548 if (ABS (dist) >= 1.0) {
2549 hildon_pannable_area_calculate_velocity (&priv->vel_x,
2556 priv->motion_x = dist;
2557 hildon_pannable_area_motion_event_scroll_timeout (HILDON_PANNABLE_AREA (widget));
2559 if (delta >= CURSOR_STOPPED_TIMEOUT) {
2566 if ((ABS (priv->vel_y) > 1.0)||
2567 (ABS (priv->vel_x) > 1.0)) {
2568 priv->scroll_indicator_alpha = 1.0;
2571 hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget),
2572 priv->scroll_indicator_alpha);
2574 priv->button_pressed = FALSE;
2576 if (priv->mode == HILDON_PANNABLE_AREA_MODE_AUTO ||
2577 priv->mode == HILDON_PANNABLE_AREA_MODE_ACCEL) {
2579 /* If overshoot has been initiated with a finger down, on release set max speed */
2580 if (priv->overshot_dist_y != 0) {
2581 priv->overshooting_y = priv->bounce_steps; /* Hack to stop a bounce in the finger down case */
2582 priv->vel_y = priv->vmax_overshooting;
2585 if (priv->overshot_dist_x != 0) {
2586 priv->overshooting_x = priv->bounce_steps; /* Hack to stop a bounce in the finger down case */
2587 priv->vel_x = priv->vmax_overshooting;
2590 if ((ABS (priv->vel_y) >= priv->vmin) ||
2591 (ABS (priv->vel_x) >= priv->vmin)) {
2594 priv->idle_id = gdk_threads_add_timeout ((gint) (1000.0 / (gdouble) priv->sps),
2596 hildon_pannable_area_timeout, widget);
2600 priv->last_time = event->time;
2601 priv->last_type = 3;
2604 priv->moved = FALSE;
2609 hildon_pannable_area_get_topmost (gtk_bin_get_child (GTK_BIN (widget))->window,
2610 event->x, event->y, &x, &y, GDK_BUTTON_RELEASE_MASK);
2612 event = (GdkEventButton *) gdk_event_copy ((GdkEvent *) event);
2616 /* Leave the widget if we've moved - This doesn't break selection,
2617 * but stops buttons from being clicked.
2619 if ((child != priv->child) || (priv->moved)) {
2620 /* Send synthetic leave event */
2621 synth_crossing (priv->child, x, y, event->x_root,
2622 event->y_root, event->time, FALSE);
2623 /* Send synthetic button release event */
2624 ((GdkEventAny *) event)->window = g_object_ref (priv->child);
2625 gdk_event_put ((GdkEvent *) event);
2627 /* Send synthetic button release event */
2628 ((GdkEventAny *) event)->window = g_object_ref (child);
2629 gdk_event_put ((GdkEvent *) event);
2630 /* Send synthetic leave event */
2631 synth_crossing (priv->child, x, y, event->x_root,
2632 event->y_root, event->time, FALSE);
2634 g_object_remove_weak_pointer ((GObject *) priv->child,
2635 (gpointer) & priv->child);
2637 priv->moved = FALSE;
2638 gdk_event_free ((GdkEvent *) event);
2643 /* utility event handler */
2645 hildon_pannable_area_scroll_cb (GtkWidget *widget,
2646 GdkEventScroll *event)
2648 GtkAdjustment *adj = NULL;
2649 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
2651 if ((!priv->enabled) ||
2652 (gtk_bin_get_child (GTK_BIN (widget)) == NULL))
2655 priv->scroll_indicator_event_interrupt = 0;
2656 priv->scroll_delay_counter = priv->scrollbar_fade_delay + 20;
2658 hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget), 1.0);
2660 /* Stop inertial scrolling */
2661 if (priv->idle_id) {
2664 priv->overshooting_x = 0;
2665 priv->overshooting_y = 0;
2667 if ((priv->overshot_dist_x>0)||(priv->overshot_dist_y>0)) {
2668 priv->overshot_dist_x = 0;
2669 priv->overshot_dist_y = 0;
2671 gtk_widget_queue_resize (GTK_WIDGET (widget));
2674 g_source_remove (priv->idle_id);
2678 if (event->direction == GDK_SCROLL_UP || event->direction == GDK_SCROLL_DOWN)
2679 adj = priv->vadjust;
2681 adj = priv->hadjust;
2685 gdouble delta, new_value;
2687 /* from gtkrange.c calculate delta*/
2688 delta = pow (adj->page_size, 2.0 / 3.0);
2690 if (event->direction == GDK_SCROLL_UP ||
2691 event->direction == GDK_SCROLL_LEFT)
2694 new_value = CLAMP (adj->value + delta, adj->lower, adj->upper - adj->page_size);
2696 gtk_adjustment_set_value (adj, new_value);
2703 hildon_pannable_area_child_mapped (GtkWidget *widget,
2707 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (user_data)->priv;
2709 if (priv->event_window != NULL && priv->enabled)
2710 gdk_window_raise (priv->event_window);
2714 hildon_pannable_area_add (GtkContainer *container, GtkWidget *child)
2716 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (container)->priv;
2718 g_return_if_fail (gtk_bin_get_child (GTK_BIN (container)) == NULL);
2720 gtk_widget_set_parent (child, GTK_WIDGET (container));
2721 GTK_BIN (container)->child = child;
2723 g_signal_connect_after (child, "map-event",
2724 G_CALLBACK (hildon_pannable_area_child_mapped),
2727 if (!gtk_widget_set_scroll_adjustments (child, priv->hadjust, priv->vadjust)) {
2728 g_warning ("%s: cannot add non scrollable widget, "
2729 "wrap it in a viewport", __FUNCTION__);
2734 hildon_pannable_area_remove (GtkContainer *container, GtkWidget *child)
2736 g_return_if_fail (HILDON_IS_PANNABLE_AREA (container));
2737 g_return_if_fail (child != NULL);
2738 g_return_if_fail (gtk_bin_get_child (GTK_BIN (container)) == child);
2740 gtk_widget_set_scroll_adjustments (child, NULL, NULL);
2742 g_signal_handlers_disconnect_by_func (child,
2743 hildon_pannable_area_child_mapped,
2746 /* chain parent class handler to remove child */
2747 GTK_CONTAINER_CLASS (hildon_pannable_area_parent_class)->remove (container, child);
2751 * This method calculates a factor necessary to determine the initial distance
2752 * to jump in hildon_pannable_area_scroll_to(). For fixed time and frames per
2753 * second, we know in how many frames 'n' we need to reach the destination
2754 * point. We know that, for a distance d,
2756 * d = d_0 + d_1 + ... + d_n
2758 * where d_i is the distance travelled in the i-th frame and decel_factor is
2759 * the deceleration factor. This can be rewritten as
2761 * d = d_0 + (d_0 * decel_factor) + ... + (d_n-1 * decel_factor),
2763 * since the distance travelled on each frame is the distance travelled in the
2764 * previous frame reduced by the deceleration factor. Reducing this and
2765 * factoring d_0 out, we get
2767 * d = d_0 (1 + decel_factor + ... + decel_factor^(n-1)).
2769 * Since the sum is independent of the distance to be travelled, we can define
2772 * vel_factor = 1 + decel_factor + ... + decel_factor^(n-1).
2774 * That's the gem we calculate in this method.
2777 hildon_pannable_calculate_vel_factor (HildonPannableArea * self)
2779 HildonPannableAreaPrivate *priv = self->priv;
2784 n = ceil (priv->sps * priv->scroll_time);
2786 for (i = 1; i < n && fct_i >= RATIO_TOLERANCE; i++) {
2787 fct_i *= priv->decel;
2791 priv->vel_factor = fct;
2795 * hildon_pannable_area_new:
2797 * Create a new pannable area widget
2799 * Returns: the newly created #HildonPannableArea
2805 hildon_pannable_area_new (void)
2807 return g_object_new (HILDON_TYPE_PANNABLE_AREA, NULL);
2811 * hildon_pannable_area_new_full:
2812 * @mode: #HildonPannableAreaMode
2813 * @enabled: Value for the enabled property
2814 * @vel_min: Value for the velocity-min property
2815 * @vel_max: Value for the velocity-max property
2816 * @decel: Value for the deceleration property
2817 * @sps: Value for the sps property
2819 * Create a new #HildonPannableArea widget and set various properties
2821 * returns: the newly create #HildonPannableArea
2827 hildon_pannable_area_new_full (gint mode, gboolean enabled,
2828 gdouble vel_min, gdouble vel_max,
2829 gdouble decel, guint sps)
2831 return g_object_new (HILDON_TYPE_PANNABLE_AREA,
2834 "velocity_min", vel_min,
2835 "velocity_max", vel_max,
2836 "deceleration", decel, "sps", sps, NULL);
2840 * hildon_pannable_area_add_with_viewport:
2841 * @area: A #HildonPannableArea
2842 * @child: Child widget to add to the viewport
2844 * Convenience function used to add a child to a #GtkViewport, and add the
2845 * viewport to the scrolled window.
2851 hildon_pannable_area_add_with_viewport (HildonPannableArea * area,
2855 GtkWidget *viewport;
2857 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
2858 g_return_if_fail (GTK_IS_WIDGET (child));
2859 g_return_if_fail (child->parent == NULL);
2861 bin = GTK_BIN (area);
2863 if (bin->child != NULL)
2865 g_return_if_fail (GTK_IS_VIEWPORT (bin->child));
2866 g_return_if_fail (GTK_BIN (bin->child)->child == NULL);
2868 viewport = bin->child;
2872 HildonPannableAreaPrivate *priv = area->priv;
2874 viewport = gtk_viewport_new (priv->hadjust,
2876 gtk_viewport_set_shadow_type (GTK_VIEWPORT (viewport), GTK_SHADOW_NONE);
2877 gtk_container_add (GTK_CONTAINER (area), viewport);
2880 gtk_widget_show (viewport);
2881 gtk_container_add (GTK_CONTAINER (viewport), child);
2885 * hildon_pannable_area_scroll_to:
2886 * @area: A #HildonPannableArea.
2887 * @x: The x coordinate of the destination point or -1 to ignore this axis.
2888 * @y: The y coordinate of the destination point or -1 to ignore this axis.
2890 * Smoothly scrolls @area to ensure that (@x, @y) is a visible point
2891 * on the widget. To move in only one coordinate, you must set the other one
2892 * to -1. Notice that, in %HILDON_PANNABLE_AREA_MODE_PUSH mode, this function
2893 * works just like hildon_pannable_area_jump_to().
2895 * This function is useful if you need to present the user with a particular
2896 * element inside a scrollable widget, like #GtkTreeView. For instance,
2897 * the following example shows how to scroll inside a #GtkTreeView to
2898 * make visible an item, indicated by the #GtkTreeIter @iter.
2902 * GtkTreePath *path;
2903 * GdkRectangle *rect;
2905 * path = gtk_tree_model_get_path (model, &iter);
2906 * gtk_tree_view_get_background_area (GTK_TREE_VIEW (treeview),
2907 * path, NULL, &rect);
2908 * gtk_tree_view_convert_bin_window_to_tree_coords (GTK_TREE_VIEW (treeview),
2909 * 0, rect.y, NULL, &y);
2910 * hildon_pannable_area_scroll_to (panarea, -1, y);
2911 * gtk_tree_path_free (path);
2915 * If you want to present a child widget in simpler scenarios,
2916 * use hildon_pannable_area_scroll_to_child() instead.
2918 * There is a precondition to this function: the widget must be
2919 * already realized. Check the hildon_pannable_area_jump_to_child() for
2920 * more tips regarding how to call this function during
2926 hildon_pannable_area_scroll_to (HildonPannableArea *area,
2927 const gint x, const gint y)
2929 HildonPannableAreaPrivate *priv;
2931 gint dist_x, dist_y;
2932 gboolean hscroll_visible, vscroll_visible;
2934 g_return_if_fail (GTK_WIDGET_REALIZED (area));
2935 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
2939 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
2940 priv->vadjust->page_size);
2941 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
2942 priv->hadjust->page_size);
2944 if (((!vscroll_visible)&&(!hscroll_visible)) ||
2945 (x == -1 && y == -1)) {
2949 if (priv->mode == HILDON_PANNABLE_AREA_MODE_PUSH)
2950 hildon_pannable_area_jump_to (area, x, y);
2952 width = priv->hadjust->upper - priv->hadjust->lower;
2953 height = priv->vadjust->upper - priv->vadjust->lower;
2955 g_return_if_fail (x < width || y < height);
2957 if ((x > -1)&&(hscroll_visible)) {
2958 priv->scroll_to_x = x - priv->hadjust->page_size/2;
2959 dist_x = priv->scroll_to_x - priv->hadjust->value;
2961 priv->scroll_to_x = -1;
2963 priv->vel_x = - dist_x/priv->vel_factor;
2966 priv->scroll_to_x = -1;
2969 if ((y > -1)&&(vscroll_visible)) {
2970 priv->scroll_to_y = y - priv->vadjust->page_size/2;
2971 dist_y = priv->scroll_to_y - priv->vadjust->value;
2973 priv->scroll_to_y = -1;
2975 priv->vel_y = - dist_y/priv->vel_factor;
2978 priv->scroll_to_y = y;
2981 if ((priv->scroll_to_y == -1) && (priv->scroll_to_y == -1)) {
2985 hildon_pannable_area_launch_fade_timeout (area, 1.0);
2988 priv->idle_id = gdk_threads_add_timeout ((gint) (1000.0 / (gdouble) priv->sps),
2990 hildon_pannable_area_timeout, area);
2994 * hildon_pannable_area_jump_to:
2995 * @area: A #HildonPannableArea.
2996 * @x: The x coordinate of the destination point or -1 to ignore this axis.
2997 * @y: The y coordinate of the destination point or -1 to ignore this axis.
2999 * Jumps the position of @area to ensure that (@x, @y) is a visible
3000 * point in the widget. In order to move in only one coordinate, you
3001 * must set the other one to -1. See hildon_pannable_area_scroll_to()
3002 * function for an example of how to calculate the position of
3003 * children in scrollable widgets like #GtkTreeview.
3005 * There is a precondition to this function: the widget must be
3006 * already realized. Check the hildon_pannable_area_jump_to_child() for
3007 * more tips regarding how to call this function during
3013 hildon_pannable_area_jump_to (HildonPannableArea *area,
3014 const gint x, const gint y)
3016 HildonPannableAreaPrivate *priv;
3019 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3020 g_return_if_fail (GTK_WIDGET_REALIZED (area));
3021 g_return_if_fail (x >= -1 && y >= -1);
3023 if (x == -1 && y == -1) {
3029 width = priv->hadjust->upper - priv->hadjust->lower;
3030 height = priv->vadjust->upper - priv->vadjust->lower;
3032 g_return_if_fail (x < width || y < height);
3035 gdouble jump_to = x - priv->hadjust->page_size/2;
3037 if (jump_to > priv->hadjust->upper - priv->hadjust->page_size) {
3038 jump_to = priv->hadjust->upper - priv->hadjust->page_size;
3041 gtk_adjustment_set_value (priv->hadjust, jump_to);
3045 gdouble jump_to = y - priv->vadjust->page_size/2;
3047 if (jump_to > priv->vadjust->upper - priv->vadjust->page_size) {
3048 jump_to = priv->vadjust->upper - priv->vadjust->page_size;
3051 gtk_adjustment_set_value (priv->vadjust, jump_to);
3054 priv->scroll_indicator_alpha = 1.0;
3056 if (priv->scroll_indicator_timeout) {
3057 g_source_remove (priv->scroll_indicator_timeout);
3058 priv->scroll_indicator_timeout = 0;
3061 if (priv->idle_id) {
3064 priv->overshooting_x = 0;
3065 priv->overshooting_y = 0;
3067 if ((priv->overshot_dist_x>0)||(priv->overshot_dist_y>0)) {
3068 priv->overshot_dist_x = 0;
3069 priv->overshot_dist_y = 0;
3071 gtk_widget_queue_resize (GTK_WIDGET (area));
3074 g_source_remove (priv->idle_id);
3080 * hildon_pannable_area_scroll_to_child:
3081 * @area: A #HildonPannableArea.
3082 * @child: A #GtkWidget, descendant of @area.
3084 * Smoothly scrolls until @child is visible inside @area. @child must
3085 * be a descendant of @area. If you need to scroll inside a scrollable
3086 * widget, e.g., #GtkTreeview, see hildon_pannable_area_scroll_to().
3088 * There is a precondition to this function: the widget must be
3089 * already realized. Check the hildon_pannable_area_jump_to_child() for
3090 * more tips regarding how to call this function during
3096 hildon_pannable_area_scroll_to_child (HildonPannableArea *area, GtkWidget *child)
3098 GtkWidget *bin_child;
3101 g_return_if_fail (GTK_WIDGET_REALIZED (area));
3102 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3103 g_return_if_fail (GTK_IS_WIDGET (child));
3104 g_return_if_fail (gtk_widget_is_ancestor (child, GTK_WIDGET (area)));
3106 if (GTK_BIN (area)->child == NULL)
3109 /* We need to get to check the child of the inside the area */
3110 bin_child = GTK_BIN (area)->child;
3112 /* we check if we added a viewport */
3113 if (GTK_IS_VIEWPORT (bin_child)) {
3114 bin_child = GTK_BIN (bin_child)->child;
3117 if (gtk_widget_translate_coordinates (child, bin_child, 0, 0, &x, &y))
3118 hildon_pannable_area_scroll_to (area, x, y);
3122 * hildon_pannable_area_jump_to_child:
3123 * @area: A #HildonPannableArea.
3124 * @child: A #GtkWidget, descendant of @area.
3126 * Jumps to make sure @child is visible inside @area. @child must
3127 * be a descendant of @area. If you want to move inside a scrollable
3128 * widget, like, #GtkTreeview, see hildon_pannable_area_scroll_to().
3130 * There is a precondition to this function: the widget must be
3131 * already realized. You can control if the widget is ready with the
3132 * GTK_WIDGET_REALIZED macro. If you want to call this function during
3133 * the initialization process of the widget do it inside a callback to
3134 * the ::realize signal, using g_signal_connect_after() function.
3139 hildon_pannable_area_jump_to_child (HildonPannableArea *area, GtkWidget *child)
3141 GtkWidget *bin_child;
3144 g_return_if_fail (GTK_WIDGET_REALIZED (area));
3145 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3146 g_return_if_fail (GTK_IS_WIDGET (child));
3147 g_return_if_fail (gtk_widget_is_ancestor (child, GTK_WIDGET (area)));
3149 if (gtk_bin_get_child (GTK_BIN (area)) == NULL)
3152 /* We need to get to check the child of the inside the area */
3153 bin_child = gtk_bin_get_child (GTK_BIN (area));
3155 /* we check if we added a viewport */
3156 if (GTK_IS_VIEWPORT (bin_child)) {
3157 bin_child = gtk_bin_get_child (GTK_BIN (bin_child));
3160 if (gtk_widget_translate_coordinates (child, bin_child, 0, 0, &x, &y))
3161 hildon_pannable_area_jump_to (area, x, y);
3165 * hildon_pannable_get_child_widget_at:
3166 * @area: A #HildonPannableArea.
3167 * @x: horizontal coordinate of the point
3168 * @y: vertical coordinate of the point
3170 * Get the widget at the point (x, y) inside the pannable area. In
3171 * case no widget found it returns NULL.
3173 * returns: the #GtkWidget if we find a widget, NULL in any other case
3178 hildon_pannable_get_child_widget_at (HildonPannableArea *area,
3179 gdouble x, gdouble y)
3181 GdkWindow *window = NULL;
3182 GtkWidget *child_widget = NULL;
3184 window = hildon_pannable_area_get_topmost
3185 (gtk_bin_get_child (GTK_BIN (area))->window,
3186 x, y, NULL, NULL, GDK_ALL_EVENTS_MASK);
3188 gdk_window_get_user_data (window, (gpointer) &child_widget);
3190 return child_widget;
3195 * hildon_pannable_area_get_hadjustment:
3196 * @area: A #HildonPannableArea.
3198 * Returns the horizontal adjustment. This adjustment is the internal
3199 * widget adjustment used to control the animations. Do not modify it
3200 * directly to change the position of the pannable, to do that use the
3201 * pannable API. If you modify the object directly it could cause
3202 * artifacts in the animations.
3204 * returns: The horizontal #GtkAdjustment
3209 hildon_pannable_area_get_hadjustment (HildonPannableArea *area)
3212 g_return_val_if_fail (HILDON_IS_PANNABLE_AREA (area), NULL);
3214 return area->priv->hadjust;
3218 * hildon_pannable_area_get_vadjustment:
3219 * @area: A #HildonPannableArea.
3221 * Returns the vertical adjustment. This adjustment is the internal
3222 * widget adjustment used to control the animations. Do not modify it
3223 * directly to change the position of the pannable, to do that use the
3224 * pannable API. If you modify the object directly it could cause
3225 * artifacts in the animations.
3227 * returns: The vertical #GtkAdjustment
3232 hildon_pannable_area_get_vadjustment (HildonPannableArea *area)
3234 g_return_val_if_fail (HILDON_IS_PANNABLE_AREA (area), NULL);
3236 return area->priv->vadjust;
3241 * hildon_pannable_area_get_size_request_policy:
3242 * @area: A #HildonPannableArea.
3244 * This function returns the current size request policy of the
3245 * widget. That policy controls the way the size_request is done in
3246 * the pannable area. Check
3247 * hildon_pannable_area_set_size_request_policy() for a more detailed
3250 * returns: the policy is currently being used in the widget
3251 * #HildonSizeRequestPolicy.
3255 HildonSizeRequestPolicy
3256 hildon_pannable_area_get_size_request_policy (HildonPannableArea *area)
3258 HildonPannableAreaPrivate *priv;
3260 g_return_val_if_fail (HILDON_IS_PANNABLE_AREA (area), FALSE);
3264 return priv->size_request_policy;
3268 * hildon_pannable_area_set_size_request_policy:
3269 * @area: A #HildonPannableArea.
3270 * @size_request_policy: One of the allowed #HildonSizeRequestPolicy
3272 * This function sets the pannable area size request policy. That
3273 * policy controls the way the size_request is done in the pannable
3274 * area. Pannable can use the size request of its children
3275 * (#HILDON_SIZE_REQUEST_CHILDREN) or the minimum size required for
3276 * the area itself (#HILDON_SIZE_REQUEST_MINIMUM), the latter is the
3277 * default. Recall this size depends on the scrolling policy you are
3278 * requesting to the pannable area, if you set #GTK_POLICY_NEVER this
3279 * parameter will not have any effect with
3280 * #HILDON_SIZE_REQUEST_MINIMUM set.
3284 * Deprecated: This method and the policy request is deprecated, DO
3285 * NOT use it in future code, the only policy properly supported in
3286 * gtk+ nowadays is the minimum size. Use #gtk_window_set_default_size
3287 * or #gtk_window_set_geometry_hints with the proper size in your case
3288 * to define the height of your dialogs.
3291 hildon_pannable_area_set_size_request_policy (HildonPannableArea *area,
3292 HildonSizeRequestPolicy size_request_policy)
3294 HildonPannableAreaPrivate *priv;
3296 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3300 if (priv->size_request_policy == size_request_policy)
3303 priv->size_request_policy = size_request_policy;
3305 gtk_widget_queue_resize (GTK_WIDGET (area));
3307 g_object_notify (G_OBJECT (area), "size-request-policy");