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 200
60 #define MAX_SPEED_THRESHOLD 290
61 #define PANNABLE_MAX_WIDTH 788
62 #define PANNABLE_MAX_HEIGHT 378
64 G_DEFINE_TYPE (HildonPannableArea, hildon_pannable_area, GTK_TYPE_BIN)
66 #define PANNABLE_AREA_PRIVATE(o) \
67 (G_TYPE_INSTANCE_GET_PRIVATE ((o), HILDON_TYPE_PANNABLE_AREA, \
68 HildonPannableAreaPrivate))
70 struct _HildonPannableAreaPrivate {
71 HildonPannableAreaMode mode;
72 HildonMovementMode mov_mode;
73 GdkWindow *event_window;
74 gdouble x; /* Used to store mouse co-ordinates of the first or */
75 gdouble y; /* previous events in a press-motion pair */
76 gdouble ex; /* Used to store mouse co-ordinates of the last */
77 gdouble ey; /* motion event in acceleration mode */
79 gboolean button_pressed;
80 guint32 last_time; /* Last event time, to stop infinite loops */
81 guint32 last_press_time;
87 gdouble vmax_overshooting;
94 guint panning_threshold;
95 guint scrollbar_fade_delay;
98 guint direction_error_margin;
106 gint ix; /* Initial click mouse co-ordinates */
108 gint cx; /* Initial click child window mouse co-ordinates */
115 gint overshot_dist_x;
116 gint overshot_dist_y;
119 gdouble scroll_indicator_alpha;
120 gint motion_event_scroll_timeout;
121 gint scroll_indicator_timeout;
122 gint scroll_indicator_event_interrupt;
123 gint scroll_delay_counter;
126 gboolean initial_hint;
127 gboolean initial_effect;
128 gboolean low_friction_mode;
131 gboolean size_request_policy;
132 gboolean hscroll_visible;
133 gboolean vscroll_visible;
134 GdkRectangle hscroll_rect;
135 GdkRectangle vscroll_rect;
136 guint indicator_width;
138 GtkAdjustment *hadjust;
139 GtkAdjustment *vadjust;
143 GtkPolicyType vscrollbar_policy;
144 GtkPolicyType hscrollbar_policy;
146 GdkGC *scrollbars_gc;
147 GdkColor scroll_color;
149 gboolean center_on_child_focus;
150 gboolean center_on_child_focus_pending;
162 static guint pannable_area_signals [LAST_SIGNAL] = { 0 };
170 PROP_VEL_MAX_OVERSHOOTING,
171 PROP_VELOCITY_FAST_FACTOR,
175 PROP_PANNING_THRESHOLD,
176 PROP_SCROLLBAR_FADE_DELAY,
179 PROP_DIRECTION_ERROR_MARGIN,
180 PROP_VSCROLLBAR_POLICY,
181 PROP_HSCROLLBAR_POLICY,
186 PROP_LOW_FRICTION_MODE,
187 PROP_SIZE_REQUEST_POLICY,
190 PROP_CENTER_ON_CHILD_FOCUS,
194 static void hildon_pannable_area_class_init (HildonPannableAreaClass * klass);
195 static void hildon_pannable_area_init (HildonPannableArea * area);
196 static void hildon_pannable_area_get_property (GObject * object,
200 static void hildon_pannable_area_set_property (GObject * object,
202 const GValue * value,
204 static void hildon_pannable_area_remove_timeouts (GtkWidget * widget);
205 static void hildon_pannable_area_dispose (GObject * object);
206 static void hildon_pannable_area_realize (GtkWidget * widget);
207 static void hildon_pannable_area_unrealize (GtkWidget * widget);
208 static void hildon_pannable_area_size_request (GtkWidget * widget,
209 GtkRequisition * requisition);
210 static void hildon_pannable_area_size_allocate (GtkWidget * widget,
211 GtkAllocation * allocation);
212 static void hildon_pannable_area_child_allocate_calculate (GtkWidget * widget,
213 GtkAllocation * allocation,
214 GtkAllocation * child_allocation);
215 static void hildon_pannable_area_style_set (GtkWidget * widget,
216 GtkStyle * previous_style);
217 static void hildon_pannable_area_map (GtkWidget * widget);
218 static void hildon_pannable_area_unmap (GtkWidget * widget);
219 static void hildon_pannable_area_grab_notify (GtkWidget *widget,
220 gboolean was_grabbed,
222 #if USE_CAIRO_SCROLLBARS == 1
223 static void rgb_from_gdkcolor (GdkColor *color, gdouble *r, gdouble *g, gdouble *b);
224 #else /* USE_CAIRO_SCROLLBARS */
225 static void tranparency_color (GdkColor *color,
228 gdouble transparency);
229 #endif /* USE_CAIRO_SCROLLBARS */
230 static void hildon_pannable_draw_vscroll (GtkWidget * widget,
231 GdkColor *back_color,
232 GdkColor *scroll_color);
233 static void hildon_pannable_draw_hscroll (GtkWidget * widget,
234 GdkColor *back_color,
235 GdkColor *scroll_color);
236 static void hildon_pannable_area_initial_effect (GtkWidget * widget);
237 static void hildon_pannable_area_redraw (HildonPannableArea * area);
238 static void hildon_pannable_area_launch_fade_timeout (HildonPannableArea * area,
240 static void hildon_pannable_area_adjust_value_changed (HildonPannableArea * area,
242 static void hildon_pannable_area_adjust_changed (HildonPannableArea * area,
244 static gboolean hildon_pannable_area_scroll_indicator_fade(HildonPannableArea * area);
245 static gboolean hildon_pannable_area_expose_event (GtkWidget * widget,
246 GdkEventExpose * event);
247 static GdkWindow * hildon_pannable_area_get_topmost (GdkWindow * window,
249 gint * tx, gint * ty,
251 static void synth_crossing (GdkWindow * child,
253 gint x_root, gint y_root,
254 guint32 time, gboolean in);
255 static gboolean hildon_pannable_area_button_press_cb (GtkWidget * widget,
256 GdkEventButton * event);
257 static void hildon_pannable_area_refresh (HildonPannableArea * area);
258 static gboolean hildon_pannable_area_check_scrollbars (HildonPannableArea * area);
259 static void hildon_pannable_axis_scroll (HildonPannableArea *area,
260 GtkAdjustment *adjust,
268 static void hildon_pannable_area_scroll (HildonPannableArea *area,
269 gdouble x, gdouble y);
270 static gboolean hildon_pannable_area_timeout (HildonPannableArea * area);
271 static void hildon_pannable_area_calculate_velocity (gdouble *vel,
275 gdouble drag_inertia,
278 static gboolean hildon_pannable_area_motion_event_scroll_timeout (HildonPannableArea *area);
279 static void hildon_pannable_area_motion_event_scroll (HildonPannableArea *area,
280 gdouble x, gdouble y);
281 static void hildon_pannable_area_check_move (HildonPannableArea *area,
282 GdkEventMotion * event,
285 static void hildon_pannable_area_handle_move (HildonPannableArea *area,
286 GdkEventMotion * event,
289 static gboolean hildon_pannable_area_motion_notify_cb (GtkWidget * widget,
290 GdkEventMotion * event);
291 static gboolean hildon_pannable_leave_notify_event (GtkWidget *widget,
292 GdkEventCrossing *event);
293 static gboolean hildon_pannable_area_button_release_cb (GtkWidget * widget,
294 GdkEventButton * event);
295 static gboolean hildon_pannable_area_scroll_cb (GtkWidget *widget,
296 GdkEventScroll *event);
297 static void hildon_pannable_area_child_mapped (GtkWidget *widget,
300 static void hildon_pannable_area_add (GtkContainer *container, GtkWidget *child);
301 static void hildon_pannable_area_remove (GtkContainer *container, GtkWidget *child);
302 static void hildon_pannable_calculate_vel_factor (HildonPannableArea * self);
303 static void hildon_pannable_area_set_focus_child (GtkContainer *container,
305 static void hildon_pannable_area_center_on_child_focus (HildonPannableArea *area);
309 hildon_pannable_area_class_init (HildonPannableAreaClass * klass)
311 GObjectClass *object_class = G_OBJECT_CLASS (klass);
312 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
313 GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
316 g_type_class_add_private (klass, sizeof (HildonPannableAreaPrivate));
318 object_class->get_property = hildon_pannable_area_get_property;
319 object_class->set_property = hildon_pannable_area_set_property;
320 object_class->dispose = hildon_pannable_area_dispose;
322 widget_class->realize = hildon_pannable_area_realize;
323 widget_class->unrealize = hildon_pannable_area_unrealize;
324 widget_class->map = hildon_pannable_area_map;
325 widget_class->unmap = hildon_pannable_area_unmap;
326 widget_class->size_request = hildon_pannable_area_size_request;
327 widget_class->size_allocate = hildon_pannable_area_size_allocate;
328 widget_class->expose_event = hildon_pannable_area_expose_event;
329 widget_class->style_set = hildon_pannable_area_style_set;
330 widget_class->button_press_event = hildon_pannable_area_button_press_cb;
331 widget_class->button_release_event = hildon_pannable_area_button_release_cb;
332 widget_class->motion_notify_event = hildon_pannable_area_motion_notify_cb;
333 widget_class->leave_notify_event = hildon_pannable_leave_notify_event;
334 widget_class->scroll_event = hildon_pannable_area_scroll_cb;
336 container_class->add = hildon_pannable_area_add;
337 container_class->remove = hildon_pannable_area_remove;
338 container_class->set_focus_child = hildon_pannable_area_set_focus_child;
340 klass->horizontal_movement = NULL;
341 klass->vertical_movement = NULL;
343 g_object_class_install_property (object_class,
345 g_param_spec_boolean ("enabled",
347 "Enable or disable finger-scroll.",
352 g_object_class_install_property (object_class,
353 PROP_VSCROLLBAR_POLICY,
354 g_param_spec_enum ("vscrollbar_policy",
356 "Visual policy of the vertical scrollbar",
357 GTK_TYPE_POLICY_TYPE,
358 GTK_POLICY_AUTOMATIC,
362 g_object_class_install_property (object_class,
363 PROP_HSCROLLBAR_POLICY,
364 g_param_spec_enum ("hscrollbar_policy",
366 "Visual policy of the horizontal scrollbar",
367 GTK_TYPE_POLICY_TYPE,
368 GTK_POLICY_AUTOMATIC,
372 g_object_class_install_property (object_class,
374 g_param_spec_enum ("mode",
376 "Change the finger-scrolling mode.",
377 HILDON_TYPE_PANNABLE_AREA_MODE,
378 HILDON_PANNABLE_AREA_MODE_AUTO,
382 g_object_class_install_property (object_class,
384 g_param_spec_flags ("mov_mode",
385 "Scroll movement mode",
386 "Controls if the widget can scroll vertically, horizontally or both",
387 HILDON_TYPE_MOVEMENT_MODE,
388 HILDON_MOVEMENT_MODE_VERT,
392 g_object_class_install_property (object_class,
394 g_param_spec_double ("velocity_min",
395 "Minimum scroll velocity",
396 "Minimum distance the child widget should scroll "
397 "per 'frame', in pixels per frame.",
402 g_object_class_install_property (object_class,
404 g_param_spec_double ("velocity_max",
405 "Maximum scroll velocity",
406 "Maximum distance the child widget should scroll "
407 "per 'frame', in pixels per frame.",
412 g_object_class_install_property (object_class,
413 PROP_VEL_MAX_OVERSHOOTING,
414 g_param_spec_double ("velocity_overshooting_max",
415 "Maximum scroll velocity when overshooting",
416 "Maximum distance the child widget should scroll "
417 "per 'frame', in pixels per frame when it overshoots after hitting the edge.",
422 g_object_class_install_property (object_class,
423 PROP_VELOCITY_FAST_FACTOR,
424 g_param_spec_double ("velocity_fast_factor",
425 "Fast velocity factor",
426 "Minimum velocity that is considered 'fast': "
427 "children widgets won't receive button presses. "
428 "Expressed as a fraction of the maximum velocity.",
433 g_object_class_install_property (object_class,
435 g_param_spec_double ("deceleration",
436 "Deceleration multiplier",
437 "The multiplier used when decelerating when in "
438 "acceleration scrolling mode.",
443 g_object_class_install_property (object_class,
445 g_param_spec_double ("drag_inertia",
446 "Inertia of the cursor dragging",
447 "Percentage of the calculated speed in each moment we are are going to use"
448 "to calculate the launch speed, the other part would be the speed"
449 "calculated previously",
454 g_object_class_install_property (object_class,
456 g_param_spec_uint ("sps",
457 "Scrolls per second",
458 "Amount of scroll events to generate per second.",
463 g_object_class_install_property (object_class,
464 PROP_PANNING_THRESHOLD,
465 g_param_spec_uint ("panning_threshold",
466 "Threshold to consider a motion event an scroll",
467 "Amount of pixels to consider a motion event an scroll, if it is less"
468 "it is a click detected incorrectly by the touch screen.",
473 g_object_class_install_property (object_class,
474 PROP_SCROLLBAR_FADE_DELAY,
475 g_param_spec_uint ("scrollbar_fade_delay",
476 "Time before starting to fade the scrollbar",
477 "Time the scrollbar is going to be visible if the widget is not in"
478 "action in miliseconds",
483 g_object_class_install_property (object_class,
485 g_param_spec_uint ("bounce_steps",
487 "Number of steps that is going to be used to bounce when hitting the"
488 "edge, the rubberband effect depends on it",
493 g_object_class_install_property (object_class,
495 g_param_spec_uint ("force",
496 "Multiplier of the calculated speed",
497 "Force applied to the movement, multiplies the calculated speed of the"
498 "user movement the cursor in the screen",
503 g_object_class_install_property (object_class,
504 PROP_DIRECTION_ERROR_MARGIN,
505 g_param_spec_uint ("direction_error_margin",
506 "Margin in the direction detection",
507 "After detecting the direction of the movement (horizontal or"
508 "vertical), we can add this margin of error to allow the movement in"
509 "the other direction even apparently it is not",
514 g_object_class_install_property (object_class,
516 g_param_spec_int ("vovershoot_max",
517 "Vertical overshoot distance",
518 "Space we allow the widget to pass over its vertical limits when"
519 "hitting the edges, set 0 in order to deactivate overshooting.",
524 g_object_class_install_property (object_class,
526 g_param_spec_int ("hovershoot_max",
527 "Horizontal overshoot distance",
528 "Space we allow the widget to pass over its horizontal limits when"
529 "hitting the edges, set 0 in order to deactivate overshooting.",
534 g_object_class_install_property (object_class,
536 g_param_spec_double ("scroll_time",
537 "Time to scroll to a position",
538 "The time to scroll to a position when calling the hildon_pannable_scroll_to function",
543 g_object_class_install_property (object_class,
545 g_param_spec_boolean ("initial-hint",
547 "Whether to hint the user about the pannability of the container.",
552 g_object_class_install_property (object_class,
553 PROP_LOW_FRICTION_MODE,
554 g_param_spec_boolean ("low-friction-mode",
555 "Do not decelerate the initial velocity",
556 "Avoid decelerating the panning movement, like no friction, the widget"
557 "will stop in the edges or if the user clicks.",
562 g_object_class_install_property (object_class,
563 PROP_SIZE_REQUEST_POLICY,
564 g_param_spec_enum ("size-request-policy",
565 "Size Requisition policy",
566 "Controls the size request policy of the widget",
567 HILDON_TYPE_SIZE_REQUEST_POLICY,
568 HILDON_SIZE_REQUEST_MINIMUM,
572 g_object_class_install_property (object_class,
574 g_param_spec_object ("hadjustment",
575 "Horizontal Adjustment",
576 "The GtkAdjustment for the horizontal position",
579 g_object_class_install_property (object_class,
581 g_param_spec_object ("vadjustment",
582 "Vertical Adjustment",
583 "The GtkAdjustment for the vertical position",
587 g_object_class_install_property (object_class,
588 PROP_CENTER_ON_CHILD_FOCUS,
589 g_param_spec_boolean ("center-on-child-focus",
590 "Center on the child with the focus",
591 "Whether to center the pannable on the child that receives the focus.",
597 gtk_widget_class_install_style_property (widget_class,
600 "Width of the scroll indicators",
601 "Pixel width used to draw the scroll indicators.",
606 * HildonPannableArea::horizontal-movement:
607 * @hildonpannable: the object which received the signal
608 * @direction: the direction of the movement #HILDON_MOVEMENT_LEFT or #HILDON_MOVEMENT_RIGHT
609 * @initial_x: the x coordinate of the point where the user clicked to start the movement
610 * @initial_y: the y coordinate of the point where the user clicked to start the movement
612 * The horizontal-movement signal is emitted when the pannable area
613 * detects a horizontal movement. The detection does not mean the
614 * widget is going to move (i.e. maybe the children are smaller
615 * horizontally than the screen).
619 pannable_area_signals[HORIZONTAL_MOVEMENT] =
620 g_signal_new ("horizontal_movement",
621 G_TYPE_FROM_CLASS (object_class),
622 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
623 G_STRUCT_OFFSET (HildonPannableAreaClass, horizontal_movement),
625 _hildon_marshal_VOID__INT_DOUBLE_DOUBLE,
632 * HildonPannableArea::vertical-movement:
633 * @hildonpannable: the object which received the signal
634 * @direction: the direction of the movement #HILDON_MOVEMENT_UP or #HILDON_MOVEMENT_DOWN
635 * @initial_x: the x coordinate of the point where the user clicked to start the movement
636 * @initial_y: the y coordinate of the point where the user clicked to start the movement
638 * The vertical-movement signal is emitted when the pannable area
639 * detects a vertical movement. The detection does not mean the
640 * widget is going to move (i.e. maybe the children are smaller
641 * vertically than the screen).
645 pannable_area_signals[VERTICAL_MOVEMENT] =
646 g_signal_new ("vertical_movement",
647 G_TYPE_FROM_CLASS (object_class),
648 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
649 G_STRUCT_OFFSET (HildonPannableAreaClass, vertical_movement),
651 _hildon_marshal_VOID__INT_DOUBLE_DOUBLE,
658 * HildonPannableArea::panning-started:
659 * @hildonpannable: the pannable area object that is going to start
662 * This signal is emitted before the panning starts. Applications
663 * can return %TRUE to avoid the panning. The main difference with
664 * the vertical-movement and horizontal-movement signals is those
665 * gesture signals are launched no matter if the widget is going to
666 * move, this signal means the widget is going to start moving. It
667 * could even happen that the widget moves and there was no gesture
668 * (i.e. click meanwhile the pannable is overshooting).
670 * Returns: %TRUE to stop the panning launch. %FALSE to continue
675 pannable_area_signals[PANNING_STARTED] =
676 g_signal_new ("panning-started",
677 G_TYPE_FROM_CLASS (object_class),
681 _hildon_marshal_BOOLEAN__VOID,
685 * HildonPannableArea::panning-finished:
686 * @hildonpannable: the pannable area object that finished the
689 * This signal is emitted after the kinetic panning has
694 pannable_area_signals[PANNING_FINISHED] =
695 g_signal_new ("panning-finished",
696 G_TYPE_FROM_CLASS (object_class),
700 _hildon_marshal_VOID__VOID,
706 hildon_pannable_area_init (HildonPannableArea * area)
708 HildonPannableAreaPrivate *priv = PANNABLE_AREA_PRIVATE (area);
710 GTK_WIDGET_UNSET_FLAGS (area, GTK_NO_WINDOW);
715 priv->button_pressed = FALSE;
717 priv->last_press_time = 0;
719 priv->vscroll_visible = TRUE;
720 priv->hscroll_visible = TRUE;
721 priv->indicator_width = 6;
722 priv->overshot_dist_x = 0;
723 priv->overshot_dist_y = 0;
724 priv->overshooting_y = 0;
725 priv->overshooting_x = 0;
731 priv->scroll_indicator_alpha = 0.0;
732 priv->scroll_indicator_timeout = 0;
733 priv->motion_event_scroll_timeout = 0;
734 priv->scroll_indicator_event_interrupt = 0;
735 priv->scroll_delay_counter = 0;
736 priv->scrollbar_fade_delay = 0;
737 priv->scroll_to_x = -1;
738 priv->scroll_to_y = -1;
739 priv->first_drag = TRUE;
740 priv->initial_effect = TRUE;
741 priv->child_width = 0;
742 priv->child_height = 0;
743 priv->last_in = TRUE;
746 priv->center_on_child_focus_pending = FALSE;
748 gtk_style_lookup_color (GTK_WIDGET (area)->style,
749 "SecondaryTextColor", &priv->scroll_color);
752 GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
754 GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
756 g_object_ref_sink (G_OBJECT (priv->hadjust));
757 g_object_ref_sink (G_OBJECT (priv->vadjust));
759 g_signal_connect_swapped (priv->hadjust, "value-changed",
760 G_CALLBACK (hildon_pannable_area_adjust_value_changed), area);
761 g_signal_connect_swapped (priv->vadjust, "value-changed",
762 G_CALLBACK (hildon_pannable_area_adjust_value_changed), area);
763 g_signal_connect_swapped (priv->hadjust, "changed",
764 G_CALLBACK (hildon_pannable_area_adjust_changed), area);
765 g_signal_connect_swapped (priv->vadjust, "changed",
766 G_CALLBACK (hildon_pannable_area_adjust_changed), area);
767 g_signal_connect (area, "grab-notify",
768 G_CALLBACK (hildon_pannable_area_grab_notify), NULL);
772 hildon_pannable_area_get_property (GObject * object,
777 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (object)->priv;
779 switch (property_id) {
781 g_value_set_boolean (value, priv->enabled);
784 g_value_set_enum (value, priv->mode);
786 case PROP_MOVEMENT_MODE:
787 g_value_set_flags (value, priv->mov_mode);
789 case PROP_VELOCITY_MIN:
790 g_value_set_double (value, priv->vmin);
792 case PROP_VELOCITY_MAX:
793 g_value_set_double (value, priv->vmax);
795 case PROP_VEL_MAX_OVERSHOOTING:
796 g_value_set_double (value, priv->vmax_overshooting);
798 case PROP_VELOCITY_FAST_FACTOR:
799 g_value_set_double (value, priv->vfast_factor);
801 case PROP_DECELERATION:
802 g_value_set_double (value, priv->decel);
804 case PROP_DRAG_INERTIA:
805 g_value_set_double (value, priv->drag_inertia);
808 g_value_set_uint (value, priv->sps);
810 case PROP_PANNING_THRESHOLD:
811 g_value_set_uint (value, priv->panning_threshold);
813 case PROP_SCROLLBAR_FADE_DELAY:
814 /* convert to miliseconds */
815 g_value_set_uint (value, priv->scrollbar_fade_delay * SCROLL_FADE_TIMEOUT);
817 case PROP_BOUNCE_STEPS:
818 g_value_set_uint (value, priv->bounce_steps);
821 g_value_set_uint (value, priv->force);
823 case PROP_DIRECTION_ERROR_MARGIN:
824 g_value_set_uint (value, priv->direction_error_margin);
826 case PROP_VSCROLLBAR_POLICY:
827 g_value_set_enum (value, priv->vscrollbar_policy);
829 case PROP_HSCROLLBAR_POLICY:
830 g_value_set_enum (value, priv->hscrollbar_policy);
832 case PROP_VOVERSHOOT_MAX:
833 g_value_set_int (value, priv->vovershoot_max);
835 case PROP_HOVERSHOOT_MAX:
836 g_value_set_int (value, priv->hovershoot_max);
838 case PROP_SCROLL_TIME:
839 g_value_set_double (value, priv->scroll_time);
841 case PROP_INITIAL_HINT:
842 g_value_set_boolean (value, priv->initial_hint);
844 case PROP_LOW_FRICTION_MODE:
845 g_value_set_boolean (value, priv->low_friction_mode);
847 case PROP_SIZE_REQUEST_POLICY:
848 g_value_set_enum (value, priv->size_request_policy);
850 case PROP_HADJUSTMENT:
851 g_value_set_object (value,
852 hildon_pannable_area_get_hadjustment
853 (HILDON_PANNABLE_AREA (object)));
855 case PROP_VADJUSTMENT:
856 g_value_set_object (value,
857 hildon_pannable_area_get_vadjustment
858 (HILDON_PANNABLE_AREA (object)));
860 case PROP_CENTER_ON_CHILD_FOCUS:
861 g_value_set_boolean (value, priv->center_on_child_focus);
864 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
869 hildon_pannable_area_set_property (GObject * object,
871 const GValue * value,
874 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (object)->priv;
877 switch (property_id) {
879 enabled = g_value_get_boolean (value);
881 if ((priv->enabled != enabled) && (GTK_WIDGET_REALIZED (object))) {
883 gdk_window_raise (priv->event_window);
885 gdk_window_lower (priv->event_window);
888 priv->enabled = enabled;
891 priv->mode = g_value_get_enum (value);
893 case PROP_MOVEMENT_MODE:
894 priv->mov_mode = g_value_get_flags (value);
896 case PROP_VELOCITY_MIN:
897 priv->vmin = g_value_get_double (value);
899 case PROP_VELOCITY_MAX:
900 priv->vmax = g_value_get_double (value);
902 case PROP_VEL_MAX_OVERSHOOTING:
903 priv->vmax_overshooting = g_value_get_double (value);
905 case PROP_VELOCITY_FAST_FACTOR:
906 priv->vfast_factor = g_value_get_double (value);
908 case PROP_DECELERATION:
909 priv->decel = g_value_get_double (value);
910 hildon_pannable_calculate_vel_factor (HILDON_PANNABLE_AREA (object));
912 case PROP_DRAG_INERTIA:
913 priv->drag_inertia = g_value_get_double (value);
916 priv->sps = g_value_get_uint (value);
918 case PROP_PANNING_THRESHOLD:
920 GtkSettings *settings = gtk_settings_get_default ();
921 GtkSettingsValue svalue = { NULL, { 0, }, };
923 priv->panning_threshold = g_value_get_uint (value);
925 /* insure gtk dnd is the same we are using, not allowed
926 different thresholds in the same application */
927 svalue.origin = "panning_threshold";
928 g_value_init (&svalue.value, G_TYPE_LONG);
929 g_value_set_long (&svalue.value, priv->panning_threshold);
930 gtk_settings_set_property_value (settings, "gtk-dnd-drag-threshold", &svalue);
931 g_value_unset (&svalue.value);
934 case PROP_SCROLLBAR_FADE_DELAY:
935 /* convert to miliseconds */
936 priv->scrollbar_fade_delay = g_value_get_uint (value)/(SCROLL_FADE_TIMEOUT);
938 case PROP_BOUNCE_STEPS:
939 priv->bounce_steps = g_value_get_uint (value);
942 priv->force = g_value_get_uint (value);
944 case PROP_DIRECTION_ERROR_MARGIN:
945 priv->direction_error_margin = g_value_get_uint (value);
947 case PROP_VSCROLLBAR_POLICY:
948 priv->vscrollbar_policy = g_value_get_enum (value);
950 gtk_widget_queue_resize (GTK_WIDGET (object));
952 case PROP_HSCROLLBAR_POLICY:
953 priv->hscrollbar_policy = g_value_get_enum (value);
955 gtk_widget_queue_resize (GTK_WIDGET (object));
957 case PROP_VOVERSHOOT_MAX:
958 priv->vovershoot_max = g_value_get_int (value);
960 case PROP_HOVERSHOOT_MAX:
961 priv->hovershoot_max = g_value_get_int (value);
963 case PROP_SCROLL_TIME:
964 priv->scroll_time = g_value_get_double (value);
966 hildon_pannable_calculate_vel_factor (HILDON_PANNABLE_AREA (object));
968 case PROP_INITIAL_HINT:
969 priv->initial_hint = g_value_get_boolean (value);
971 case PROP_LOW_FRICTION_MODE:
972 priv->low_friction_mode = g_value_get_boolean (value);
974 case PROP_SIZE_REQUEST_POLICY:
975 hildon_pannable_area_set_size_request_policy (HILDON_PANNABLE_AREA (object),
976 g_value_get_enum (value));
978 case PROP_CENTER_ON_CHILD_FOCUS:
979 priv->center_on_child_focus = g_value_get_boolean (value);
983 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
988 hildon_pannable_area_dispose (GObject * object)
990 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (object)->priv;
991 GtkWidget *child = gtk_bin_get_child (GTK_BIN (object));
993 hildon_pannable_area_remove_timeouts (GTK_WIDGET (object));
996 g_signal_handlers_disconnect_by_func (child,
997 hildon_pannable_area_child_mapped,
1001 g_signal_handlers_disconnect_by_func (object,
1002 hildon_pannable_area_grab_notify,
1005 if (priv->hadjust) {
1006 g_signal_handlers_disconnect_by_func (priv->hadjust,
1007 hildon_pannable_area_adjust_value_changed,
1009 g_signal_handlers_disconnect_by_func (priv->hadjust,
1010 hildon_pannable_area_adjust_changed,
1012 g_object_unref (priv->hadjust);
1013 priv->hadjust = NULL;
1016 if (priv->vadjust) {
1017 g_signal_handlers_disconnect_by_func (priv->vadjust,
1018 hildon_pannable_area_adjust_value_changed,
1020 g_signal_handlers_disconnect_by_func (priv->vadjust,
1021 hildon_pannable_area_adjust_changed,
1023 g_object_unref (priv->vadjust);
1024 priv->vadjust = NULL;
1027 if (G_OBJECT_CLASS (hildon_pannable_area_parent_class)->dispose)
1028 G_OBJECT_CLASS (hildon_pannable_area_parent_class)->dispose (object);
1032 hildon_pannable_area_realize (GtkWidget * widget)
1034 GdkWindowAttr attributes;
1035 gint attributes_mask;
1037 HildonPannableAreaPrivate *priv;
1039 priv = HILDON_PANNABLE_AREA (widget)->priv;
1041 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
1043 border_width = GTK_CONTAINER (widget)->border_width;
1045 attributes.x = widget->allocation.x + border_width;
1046 attributes.y = widget->allocation.y + border_width;
1047 attributes.width = MAX (widget->allocation.width - 2 * border_width, 0);
1048 attributes.height = MAX (widget->allocation.height - 2 * border_width, 0);
1049 attributes.window_type = GDK_WINDOW_CHILD;
1051 /* avoid using the hildon_window */
1052 attributes.visual = gtk_widget_get_visual (widget);
1053 attributes.colormap = gtk_widget_get_colormap (widget);
1054 attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK;
1055 attributes.wclass = GDK_INPUT_OUTPUT;
1057 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1059 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
1060 &attributes, attributes_mask);
1061 gdk_window_set_user_data (widget->window, widget);
1063 /* create the events window */
1066 attributes.event_mask = gtk_widget_get_events (widget)
1067 | GDK_BUTTON_MOTION_MASK
1068 | GDK_BUTTON_PRESS_MASK
1069 | GDK_BUTTON_RELEASE_MASK
1071 | GDK_POINTER_MOTION_HINT_MASK
1072 | GDK_EXPOSURE_MASK | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK;
1073 attributes.wclass = GDK_INPUT_ONLY;
1075 attributes_mask = GDK_WA_X | GDK_WA_Y;
1077 priv->event_window = gdk_window_new (widget->window,
1078 &attributes, attributes_mask);
1079 gdk_window_set_user_data (priv->event_window, widget);
1081 widget->style = gtk_style_attach (widget->style, widget->window);
1082 gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
1084 priv->scrollbars_gc = gdk_gc_new (GDK_DRAWABLE (widget->window));
1085 gdk_gc_copy (priv->scrollbars_gc, widget->style->fg_gc[GTK_STATE_INSENSITIVE]);
1090 hildon_pannable_area_remove_timeouts (GtkWidget * widget)
1092 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1094 if (priv->idle_id) {
1095 g_signal_emit (widget, pannable_area_signals[PANNING_FINISHED], 0);
1096 g_source_remove (priv->idle_id);
1100 if (priv->scroll_indicator_timeout){
1101 g_source_remove (priv->scroll_indicator_timeout);
1102 priv->scroll_indicator_timeout = 0;
1105 if (priv->motion_event_scroll_timeout){
1106 g_source_remove (priv->motion_event_scroll_timeout);
1107 priv->motion_event_scroll_timeout = 0;
1112 hildon_pannable_area_unrealize (GtkWidget * widget)
1114 HildonPannableAreaPrivate *priv;
1116 priv = HILDON_PANNABLE_AREA (widget)->priv;
1118 hildon_pannable_area_remove_timeouts (widget);
1120 if (priv->event_window != NULL) {
1121 gdk_window_set_user_data (priv->event_window, NULL);
1122 gdk_window_destroy (priv->event_window);
1123 priv->event_window = NULL;
1126 gdk_gc_unref (priv->scrollbars_gc);
1128 if (GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->unrealize)
1129 (*GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->unrealize)(widget);
1133 hildon_pannable_area_size_request (GtkWidget * widget,
1134 GtkRequisition * requisition)
1136 GtkRequisition child_requisition = {0};
1137 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1138 GtkWidget *child = gtk_bin_get_child (GTK_BIN (widget));
1140 if (child && GTK_WIDGET_VISIBLE (child))
1142 gtk_widget_size_request (child, &child_requisition);
1145 if (priv->hscrollbar_policy == GTK_POLICY_NEVER) {
1146 requisition->width = child_requisition.width;
1148 switch (priv->size_request_policy) {
1149 case HILDON_SIZE_REQUEST_CHILDREN:
1150 requisition->width = MIN (PANNABLE_MAX_WIDTH,
1151 child_requisition.width);
1153 case HILDON_SIZE_REQUEST_MINIMUM:
1155 requisition->width = priv->indicator_width;
1159 if (priv->vscrollbar_policy == GTK_POLICY_NEVER) {
1160 requisition->height = child_requisition.height;
1162 switch (priv->size_request_policy) {
1163 case HILDON_SIZE_REQUEST_CHILDREN:
1164 requisition->height = MIN (PANNABLE_MAX_HEIGHT,
1165 child_requisition.height);
1167 case HILDON_SIZE_REQUEST_MINIMUM:
1169 requisition->height = priv->indicator_width;
1173 requisition->width += 2 * GTK_CONTAINER (widget)->border_width;
1174 requisition->height += 2 * GTK_CONTAINER (widget)->border_width;
1178 hildon_pannable_area_child_allocate_calculate (GtkWidget * widget,
1179 GtkAllocation * allocation,
1180 GtkAllocation * child_allocation)
1183 HildonPannableAreaPrivate *priv;
1185 border_width = GTK_CONTAINER (widget)->border_width;
1187 priv = HILDON_PANNABLE_AREA (widget)->priv;
1189 child_allocation->x = 0;
1190 child_allocation->y = 0;
1191 child_allocation->width = MAX (allocation->width - 2 * border_width -
1192 (priv->vscroll_visible ? priv->vscroll_rect.width : 0), 0);
1193 child_allocation->height = MAX (allocation->height - 2 * border_width -
1194 (priv->hscroll_visible ? priv->hscroll_rect.height : 0), 0);
1196 if (priv->overshot_dist_y > 0) {
1197 child_allocation->y = MIN (child_allocation->y + priv->overshot_dist_y,
1198 child_allocation->height);
1199 child_allocation->height = MAX (child_allocation->height - priv->overshot_dist_y, 0);
1200 } else if (priv->overshot_dist_y < 0) {
1201 child_allocation->height = MAX (child_allocation->height + priv->overshot_dist_y, 0);
1204 if (priv->overshot_dist_x > 0) {
1205 child_allocation->x = MIN (child_allocation->x + priv->overshot_dist_x,
1206 child_allocation->width);
1207 child_allocation->width = MAX (child_allocation->width - priv->overshot_dist_x, 0);
1208 } else if (priv->overshot_dist_x < 0) {
1209 child_allocation->width = MAX (child_allocation->width + priv->overshot_dist_x, 0);
1214 hildon_pannable_area_size_allocate (GtkWidget * widget,
1215 GtkAllocation * allocation)
1217 GtkAllocation child_allocation;
1218 HildonPannableAreaPrivate *priv;
1219 GtkWidget *child = gtk_bin_get_child (GTK_BIN (widget));
1223 border_width = GTK_CONTAINER (widget)->border_width;
1225 widget->allocation = *allocation;
1227 priv = HILDON_PANNABLE_AREA (widget)->priv;
1229 if (GTK_WIDGET_REALIZED (widget)) {
1230 gdk_window_move_resize (widget->window,
1231 allocation->x + border_width,
1232 allocation->y + border_width,
1233 allocation->width - border_width * 2,
1234 allocation->height - border_width * 2);
1235 gdk_window_move_resize (priv->event_window,
1238 allocation->width - border_width * 2,
1239 allocation->height - border_width * 2);
1242 if (child && GTK_WIDGET_VISIBLE (child)) {
1244 hildon_pannable_area_check_scrollbars (HILDON_PANNABLE_AREA (widget));
1246 hildon_pannable_area_child_allocate_calculate (widget,
1250 gtk_widget_size_allocate (child, &child_allocation);
1252 if (hildon_pannable_area_check_scrollbars (HILDON_PANNABLE_AREA (widget))) {
1253 hildon_pannable_area_child_allocate_calculate (widget,
1257 gtk_widget_size_allocate (child, &child_allocation);
1260 hv = priv->hadjust->value;
1261 vv = priv->vadjust->value;
1263 /* we have to do this after child size_allocate because page_size is
1264 * changed when we allocate the size of the children */
1265 if (priv->overshot_dist_y < 0) {
1266 priv->vadjust->value = priv->vadjust->upper - priv->vadjust->page_size;
1269 if (priv->overshot_dist_x < 0) {
1270 priv->hadjust->value = priv->hadjust->upper - priv->hadjust->page_size;
1273 if (hv != priv->hadjust->value)
1274 gtk_adjustment_value_changed (priv->hadjust);
1276 if (vv != priv->vadjust->value)
1277 gtk_adjustment_value_changed (priv->vadjust);
1280 hildon_pannable_area_check_scrollbars (HILDON_PANNABLE_AREA (widget));
1285 hildon_pannable_area_style_set (GtkWidget * widget,
1286 GtkStyle * previous_style)
1288 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1290 GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->
1291 style_set (widget, previous_style);
1293 gtk_style_lookup_color (widget->style, "SecondaryTextColor", &priv->scroll_color);
1294 gtk_widget_style_get (widget, "indicator-width", &priv->indicator_width, NULL);
1298 hildon_pannable_area_map (GtkWidget * widget)
1300 HildonPannableAreaPrivate *priv;
1302 priv = HILDON_PANNABLE_AREA (widget)->priv;
1304 gdk_window_show (widget->window);
1306 if (priv->event_window != NULL && !priv->enabled)
1307 gdk_window_show (priv->event_window);
1309 (*GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->map) (widget);
1311 if (priv->event_window != NULL && priv->enabled)
1312 gdk_window_show (priv->event_window);
1316 hildon_pannable_area_unmap (GtkWidget * widget)
1318 HildonPannableAreaPrivate *priv;
1320 priv = HILDON_PANNABLE_AREA (widget)->priv;
1322 if (priv->event_window != NULL)
1323 gdk_window_hide (priv->event_window);
1325 gdk_window_hide (widget->window);
1327 (*GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->unmap) (widget);
1331 hildon_pannable_area_grab_notify (GtkWidget *widget,
1332 gboolean was_grabbed,
1335 /* an internal widget has grabbed the focus and now has returned it,
1336 we have to do some release actions */
1338 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1340 priv->scroll_indicator_event_interrupt = 0;
1342 if ((!priv->scroll_indicator_timeout)&&(priv->scroll_indicator_alpha)>0.1) {
1343 priv->scroll_delay_counter = priv->scrollbar_fade_delay;
1345 hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget),
1346 priv->scroll_indicator_alpha);
1349 priv->last_type = 3;
1350 priv->moved = FALSE;
1354 #if USE_CAIRO_SCROLLBARS == 1
1357 rgb_from_gdkcolor (GdkColor *color, gdouble *r, gdouble *g, gdouble *b)
1359 *r = (color->red >> 8) / 255.0;
1360 *g = (color->green >> 8) / 255.0;
1361 *b = (color->blue >> 8) / 255.0;
1365 hildon_pannable_draw_vscroll (GtkWidget * widget,
1366 GdkColor *back_color,
1367 GdkColor *scroll_color)
1369 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1372 cairo_pattern_t *pattern;
1374 gint radius = (priv->vscroll_rect.width/2) - 1;
1376 cr = gdk_cairo_create(widget->window);
1378 /* Draw the background */
1379 rgb_from_gdkcolor (back_color, &r, &g, &b);
1380 cairo_set_source_rgb (cr, r, g, b);
1381 cairo_rectangle(cr, priv->vscroll_rect.x, priv->vscroll_rect.y,
1382 priv->vscroll_rect.width,
1383 priv->vscroll_rect.height);
1384 cairo_fill_preserve (cr);
1387 /* Calculate the scroll bar height and position */
1388 y = ((priv->vadjust->value - priv->vadjust->lower) / (priv->vadjust->upper - priv->vadjust->lower)) *
1389 (widget->allocation.height -
1390 (priv->hscroll_visible ? priv->indicator_width : 0));
1391 height = ((((priv->vadjust->value - priv->vadjust->lower) +
1392 priv->vadjust->page_size) /
1393 (priv->vadjust->upper - priv->vadjust->lower)) *
1394 (widget->allocation.height -
1395 (priv->hscroll_visible ? priv->indicator_width : 0))) - y;
1397 /* Set a minimum height */
1398 height = MAX (SCROLL_BAR_MIN_SIZE, height);
1400 /* Check the max y position */
1401 y = MIN (y, widget->allocation.height -
1402 (priv->hscroll_visible ? priv->hscroll_rect.height : 0) -
1405 /* Draw the scrollbar */
1406 rgb_from_gdkcolor (scroll_color, &r, &g, &b);
1408 pattern = cairo_pattern_create_linear(radius+1, y, radius+1,y + height);
1409 cairo_pattern_add_color_stop_rgb(pattern, 0, r, g, b);
1410 cairo_pattern_add_color_stop_rgb(pattern, 1, r/2, g/2, b/2);
1411 cairo_set_source(cr, pattern);
1413 cairo_pattern_destroy(pattern);
1415 cairo_arc(cr, priv->vscroll_rect.x + radius + 1, y + radius + 1, radius, G_PI, 0);
1416 cairo_line_to(cr, priv->vscroll_rect.x + (radius * 2) + 1, y + height - radius);
1417 cairo_arc(cr, priv->vscroll_rect.x + radius + 1, y + height - radius, radius, 0, G_PI);
1418 cairo_line_to(cr, priv->vscroll_rect.x + 1, y + height - radius);
1421 cairo_paint_with_alpha(cr, priv->scroll_indicator_alpha);
1427 hildon_pannable_draw_hscroll (GtkWidget * widget,
1428 GdkColor *back_color,
1429 GdkColor *scroll_color)
1431 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1434 cairo_pattern_t *pattern;
1436 gint radius = (priv->hscroll_rect.height/2) - 1;
1438 cr = gdk_cairo_create(widget->window);
1440 /* Draw the background */
1441 rgb_from_gdkcolor (back_color, &r, &g, &b);
1442 cairo_set_source_rgb (cr, r, g, b);
1443 cairo_rectangle(cr, priv->hscroll_rect.x, priv->hscroll_rect.y,
1444 priv->hscroll_rect.width,
1445 priv->hscroll_rect.height);
1446 cairo_fill_preserve (cr);
1449 /* calculate the scrollbar width and position */
1450 x = ((priv->hadjust->value - priv->hadjust->lower) / (priv->hadjust->upper - priv->hadjust->lower)) *
1451 (widget->allocation.width - (priv->vscroll_visible ? priv->indicator_width : 0));
1452 width =((((priv->hadjust->value - priv->hadjust->lower) +
1453 priv->hadjust->page_size) / (priv->hadjust->upper - priv->hadjust->lower)) *
1454 (widget->allocation.width -
1455 (priv->vscroll_visible ? priv->indicator_width : 0))) - x;
1457 /* Set a minimum width */
1458 width = MAX (SCROLL_BAR_MIN_SIZE, width);
1460 /* Check the max x position */
1461 x = MIN (x, widget->allocation.width -
1462 (priv->vscroll_visible ? priv->vscroll_rect.width : 0) -
1465 /* Draw the scrollbar */
1466 rgb_from_gdkcolor (scroll_color, &r, &g, &b);
1468 pattern = cairo_pattern_create_linear(x, radius+1, x+width, radius+1);
1469 cairo_pattern_add_color_stop_rgb(pattern, 0, r, g, b);
1470 cairo_pattern_add_color_stop_rgb(pattern, 1, r/2, g/2, b/2);
1471 cairo_set_source(cr, pattern);
1473 cairo_pattern_destroy(pattern);
1475 cairo_arc_negative(cr, x + radius + 1, priv->hscroll_rect.y + radius + 1, radius, 3*G_PI_2, G_PI_2);
1476 cairo_line_to(cr, x + width - radius, priv->hscroll_rect.y + (radius * 2) + 1);
1477 cairo_arc_negative(cr, x + width - radius, priv->hscroll_rect.y + radius + 1, radius, G_PI_2, 3*G_PI_2);
1478 cairo_line_to(cr, x + width - radius, priv->hscroll_rect.y + 1);
1481 cairo_paint_with_alpha(cr, priv->scroll_indicator_alpha);
1486 #else /* USE_CAIRO_SCROLLBARS */
1489 tranparency_color (GdkColor *color,
1492 gdouble transparency)
1496 diff = colora.red - colorb.red;
1497 color->red = colora.red-diff*transparency;
1499 diff = colora.green - colorb.green;
1500 color->green = colora.green-diff*transparency;
1502 diff = colora.blue - colorb.blue;
1503 color->blue = colora.blue-diff*transparency;
1507 hildon_pannable_draw_vscroll (GtkWidget *widget,
1508 GdkColor *back_color,
1509 GdkColor *scroll_color)
1511 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1513 GdkColor transp_color;
1514 GdkGC *gc = priv->scrollbars_gc;
1516 gdk_draw_rectangle (widget->window,
1517 widget->style->bg_gc[GTK_STATE_NORMAL],
1519 priv->vscroll_rect.x, priv->vscroll_rect.y,
1520 priv->vscroll_rect.width,
1521 priv->vscroll_rect.height);
1523 y = ((priv->vadjust->value - priv->vadjust->lower) / (priv->vadjust->upper - priv->vadjust->lower)) *
1524 (widget->allocation.height - (priv->hscroll_visible ? priv->indicator_width : 0));
1525 height = ((((priv->vadjust->value - priv->vadjust->lower) + priv->vadjust->page_size) /
1526 (priv->vadjust->upper - priv->vadjust->lower)) *
1527 (widget->allocation.height -
1528 (priv->hscroll_visible ? priv->indicator_width : 0))) - y;
1530 /* Set a minimum height */
1531 height = MAX (SCROLL_BAR_MIN_SIZE, height);
1533 /* Check the max y position */
1534 y = MIN (y, widget->allocation.height -
1535 (priv->hscroll_visible ? priv->hscroll_rect.height : 0) -
1538 if (priv->scroll_indicator_alpha == 1.0) {
1539 transp_color = priv->scroll_color;
1540 } else if (priv->scroll_indicator_alpha < 1.0) {
1541 tranparency_color (&transp_color, *back_color, *scroll_color,
1542 priv->scroll_indicator_alpha);
1544 gdk_gc_set_rgb_fg_color (gc, &transp_color);
1546 gdk_draw_rectangle (widget->window, gc,
1547 TRUE, priv->vscroll_rect.x, y,
1548 priv->vscroll_rect.width, height);
1552 hildon_pannable_draw_hscroll (GtkWidget *widget,
1553 GdkColor *back_color,
1554 GdkColor *scroll_color)
1556 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1558 GdkColor transp_color;
1559 GdkGC *gc = priv->scrollbars_gc;
1561 gdk_draw_rectangle (widget->window,
1562 widget->style->bg_gc[GTK_STATE_INSENSITIVE],
1564 priv->hscroll_rect.x, priv->hscroll_rect.y,
1565 priv->hscroll_rect.width,
1566 priv->hscroll_rect.height);
1568 /* calculate the scrollbar width and position */
1569 x = ((priv->hadjust->value - priv->hadjust->lower) / (priv->hadjust->upper - priv->hadjust->lower)) *
1570 (widget->allocation.width - (priv->vscroll_visible ? priv->indicator_width : 0));
1571 width =((((priv->hadjust->value - priv->hadjust->lower) +
1572 priv->hadjust->page_size) / (priv->hadjust->upper - priv->hadjust->lower)) *
1573 (widget->allocation.width -
1574 (priv->vscroll_visible ? priv->indicator_width : 0))) - x;
1576 /* Set a minimum width */
1577 width = MAX (SCROLL_BAR_MIN_SIZE, width);
1579 /* Check the max x position */
1580 x = MIN (x, widget->allocation.width -
1581 (priv->vscroll_visible ? priv->vscroll_rect.width : 0) -
1584 if (priv->scroll_indicator_alpha == 1.0) {
1585 transp_color = priv->scroll_color;
1586 } else if (priv->scroll_indicator_alpha < 1.0) {
1587 tranparency_color (&transp_color, *back_color, *scroll_color,
1588 priv->scroll_indicator_alpha);
1590 gdk_gc_set_rgb_fg_color (gc, &transp_color);
1592 gdk_draw_rectangle (widget->window, gc,
1593 TRUE, x, priv->hscroll_rect.y, width,
1594 priv->hscroll_rect.height);
1597 #endif /* USE_CAIRO_SCROLLBARS */
1600 hildon_pannable_area_initial_effect (GtkWidget * widget)
1602 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1604 if (priv->initial_hint) {
1605 if (priv->vscroll_visible || priv->hscroll_visible) {
1607 priv->scroll_indicator_event_interrupt = 0;
1608 priv->scroll_delay_counter = priv->scrollbar_fade_delay;
1610 hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget), 1.0);
1616 hildon_pannable_area_launch_fade_timeout (HildonPannableArea * area,
1619 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (area)->priv;
1621 priv->scroll_indicator_alpha = alpha;
1623 if (!priv->scroll_indicator_timeout)
1624 priv->scroll_indicator_timeout =
1625 gdk_threads_add_timeout_full (G_PRIORITY_HIGH_IDLE + 20,
1626 SCROLL_FADE_TIMEOUT,
1627 (GSourceFunc) hildon_pannable_area_scroll_indicator_fade,
1633 hildon_pannable_area_adjust_changed (HildonPannableArea * area,
1636 if (GTK_WIDGET_REALIZED (area))
1637 hildon_pannable_area_refresh (area);
1641 hildon_pannable_area_adjust_value_changed (HildonPannableArea * area,
1644 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (area)->priv;
1646 gint x = priv->x_offset;
1647 gint y = priv->y_offset;
1649 priv->x_offset = priv->hadjust->value;
1650 xdiff = x - priv->x_offset;
1651 priv->y_offset = priv->vadjust->value;
1652 ydiff = y - priv->y_offset;
1654 if ((xdiff || ydiff) && GTK_WIDGET_DRAWABLE (area)) {
1655 hildon_pannable_area_redraw (area);
1657 if ((priv->vscroll_visible) || (priv->hscroll_visible)) {
1658 priv->scroll_indicator_event_interrupt = 0;
1659 priv->scroll_delay_counter = priv->scrollbar_fade_delay;
1661 hildon_pannable_area_launch_fade_timeout (area, 1.0);
1667 hildon_pannable_area_redraw (HildonPannableArea * area)
1669 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (area)->priv;
1671 /* Redraw scroll indicators */
1672 if (GTK_WIDGET_DRAWABLE (area)) {
1673 if (priv->hscroll_visible) {
1674 gdk_window_invalidate_rect (GTK_WIDGET (area)->window,
1675 &priv->hscroll_rect, FALSE);
1678 if (priv->vscroll_visible) {
1679 gdk_window_invalidate_rect (GTK_WIDGET (area)->window,
1680 &priv->vscroll_rect, FALSE);
1686 hildon_pannable_area_scroll_indicator_fade(HildonPannableArea * area)
1688 HildonPannableAreaPrivate *priv = area->priv;
1690 /* if moving do not fade out */
1691 if (((ABS (priv->vel_y)>priv->vmin)||
1692 (ABS (priv->vel_x)>priv->vmin))&&(!priv->button_pressed)) {
1697 if (priv->scroll_indicator_event_interrupt) {
1698 /* Stop a fade out, and fade back in */
1699 if (priv->scroll_indicator_alpha > 0.9) {
1700 priv->scroll_indicator_alpha = 1.0;
1701 priv->scroll_indicator_timeout = 0;
1705 priv->scroll_indicator_alpha += 0.2;
1706 hildon_pannable_area_redraw (area);
1712 if ((priv->scroll_indicator_alpha > 0.9) &&
1713 (priv->scroll_delay_counter > 0)) {
1714 priv->scroll_delay_counter--;
1719 if (!priv->scroll_indicator_event_interrupt) {
1720 /* Continue fade out */
1721 if (priv->scroll_indicator_alpha < 0.1) {
1722 priv->scroll_indicator_timeout = 0;
1723 priv->scroll_indicator_alpha = 0.0;
1727 priv->scroll_indicator_alpha -= 0.2;
1728 hildon_pannable_area_redraw (area);
1738 hildon_pannable_area_expose_event (GtkWidget * widget,
1739 GdkEventExpose * event)
1742 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1743 #if USE_CAIRO_SCROLLBARS == 1
1744 GdkColor back_color = widget->style->bg[GTK_STATE_NORMAL];
1745 GdkColor scroll_color = widget->style->base[GTK_STATE_SELECTED];
1746 #else /* USE_CAIRO_SCROLLBARS */
1747 GdkColor back_color = widget->style->bg[GTK_STATE_NORMAL];
1748 GdkColor scroll_color = priv->scroll_color;
1751 if (G_UNLIKELY (priv->initial_effect)) {
1752 hildon_pannable_area_initial_effect (widget);
1754 priv->initial_effect = FALSE;
1757 if (gtk_bin_get_child (GTK_BIN (widget))) {
1759 if (priv->scroll_indicator_alpha > 0.1) {
1760 if (priv->vscroll_visible) {
1761 hildon_pannable_draw_vscroll (widget, &back_color, &scroll_color);
1763 if (priv->hscroll_visible) {
1764 hildon_pannable_draw_hscroll (widget, &back_color, &scroll_color);
1768 /* draw overshooting rectangles */
1769 if (priv->overshot_dist_y > 0) {
1770 gint overshot_height;
1772 overshot_height = MIN (priv->overshot_dist_y, widget->allocation.height -
1773 (priv->hscroll_visible ? priv->hscroll_rect.height : 0));
1775 gdk_draw_rectangle (widget->window,
1776 widget->style->bg_gc[GTK_STATE_NORMAL],
1780 widget->allocation.width -
1781 (priv->vscroll_visible ? priv->vscroll_rect.width : 0),
1783 } else if (priv->overshot_dist_y < 0) {
1784 gint overshot_height;
1788 MAX (priv->overshot_dist_y,
1789 -(widget->allocation.height -
1790 (priv->hscroll_visible ? priv->hscroll_rect.height : 0)));
1792 overshot_y = MAX (widget->allocation.height +
1794 (priv->hscroll_visible ? priv->hscroll_rect.height : 0), 0);
1796 gdk_draw_rectangle (widget->window,
1797 widget->style->bg_gc[GTK_STATE_NORMAL],
1801 widget->allocation.width -
1802 priv->vscroll_rect.width,
1806 if (priv->overshot_dist_x > 0) {
1807 gint overshot_width;
1809 overshot_width = MIN (priv->overshot_dist_x, widget->allocation.width -
1810 (priv->vscroll_visible ? priv->vscroll_rect.width : 0));
1812 gdk_draw_rectangle (widget->window,
1813 widget->style->bg_gc[GTK_STATE_NORMAL],
1818 widget->allocation.height -
1819 (priv->hscroll_visible ? priv->hscroll_rect.height : 0));
1820 } else if (priv->overshot_dist_x < 0) {
1821 gint overshot_width;
1825 MAX (priv->overshot_dist_x,
1826 -(widget->allocation.width -
1827 (priv->vscroll_visible ? priv->vscroll_rect.width : 0)));
1829 overshot_x = MAX (widget->allocation.width +
1831 (priv->vscroll_visible ? priv->vscroll_rect.width : 0), 0);
1833 gdk_draw_rectangle (widget->window,
1834 widget->style->bg_gc[GTK_STATE_NORMAL],
1839 widget->allocation.height -
1840 priv->hscroll_rect.height);
1845 return GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->expose_event (widget, event);
1849 hildon_pannable_area_get_topmost (GdkWindow * window,
1851 gint * tx, gint * ty,
1854 /* Find the GdkWindow at the given point, by recursing from a given
1855 * parent GdkWindow. Optionally return the co-ordinates transformed
1856 * relative to the child window.
1859 GList *c, *children;
1860 GdkWindow *selected_window = NULL;
1862 gdk_drawable_get_size (GDK_DRAWABLE (window), &width, &height);
1863 if ((x < 0) || (x >= width) || (y < 0) || (y >= height))
1866 children = gdk_window_peek_children (window);
1873 selected_window = window;
1876 for (c = children; c; c = c->next) {
1877 GdkWindow *child = (GdkWindow *) c->data;
1880 gdk_drawable_get_size (GDK_DRAWABLE (child), &width, &height);
1881 gdk_window_get_position (child, &wx, &wy);
1883 if ((x >= wx) && (x < (wx + width)) && (y >= wy) && (y < (wy + height)) &&
1884 (gdk_window_is_visible (child))) {
1886 if (gdk_window_peek_children (child)) {
1887 selected_window = hildon_pannable_area_get_topmost (child, x-wx, y-wy,
1889 if (!selected_window) {
1894 selected_window = child;
1897 if ((gdk_window_get_events (child)&mask)) {
1902 selected_window = child;
1908 return selected_window;
1912 synth_crossing (GdkWindow * child,
1914 gint x_root, gint y_root,
1915 guint32 time, gboolean in)
1917 GdkEventCrossing *crossing_event;
1918 GdkEventType type = in ? GDK_ENTER_NOTIFY : GDK_LEAVE_NOTIFY;
1920 /* Send synthetic enter event */
1921 crossing_event = (GdkEventCrossing *) gdk_event_new (type);
1922 ((GdkEventAny *) crossing_event)->type = type;
1923 ((GdkEventAny *) crossing_event)->window = g_object_ref (child);
1924 ((GdkEventAny *) crossing_event)->send_event = FALSE;
1925 crossing_event->subwindow = g_object_ref (child);
1926 crossing_event->time = time;
1927 crossing_event->x = x;
1928 crossing_event->y = y;
1929 crossing_event->x_root = x_root;
1930 crossing_event->y_root = y_root;
1931 crossing_event->mode = GDK_CROSSING_NORMAL;
1932 crossing_event->detail = GDK_NOTIFY_UNKNOWN;
1933 crossing_event->focus = FALSE;
1934 crossing_event->state = 0;
1935 gdk_event_put ((GdkEvent *) crossing_event);
1936 gdk_event_free ((GdkEvent *) crossing_event);
1940 hildon_pannable_area_button_press_cb (GtkWidget * widget,
1941 GdkEventButton * event)
1944 HildonPannableArea *area = HILDON_PANNABLE_AREA (widget);
1945 HildonPannableAreaPrivate *priv = area->priv;
1947 if ((!priv->enabled) || (event->button != 1) ||
1948 ((event->time == priv->last_time) &&
1949 (priv->last_type == 1)) || (gtk_bin_get_child (GTK_BIN (widget)) == NULL))
1952 priv->scroll_indicator_event_interrupt = 1;
1954 hildon_pannable_area_launch_fade_timeout (area,
1955 priv->scroll_indicator_alpha);
1957 priv->last_time = event->time;
1958 priv->last_press_time = event->time;
1959 priv->last_type = 1;
1961 priv->scroll_to_x = -1;
1962 priv->scroll_to_y = -1;
1964 if (priv->button_pressed && priv->child) {
1965 /* Widget stole focus on last click, send crossing-out event */
1966 synth_crossing (priv->child, 0, 0, event->x_root, event->y_root,
1967 event->time, FALSE);
1975 /* Don't allow a click if we're still moving fast */
1976 if ((ABS (priv->vel_x) <= (priv->vmax * priv->vfast_factor)) &&
1977 (ABS (priv->vel_y) <= (priv->vmax * priv->vfast_factor)))
1979 hildon_pannable_area_get_topmost (gtk_bin_get_child (GTK_BIN (widget))->window,
1980 event->x, event->y, &x, &y, GDK_BUTTON_PRESS_MASK);
1984 priv->button_pressed = TRUE;
1986 /* Stop scrolling on mouse-down (so you can flick, then hold to stop) */
1987 priv->old_vel_x = priv->vel_x;
1988 priv->old_vel_y = priv->vel_y;
1991 if (priv->idle_id) {
1992 g_source_remove (priv->idle_id);
1994 g_signal_emit (area, pannable_area_signals[PANNING_FINISHED], 0);
1999 gdk_drawable_get_size (priv->child, &priv->child_width,
2000 &priv->child_height);
2001 priv->last_in = TRUE;
2003 g_object_add_weak_pointer ((GObject *) priv->child,
2004 (gpointer) & priv->child);
2006 event = (GdkEventButton *) gdk_event_copy ((GdkEvent *) event);
2007 /* remove the reference we added with the copy */
2008 g_object_unref (priv->event_window);
2014 synth_crossing (priv->child, x, y, event->x_root,
2015 event->y_root, event->time, TRUE);
2017 /* Send synthetic click (button press/release) event */
2018 ((GdkEventAny *) event)->window = g_object_ref (priv->child);
2020 gdk_event_put ((GdkEvent *) event);
2021 gdk_event_free ((GdkEvent *) event);
2029 hildon_pannable_area_check_scrollbars (HildonPannableArea * area)
2031 HildonPannableAreaPrivate *priv = area->priv;
2032 gboolean prev_hscroll_visible, prev_vscroll_visible;
2034 prev_hscroll_visible = priv->hscroll_visible;
2035 prev_vscroll_visible = priv->vscroll_visible;
2037 if (!gtk_bin_get_child (GTK_BIN (area))) {
2038 priv->vscroll_visible = FALSE;
2039 priv->hscroll_visible = FALSE;
2041 switch (priv->hscrollbar_policy) {
2042 case GTK_POLICY_ALWAYS:
2043 priv->hscroll_visible = TRUE;
2045 case GTK_POLICY_NEVER:
2046 priv->hscroll_visible = FALSE;
2049 priv->hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
2050 priv->hadjust->page_size);
2053 switch (priv->vscrollbar_policy) {
2054 case GTK_POLICY_ALWAYS:
2055 priv->vscroll_visible = TRUE;
2057 case GTK_POLICY_NEVER:
2058 priv->vscroll_visible = FALSE;
2061 priv->vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
2062 priv->vadjust->page_size);
2065 /* Store the vscroll/hscroll areas for redrawing */
2066 if (priv->vscroll_visible) {
2067 GtkAllocation *allocation = >K_WIDGET (area)->allocation;
2068 priv->vscroll_rect.x = allocation->width - priv->indicator_width;
2069 priv->vscroll_rect.y = 0;
2070 priv->vscroll_rect.width = priv->indicator_width;
2071 priv->vscroll_rect.height = allocation->height -
2072 (priv->hscroll_visible ? priv->indicator_width : 0);
2074 if (priv->hscroll_visible) {
2075 GtkAllocation *allocation = >K_WIDGET (area)->allocation;
2076 priv->hscroll_rect.y = allocation->height - priv->indicator_width;
2077 priv->hscroll_rect.x = 0;
2078 priv->hscroll_rect.height = priv->indicator_width;
2079 priv->hscroll_rect.width = allocation->width -
2080 (priv->vscroll_visible ? priv->indicator_width : 0);
2084 return ((priv->hscroll_visible != prev_hscroll_visible) ||
2085 (priv->vscroll_visible != prev_vscroll_visible));
2089 hildon_pannable_area_refresh (HildonPannableArea * area)
2091 if (GTK_WIDGET_DRAWABLE (area) &&
2092 hildon_pannable_area_check_scrollbars (area)) {
2093 HildonPannableAreaPrivate *priv = area->priv;
2095 gtk_widget_queue_resize (GTK_WIDGET (area));
2097 if ((priv->vscroll_visible) || (priv->hscroll_visible)) {
2098 priv->scroll_indicator_event_interrupt = 0;
2099 priv->scroll_delay_counter = area->priv->scrollbar_fade_delay;
2101 hildon_pannable_area_launch_fade_timeout (area, 1.0);
2104 hildon_pannable_area_redraw (area);
2108 /* Scroll by a particular amount (in pixels). Optionally, return if
2109 * the scroll on a particular axis was successful.
2112 hildon_pannable_axis_scroll (HildonPannableArea *area,
2113 GtkAdjustment *adjust,
2117 gint *overshot_dist,
2123 HildonPannableAreaPrivate *priv = area->priv;
2125 dist = gtk_adjustment_get_value (adjust) - inc;
2128 * We use overshot_dist to define the distance of the current overshoot,
2129 * and overshooting to define the direction/whether or not we are overshot
2131 if (!(*overshooting)) {
2133 /* Initiation of the overshoot happens when the finger is released
2134 * and the current position of the pannable contents are out of range
2136 if (dist < adjust->lower) {
2139 dist = adjust->lower;
2141 if (overshoot_max!=0) {
2144 *overshot_dist = CLAMP (*overshot_dist + *vel, 0, overshoot_max);
2145 *vel = MIN (priv->vmax_overshooting, *vel);
2146 gtk_widget_queue_resize (GTK_WIDGET (area));
2150 } else if (dist > adjust->upper - adjust->page_size) {
2153 dist = adjust->upper - adjust->page_size;
2155 if (overshoot_max!=0) {
2158 *overshot_dist = CLAMP (*overshot_dist + *vel, -overshoot_max, 0);
2159 *vel = MAX (-priv->vmax_overshooting, *vel);
2160 gtk_widget_queue_resize (GTK_WIDGET (area));
2165 if ((*scroll_to) != -1) {
2166 if (((inc < 0)&&(*scroll_to <= dist))||
2167 ((inc > 0)&&(*scroll_to >= dist))) {
2175 adjust->value = dist;
2177 if (!priv->button_pressed) {
2179 /* When the overshoot has started we continue for
2180 * PROP_BOUNCE_STEPS more steps into the overshoot before we
2181 * reverse direction. The deceleration factor is calculated
2182 * based on the percentage distance from the first item with
2183 * each iteration, therefore always returning us to the
2184 * top/bottom most element
2186 if (*overshot_dist > 0) {
2188 if ((*overshooting < priv->bounce_steps) && (*vel > 0)) {
2190 *vel = (((gdouble)*overshot_dist)/overshoot_max) * (*vel);
2191 } else if ((*overshooting >= priv->bounce_steps) && (*vel > 0)) {
2193 } else if ((*overshooting > 1) && (*vel < 0)) {
2194 /* we add the MIN in order to avoid very small speeds */
2195 *vel = MIN (((((gdouble)*overshot_dist)*0.8) * -1), -10.0);
2198 *overshot_dist = CLAMP (*overshot_dist + *vel, 0, overshoot_max);
2200 gtk_widget_queue_resize (GTK_WIDGET (area));
2202 } else if (*overshot_dist < 0) {
2204 if ((*overshooting < priv->bounce_steps) && (*vel < 0)) {
2206 *vel = (((gdouble)*overshot_dist)/overshoot_max) * (*vel) * -1;
2207 } else if ((*overshooting >= priv->bounce_steps) && (*vel < 0)) {
2209 } else if ((*overshooting > 1) && (*vel > 0)) {
2210 /* we add the MAX in order to avoid very small speeds */
2211 *vel = MAX (((((gdouble)*overshot_dist)*0.8) * -1), 10.0);
2214 *overshot_dist = CLAMP (*overshot_dist + (*vel), -overshoot_max, 0);
2216 gtk_widget_queue_resize (GTK_WIDGET (area));
2221 gtk_widget_queue_resize (GTK_WIDGET (area));
2225 gint overshot_dist_old = *overshot_dist;
2227 if (*overshot_dist > 0) {
2228 *overshot_dist = CLAMP ((*overshot_dist) + inc, 0, overshoot_max);
2229 } else if (*overshot_dist < 0) {
2230 *overshot_dist = CLAMP ((*overshot_dist) + inc, -1 * overshoot_max, 0);
2233 adjust->value = CLAMP (dist,
2239 if (*overshot_dist != overshot_dist_old)
2240 gtk_widget_queue_resize (GTK_WIDGET (area));
2246 hildon_pannable_area_scroll (HildonPannableArea *area,
2247 gdouble x, gdouble y)
2250 HildonPannableAreaPrivate *priv = area->priv;
2251 gboolean hscroll_visible, vscroll_visible;
2254 if (gtk_bin_get_child (GTK_BIN (area)) == NULL)
2257 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
2258 priv->vadjust->page_size);
2259 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
2260 priv->hadjust->page_size);
2265 hv = priv->hadjust->value;
2266 vv = priv->vadjust->value;
2268 if (vscroll_visible) {
2269 hildon_pannable_axis_scroll (area, priv->vadjust, &priv->vel_y, y,
2270 &priv->overshooting_y, &priv->overshot_dist_y,
2271 &priv->scroll_to_y, priv->vovershoot_max, &sy);
2276 if (hscroll_visible) {
2277 hildon_pannable_axis_scroll (area, priv->hadjust, &priv->vel_x, x,
2278 &priv->overshooting_x, &priv->overshot_dist_x,
2279 &priv->scroll_to_x, priv->hovershoot_max, &sx);
2284 if (hv != priv->hadjust->value)
2285 gtk_adjustment_value_changed (priv->hadjust);
2287 if (vv != priv->vadjust->value)
2288 gtk_adjustment_value_changed (priv->vadjust);
2290 /* If the scroll on a particular axis wasn't succesful, reset the
2291 * initial scroll position to the new mouse co-ordinate. This means
2292 * when you get to the top of the page, dragging down works immediately.
2294 if (priv->mode == HILDON_PANNABLE_AREA_MODE_ACCEL) {
2306 hildon_pannable_area_timeout (HildonPannableArea * area)
2308 HildonPannableAreaPrivate *priv = area->priv;
2310 if ((!priv->enabled) || (priv->mode == HILDON_PANNABLE_AREA_MODE_PUSH)) {
2312 g_signal_emit (area, pannable_area_signals[PANNING_FINISHED], 0);
2317 if (!priv->button_pressed) {
2318 /* Decelerate gradually when pointer is raised */
2319 if ((!priv->overshot_dist_y) &&
2320 (!priv->overshot_dist_x)) {
2322 /* in case we move to a specific point do not decelerate when arriving */
2323 if ((priv->scroll_to_x != -1)||(priv->scroll_to_y != -1)) {
2325 if (ABS (priv->vel_x) >= 1.5) {
2326 priv->vel_x *= priv->decel;
2329 if (ABS (priv->vel_y) >= 1.5) {
2330 priv->vel_y *= priv->decel;
2334 if ((!priv->low_friction_mode) ||
2335 ((priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ) &&
2336 (ABS (priv->vel_x) < 0.8*priv->vmax)))
2337 priv->vel_x *= priv->decel;
2339 if ((!priv->low_friction_mode) ||
2340 ((priv->mov_mode&HILDON_MOVEMENT_MODE_VERT) &&
2341 (ABS (priv->vel_y) < 0.8*priv->vmax)))
2342 priv->vel_y *= priv->decel;
2344 if ((ABS (priv->vel_x) < 1.0) && (ABS (priv->vel_y) < 1.0)) {
2349 g_signal_emit (area, pannable_area_signals[PANNING_FINISHED], 0);
2355 } else if (priv->mode == HILDON_PANNABLE_AREA_MODE_AUTO) {
2361 hildon_pannable_area_scroll (area, priv->vel_x, priv->vel_y);
2363 gdk_window_process_updates (GTK_WIDGET (area)->window, FALSE);
2369 hildon_pannable_area_calculate_velocity (gdouble *vel,
2373 gdouble drag_inertia,
2379 if (ABS (dist) >= RATIO_TOLERANCE) {
2380 rawvel = (dist / ABS (delta)) * force;
2381 *vel = *vel * (1 - drag_inertia) +
2382 rawvel * drag_inertia;
2383 *vel = *vel > 0 ? MIN (*vel, vmax)
2384 : MAX (*vel, -1 * vmax);
2389 hildon_pannable_area_motion_event_scroll_timeout (HildonPannableArea *area)
2391 HildonPannableAreaPrivate *priv = area->priv;
2393 if ((priv->motion_x != 0)||(priv->motion_y != 0))
2394 hildon_pannable_area_scroll (area, priv->motion_x, priv->motion_y);
2396 priv->motion_event_scroll_timeout = 0;
2402 hildon_pannable_area_motion_event_scroll (HildonPannableArea *area,
2403 gdouble x, gdouble y)
2405 HildonPannableAreaPrivate *priv = area->priv;
2407 if (priv->motion_event_scroll_timeout) {
2409 priv->motion_x += x;
2410 priv->motion_y += y;
2414 /* we do not delay the first event but the next ones */
2415 hildon_pannable_area_scroll (area, x, y);
2420 priv->motion_event_scroll_timeout = gdk_threads_add_timeout_full
2421 (G_PRIORITY_HIGH_IDLE + 20,
2422 (gint) (1000.0 / (gdouble) MOTION_EVENTS_PER_SECOND),
2423 (GSourceFunc) hildon_pannable_area_motion_event_scroll_timeout, area, NULL);
2428 hildon_pannable_area_check_move (HildonPannableArea *area,
2429 GdkEventMotion * event,
2433 HildonPannableAreaPrivate *priv = area->priv;
2435 if (priv->first_drag && (!priv->moved) &&
2436 ((ABS (*x) > (priv->panning_threshold))
2437 || (ABS (*y) > (priv->panning_threshold)))) {
2442 if (priv->first_drag) {
2443 gboolean vscroll_visible;
2444 gboolean hscroll_visible;
2446 if (ABS (priv->iy - event->y) >=
2447 ABS (priv->ix - event->x)) {
2449 g_signal_emit (area,
2450 pannable_area_signals[VERTICAL_MOVEMENT],
2451 0, (priv->iy > event->y) ?
2452 HILDON_MOVEMENT_UP :
2453 HILDON_MOVEMENT_DOWN,
2454 (gdouble)priv->ix, (gdouble)priv->iy);
2456 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
2457 priv->vadjust->page_size);
2459 if (!((vscroll_visible)&&
2460 (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT))) {
2462 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
2463 priv->hadjust->page_size);
2465 /* even in case we do not have to move we check if this
2466 could be a fake horizontal movement */
2467 if (!((hscroll_visible)&&
2468 (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ)) ||
2469 (ABS (priv->iy - event->y) -
2470 ABS (priv->ix - event->x) >= priv->direction_error_margin))
2471 priv->moved = FALSE;
2475 g_signal_emit (area,
2476 pannable_area_signals[HORIZONTAL_MOVEMENT],
2477 0, (priv->ix > event->x) ?
2478 HILDON_MOVEMENT_LEFT :
2479 HILDON_MOVEMENT_RIGHT,
2480 (gdouble)priv->ix, (gdouble)priv->iy);
2482 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
2483 priv->hadjust->page_size);
2485 if (!((hscroll_visible)&&
2486 (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ))) {
2488 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
2489 priv->vadjust->page_size);
2491 /* even in case we do not have to move we check if this
2492 could be a fake vertical movement */
2493 if (!((vscroll_visible) &&
2494 (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT)) ||
2495 (ABS (priv->ix - event->x) -
2496 ABS (priv->iy - event->y) >= priv->direction_error_margin))
2497 priv->moved = FALSE;
2501 if ((priv->moved)&&(priv->child)) {
2504 pos_x = priv->cx + (event->x - priv->ix);
2505 pos_y = priv->cy + (event->y - priv->iy);
2507 synth_crossing (priv->child, pos_x, pos_y, event->x_root,
2508 event->y_root, event->time, FALSE);
2512 gboolean result_val;
2514 g_signal_emit (area,
2515 pannable_area_signals[PANNING_STARTED],
2518 priv->moved = !result_val;
2522 priv->first_drag = FALSE;
2524 if ((priv->mode != HILDON_PANNABLE_AREA_MODE_PUSH) &&
2525 (priv->mode != HILDON_PANNABLE_AREA_MODE_AUTO)) {
2528 priv->idle_id = gdk_threads_add_timeout_full
2529 (G_PRIORITY_HIGH_IDLE + 20,
2530 (gint)(1000.0 / (gdouble) priv->sps),
2532 hildon_pannable_area_timeout, area, NULL);
2538 hildon_pannable_area_handle_move (HildonPannableArea *area,
2539 GdkEventMotion * event,
2543 HildonPannableAreaPrivate *priv = area->priv;
2546 switch (priv->mode) {
2547 case HILDON_PANNABLE_AREA_MODE_PUSH:
2548 /* Scroll by the amount of pixels the cursor has moved
2549 * since the last motion event.
2551 hildon_pannable_area_motion_event_scroll (area, *x, *y);
2555 case HILDON_PANNABLE_AREA_MODE_ACCEL:
2556 /* Set acceleration relative to the initial click */
2557 priv->ex = event->x;
2558 priv->ey = event->y;
2559 priv->vel_x = ((*x > 0) ? 1 : -1) *
2561 (gdouble) GTK_WIDGET (area)->allocation.width) *
2562 (priv->vmax - priv->vmin)) + priv->vmin);
2563 priv->vel_y = ((*y > 0) ? 1 : -1) *
2565 (gdouble) GTK_WIDGET (area)->allocation.height) *
2566 (priv->vmax - priv->vmin)) + priv->vmin);
2568 case HILDON_PANNABLE_AREA_MODE_AUTO:
2570 delta = event->time - priv->last_time;
2572 if (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT) {
2573 gdouble dist = event->y - priv->y;
2575 hildon_pannable_area_calculate_velocity (&priv->vel_y,
2587 if (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ) {
2588 gdouble dist = event->x - priv->x;
2590 hildon_pannable_area_calculate_velocity (&priv->vel_x,
2602 hildon_pannable_area_motion_event_scroll (area, *x, *y);
2604 if (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ)
2606 if (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT)
2616 hildon_pannable_area_motion_notify_cb (GtkWidget * widget,
2617 GdkEventMotion * event)
2619 HildonPannableArea *area = HILDON_PANNABLE_AREA (widget);
2620 HildonPannableAreaPrivate *priv = area->priv;
2623 if (gtk_bin_get_child (GTK_BIN (widget)) == NULL)
2626 if ((!priv->enabled) || (!priv->button_pressed) ||
2627 ((event->time == priv->last_time) && (priv->last_type == 2))) {
2628 gdk_window_get_pointer (widget->window, NULL, NULL, 0);
2632 if (priv->last_type == 1) {
2633 priv->first_drag = TRUE;
2636 x = event->x - priv->x;
2637 y = event->y - priv->y;
2640 hildon_pannable_area_check_move (area, event, &x, &y);
2644 hildon_pannable_area_handle_move (area, event, &x, &y);
2645 } else if (priv->child) {
2649 pos_x = priv->cx + (event->x - priv->ix);
2650 pos_y = priv->cy + (event->y - priv->iy);
2652 in = (((0 <= pos_x)&&(priv->child_width >= pos_x)) &&
2653 ((0 <= pos_y)&&(priv->child_height >= pos_y)));
2655 if (((!priv->last_in)&&in)||((priv->last_in)&&(!in))) {
2657 synth_crossing (priv->child, pos_x, pos_y, event->x_root,
2658 event->y_root, event->time, in);
2664 priv->last_time = event->time;
2665 priv->last_type = 2;
2668 /* Send motion notify to child */
2669 event = (GdkEventMotion *) gdk_event_copy ((GdkEvent *) event);
2670 /* remove the reference we added with the copy */
2671 g_object_unref (priv->event_window);
2672 event->x = priv->cx + (event->x - priv->ix);
2673 event->y = priv->cy + (event->y - priv->iy);
2674 event->window = g_object_ref (priv->child);
2675 gdk_event_put ((GdkEvent *) event);
2676 gdk_event_free ((GdkEvent *) event);
2679 gdk_window_get_pointer (widget->window, NULL, NULL, 0);
2685 hildon_pannable_leave_notify_event (GtkWidget *widget,
2686 GdkEventCrossing *event)
2688 HildonPannableArea *area = HILDON_PANNABLE_AREA (widget);
2689 HildonPannableAreaPrivate *priv = area->priv;
2691 if ((priv->child)&&(priv->last_in)) {
2692 priv->last_in = FALSE;
2694 synth_crossing (priv->child, 0, 0, event->x_root,
2695 event->y_root, event->time, FALSE);
2702 hildon_pannable_area_button_release_cb (GtkWidget * widget,
2703 GdkEventButton * event)
2705 HildonPannableArea *area = HILDON_PANNABLE_AREA (widget);
2706 HildonPannableAreaPrivate *priv = area->priv;
2710 gboolean force_fast = TRUE;
2712 if (((event->time == priv->last_time) && (priv->last_type == 3))
2713 || (gtk_bin_get_child (GTK_BIN (widget)) == NULL)
2714 || (!priv->button_pressed) || (!priv->enabled) || (event->button != 1))
2717 /* if last event was a motion-notify we have to check the movement
2718 and launch the animation */
2719 if (priv->last_type == 2) {
2721 dx = event->x - priv->x;
2722 dy = event->y - priv->y;
2724 hildon_pannable_area_check_move (area, (GdkEventMotion *) event, &dx, &dy);
2727 gdouble delta = event->time - priv->last_time;
2729 hildon_pannable_area_handle_move (area, (GdkEventMotion *) event, &dx, &dy);
2731 /* move all the way to the last position now */
2732 if (priv->motion_event_scroll_timeout) {
2733 g_source_remove (priv->motion_event_scroll_timeout);
2734 hildon_pannable_area_motion_event_scroll_timeout (HILDON_PANNABLE_AREA (widget));
2739 if ((ABS (dx) < 4.0) && (delta >= CURSOR_STOPPED_TIMEOUT))
2742 if ((ABS (dy) < 4.0) && (delta >= CURSOR_STOPPED_TIMEOUT))
2747 /* If overshoot has been initiated with a finger down, on release set max speed */
2748 if (priv->overshot_dist_y != 0) {
2749 priv->overshooting_y = priv->bounce_steps; /* Hack to stop a bounce in the finger down case */
2750 priv->vel_y = priv->overshot_dist_y * 0.9;
2753 if (priv->overshot_dist_x != 0) {
2754 priv->overshooting_x = priv->bounce_steps; /* Hack to stop a bounce in the finger down case */
2755 priv->vel_x = priv->overshot_dist_x * 0.9;
2758 priv->button_pressed = FALSE;
2760 /* if widget was moving fast in the panning, increase speed even more */
2761 if ((event->time - priv->last_press_time < 125) &&
2762 ((ABS (priv->old_vel_x) > priv->vmin) ||
2763 (ABS (priv->old_vel_y) > priv->vmin)) &&
2764 ((ABS (priv->old_vel_x) > 40) ||
2765 (ABS (priv->old_vel_y) > 40)))
2769 if (priv->vel_x != 0)
2770 symbol = ((priv->vel_x * priv->old_vel_x) > 0) ? 1 : -1;
2772 priv->vel_x = symbol *
2773 (priv->old_vel_x + ((priv->old_vel_x > 0) ? priv->vmax : -priv->vmax));
2777 if (priv->vel_y != 0)
2778 symbol = ((priv->vel_y * priv->old_vel_y) > 0) ? 1 : -1;
2780 priv->vel_y = symbol *
2781 (priv->old_vel_y + ((priv->old_vel_y > 0) ? priv->vmax : -priv->vmax));
2786 if ((ABS (priv->vel_y) >= priv->vmin) ||
2787 (ABS (priv->vel_x) >= priv->vmin)) {
2789 /* we have to move because we are in overshooting position*/
2791 gboolean result_val;
2793 g_signal_emit (area,
2794 pannable_area_signals[PANNING_STARTED],
2798 priv->scroll_indicator_alpha = 1.0;
2801 if (ABS (priv->vel_x) > MAX_SPEED_THRESHOLD)
2802 priv->vel_x = (priv->vel_x > 0) ? priv->vmax : -priv->vmax;
2804 if (ABS (priv->vel_y) > MAX_SPEED_THRESHOLD)
2805 priv->vel_y = (priv->vel_y > 0) ? priv->vmax : -priv->vmax;
2809 priv->idle_id = gdk_threads_add_timeout_full (G_PRIORITY_HIGH_IDLE + 20,
2810 (gint) (1000.0 / (gdouble) priv->sps),
2811 (GSourceFunc) hildon_pannable_area_timeout,
2814 if (priv->center_on_child_focus_pending) {
2815 hildon_pannable_area_center_on_child_focus (area);
2819 g_signal_emit (widget, pannable_area_signals[PANNING_FINISHED], 0);
2822 area->priv->center_on_child_focus_pending = FALSE;
2824 priv->scroll_indicator_event_interrupt = 0;
2825 priv->scroll_delay_counter = priv->scrollbar_fade_delay;
2827 hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget),
2828 priv->scroll_indicator_alpha);
2830 priv->last_time = event->time;
2831 priv->last_type = 3;
2834 priv->moved = FALSE;
2839 hildon_pannable_area_get_topmost (gtk_bin_get_child (GTK_BIN (widget))->window,
2840 event->x, event->y, &x, &y, GDK_BUTTON_RELEASE_MASK);
2842 event = (GdkEventButton *) gdk_event_copy ((GdkEvent *) event);
2843 /* remove the reference we added with the copy */
2844 g_object_unref (priv->event_window);
2848 /* Leave the widget if we've moved - This doesn't break selection,
2849 * but stops buttons from being clicked.
2851 if ((child != priv->child) || (priv->moved)) {
2852 /* Send synthetic leave event */
2853 synth_crossing (priv->child, x, y, event->x_root,
2854 event->y_root, event->time, FALSE);
2855 /* insure no click will happen for widgets that do not handle
2859 /* Send synthetic button release event */
2860 ((GdkEventAny *) event)->window = g_object_ref (priv->child);
2861 gdk_event_put ((GdkEvent *) event);
2863 /* Send synthetic button release event */
2864 ((GdkEventAny *) event)->window = g_object_ref (child);
2865 gdk_event_put ((GdkEvent *) event);
2866 /* Send synthetic leave event */
2867 synth_crossing (priv->child, x, y, event->x_root,
2868 event->y_root, event->time, FALSE);
2870 g_object_remove_weak_pointer ((GObject *) priv->child,
2871 (gpointer) & priv->child);
2873 priv->moved = FALSE;
2874 gdk_event_free ((GdkEvent *) event);
2879 /* utility event handler */
2881 hildon_pannable_area_scroll_cb (GtkWidget *widget,
2882 GdkEventScroll *event)
2884 GtkAdjustment *adj = NULL;
2885 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
2887 if ((!priv->enabled) ||
2888 (gtk_bin_get_child (GTK_BIN (widget)) == NULL))
2891 priv->scroll_indicator_event_interrupt = 0;
2892 priv->scroll_delay_counter = priv->scrollbar_fade_delay + 20;
2894 hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget), 1.0);
2896 /* Stop inertial scrolling */
2897 if (priv->idle_id) {
2900 priv->overshooting_x = 0;
2901 priv->overshooting_y = 0;
2903 if ((priv->overshot_dist_x>0)||(priv->overshot_dist_y>0)) {
2904 priv->overshot_dist_x = 0;
2905 priv->overshot_dist_y = 0;
2907 gtk_widget_queue_resize (GTK_WIDGET (widget));
2910 g_signal_emit (widget, pannable_area_signals[PANNING_FINISHED], 0);
2912 g_source_remove (priv->idle_id);
2916 if (event->direction == GDK_SCROLL_UP || event->direction == GDK_SCROLL_DOWN)
2917 adj = priv->vadjust;
2919 adj = priv->hadjust;
2923 gdouble delta, new_value;
2925 /* from gtkrange.c calculate delta*/
2926 delta = pow (adj->page_size, 2.0 / 3.0);
2928 if (event->direction == GDK_SCROLL_UP ||
2929 event->direction == GDK_SCROLL_LEFT)
2932 new_value = CLAMP (adj->value + delta, adj->lower, adj->upper - adj->page_size);
2934 gtk_adjustment_set_value (adj, new_value);
2941 hildon_pannable_area_child_mapped (GtkWidget *widget,
2945 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (user_data)->priv;
2947 if (priv->event_window != NULL && priv->enabled)
2948 gdk_window_raise (priv->event_window);
2952 hildon_pannable_area_add (GtkContainer *container, GtkWidget *child)
2954 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (container)->priv;
2956 g_return_if_fail (gtk_bin_get_child (GTK_BIN (container)) == NULL);
2958 gtk_widget_set_parent (child, GTK_WIDGET (container));
2959 GTK_BIN (container)->child = child;
2961 g_signal_connect_after (child, "map-event",
2962 G_CALLBACK (hildon_pannable_area_child_mapped),
2965 if (!gtk_widget_set_scroll_adjustments (child, priv->hadjust, priv->vadjust)) {
2966 g_warning ("%s: cannot add non scrollable widget, "
2967 "wrap it in a viewport", __FUNCTION__);
2971 /* call this function if you are not panning */
2973 hildon_pannable_area_center_on_child_focus (HildonPannableArea *area)
2975 GtkWidget *focused_child = NULL;
2976 GtkWidget *window = NULL;
2978 window = gtk_widget_get_toplevel (GTK_WIDGET (area));
2980 if (GTK_WIDGET_TOPLEVEL (window)) {
2981 focused_child = gtk_window_get_focus (GTK_WINDOW (window));
2984 if (focused_child) {
2985 hildon_pannable_area_scroll_to_child (area, focused_child);
2990 hildon_pannable_area_set_focus_child (GtkContainer *container,
2993 HildonPannableArea *area = HILDON_PANNABLE_AREA (container);
2995 if (!area->priv->center_on_child_focus) {
2999 if (GTK_IS_WIDGET (child)) {
3000 area->priv->center_on_child_focus_pending = TRUE;
3005 hildon_pannable_area_remove (GtkContainer *container, GtkWidget *child)
3007 g_return_if_fail (HILDON_IS_PANNABLE_AREA (container));
3008 g_return_if_fail (child != NULL);
3009 g_return_if_fail (gtk_bin_get_child (GTK_BIN (container)) == child);
3011 gtk_widget_set_scroll_adjustments (child, NULL, NULL);
3013 g_signal_handlers_disconnect_by_func (child,
3014 hildon_pannable_area_child_mapped,
3017 /* chain parent class handler to remove child */
3018 GTK_CONTAINER_CLASS (hildon_pannable_area_parent_class)->remove (container, child);
3022 * This method calculates a factor necessary to determine the initial distance
3023 * to jump in hildon_pannable_area_scroll_to(). For fixed time and frames per
3024 * second, we know in how many frames 'n' we need to reach the destination
3025 * point. We know that, for a distance d,
3027 * d = d_0 + d_1 + ... + d_n
3029 * where d_i is the distance travelled in the i-th frame and decel_factor is
3030 * the deceleration factor. This can be rewritten as
3032 * d = d_0 + (d_0 * decel_factor) + ... + (d_n-1 * decel_factor),
3034 * since the distance travelled on each frame is the distance travelled in the
3035 * previous frame reduced by the deceleration factor. Reducing this and
3036 * factoring d_0 out, we get
3038 * d = d_0 (1 + decel_factor + ... + decel_factor^(n-1)).
3040 * Since the sum is independent of the distance to be travelled, we can define
3043 * vel_factor = 1 + decel_factor + ... + decel_factor^(n-1).
3045 * That's the gem we calculate in this method.
3048 hildon_pannable_calculate_vel_factor (HildonPannableArea * self)
3050 HildonPannableAreaPrivate *priv = self->priv;
3055 n = ceil (priv->sps * priv->scroll_time);
3057 for (i = 1; i < n && fct_i >= RATIO_TOLERANCE; i++) {
3058 fct_i *= priv->decel;
3062 priv->vel_factor = fct;
3066 * hildon_pannable_area_new:
3068 * Create a new pannable area widget
3070 * Returns: the newly created #HildonPannableArea
3076 hildon_pannable_area_new (void)
3078 return g_object_new (HILDON_TYPE_PANNABLE_AREA, NULL);
3082 * hildon_pannable_area_new_full:
3083 * @mode: #HildonPannableAreaMode
3084 * @enabled: Value for the enabled property
3085 * @vel_min: Value for the velocity-min property
3086 * @vel_max: Value for the velocity-max property
3087 * @decel: Value for the deceleration property
3088 * @sps: Value for the sps property
3090 * Create a new #HildonPannableArea widget and set various properties
3092 * returns: the newly create #HildonPannableArea
3098 hildon_pannable_area_new_full (gint mode, gboolean enabled,
3099 gdouble vel_min, gdouble vel_max,
3100 gdouble decel, guint sps)
3102 return g_object_new (HILDON_TYPE_PANNABLE_AREA,
3105 "velocity_min", vel_min,
3106 "velocity_max", vel_max,
3107 "deceleration", decel, "sps", sps, NULL);
3111 * hildon_pannable_area_add_with_viewport:
3112 * @area: A #HildonPannableArea
3113 * @child: Child widget to add to the viewport
3115 * Convenience function used to add a child to a #GtkViewport, and add the
3116 * viewport to the scrolled window.
3122 hildon_pannable_area_add_with_viewport (HildonPannableArea * area,
3126 GtkWidget *viewport;
3128 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3129 g_return_if_fail (GTK_IS_WIDGET (child));
3130 g_return_if_fail (child->parent == NULL);
3132 bin = GTK_BIN (area);
3134 if (bin->child != NULL)
3136 g_return_if_fail (GTK_IS_VIEWPORT (bin->child));
3137 g_return_if_fail (GTK_BIN (bin->child)->child == NULL);
3139 viewport = bin->child;
3143 HildonPannableAreaPrivate *priv = area->priv;
3145 viewport = gtk_viewport_new (priv->hadjust,
3147 gtk_viewport_set_shadow_type (GTK_VIEWPORT (viewport), GTK_SHADOW_NONE);
3148 gtk_container_add (GTK_CONTAINER (area), viewport);
3151 gtk_widget_show (viewport);
3152 gtk_container_add (GTK_CONTAINER (viewport), child);
3156 * hildon_pannable_area_scroll_to:
3157 * @area: A #HildonPannableArea.
3158 * @x: The x coordinate of the destination point or -1 to ignore this axis.
3159 * @y: The y coordinate of the destination point or -1 to ignore this axis.
3161 * Smoothly scrolls @area to ensure that (@x, @y) is a visible point
3162 * on the widget. To move in only one coordinate, you must set the other one
3163 * to -1. Notice that, in %HILDON_PANNABLE_AREA_MODE_PUSH mode, this function
3164 * works just like hildon_pannable_area_jump_to().
3166 * This function is useful if you need to present the user with a particular
3167 * element inside a scrollable widget, like #GtkTreeView. For instance,
3168 * the following example shows how to scroll inside a #GtkTreeView to
3169 * make visible an item, indicated by the #GtkTreeIter @iter.
3173 * GtkTreePath *path;
3174 * GdkRectangle *rect;
3176 * path = gtk_tree_model_get_path (model, &iter);
3177 * gtk_tree_view_get_background_area (GTK_TREE_VIEW (treeview),
3178 * path, NULL, &rect);
3179 * gtk_tree_view_convert_bin_window_to_tree_coords (GTK_TREE_VIEW (treeview),
3180 * 0, rect.y, NULL, &y);
3181 * hildon_pannable_area_scroll_to (panarea, -1, y);
3182 * gtk_tree_path_free (path);
3186 * If you want to present a child widget in simpler scenarios,
3187 * use hildon_pannable_area_scroll_to_child() instead.
3189 * There is a precondition to this function: the widget must be
3190 * already realized. Check the hildon_pannable_area_jump_to_child() for
3191 * more tips regarding how to call this function during
3197 hildon_pannable_area_scroll_to (HildonPannableArea *area,
3198 const gint x, const gint y)
3200 HildonPannableAreaPrivate *priv;
3202 gint dist_x, dist_y;
3203 gboolean hscroll_visible, vscroll_visible;
3205 g_return_if_fail (GTK_WIDGET_REALIZED (area));
3206 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3210 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
3211 priv->vadjust->page_size);
3212 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
3213 priv->hadjust->page_size);
3215 if (((!vscroll_visible)&&(!hscroll_visible)) ||
3216 (x == -1 && y == -1)) {
3220 if (priv->mode == HILDON_PANNABLE_AREA_MODE_PUSH)
3221 hildon_pannable_area_jump_to (area, x, y);
3223 width = priv->hadjust->upper - priv->hadjust->lower;
3224 height = priv->vadjust->upper - priv->vadjust->lower;
3226 g_return_if_fail (x < width || y < height);
3228 if ((x > -1)&&(hscroll_visible)) {
3229 priv->scroll_to_x = CLAMP (x - priv->hadjust->page_size/2,
3230 priv->hadjust->lower,
3231 priv->hadjust->upper - priv->hadjust->page_size);
3232 dist_x = priv->scroll_to_x - priv->hadjust->value;
3234 priv->scroll_to_x = -1;
3236 priv->vel_x = - dist_x/priv->vel_factor;
3239 priv->scroll_to_x = -1;
3242 if ((y > -1)&&(vscroll_visible)) {
3243 priv->scroll_to_y = CLAMP (y - priv->vadjust->page_size/2,
3244 priv->vadjust->lower,
3245 priv->vadjust->upper - priv->vadjust->page_size);
3246 dist_y = priv->scroll_to_y - priv->vadjust->value;
3248 priv->scroll_to_y = -1;
3250 priv->vel_y = - dist_y/priv->vel_factor;
3253 priv->scroll_to_y = y;
3256 if ((priv->scroll_to_y == -1) && (priv->scroll_to_x == -1)) {
3260 hildon_pannable_area_launch_fade_timeout (area, 1.0);
3263 priv->idle_id = gdk_threads_add_timeout_full (G_PRIORITY_HIGH_IDLE + 20,
3264 (gint) (1000.0 / (gdouble) priv->sps),
3265 (GSourceFunc) hildon_pannable_area_timeout,
3270 * hildon_pannable_area_jump_to:
3271 * @area: A #HildonPannableArea.
3272 * @x: The x coordinate of the destination point or -1 to ignore this axis.
3273 * @y: The y coordinate of the destination point or -1 to ignore this axis.
3275 * Jumps the position of @area to ensure that (@x, @y) is a visible
3276 * point in the widget. In order to move in only one coordinate, you
3277 * must set the other one to -1. See hildon_pannable_area_scroll_to()
3278 * function for an example of how to calculate the position of
3279 * children in scrollable widgets like #GtkTreeview.
3281 * There is a precondition to this function: the widget must be
3282 * already realized. Check the hildon_pannable_area_jump_to_child() for
3283 * more tips regarding how to call this function during
3289 hildon_pannable_area_jump_to (HildonPannableArea *area,
3290 const gint x, const gint y)
3292 HildonPannableAreaPrivate *priv;
3296 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3297 g_return_if_fail (GTK_WIDGET_REALIZED (area));
3298 g_return_if_fail (x >= -1 && y >= -1);
3300 if (x == -1 && y == -1) {
3306 width = priv->hadjust->upper - priv->hadjust->lower;
3307 height = priv->vadjust->upper - priv->vadjust->lower;
3309 g_return_if_fail (x < width || y < height);
3311 hv = priv->hadjust->value;
3312 vv = priv->vadjust->value;
3315 gdouble jump_to = x - priv->hadjust->page_size/2;
3317 priv->hadjust->value = CLAMP (jump_to,
3318 priv->hadjust->lower,
3319 priv->hadjust->upper -
3320 priv->hadjust->page_size);
3324 gdouble jump_to = y - priv->vadjust->page_size/2;
3326 priv->vadjust->value = CLAMP (jump_to,
3327 priv->vadjust->lower,
3328 priv->vadjust->upper -
3329 priv->vadjust->page_size);
3332 if (hv != priv->hadjust->value)
3333 gtk_adjustment_value_changed (priv->hadjust);
3335 if (vv != priv->vadjust->value)
3336 gtk_adjustment_value_changed (priv->vadjust);
3338 priv->scroll_indicator_alpha = 1.0;
3340 if (priv->scroll_indicator_timeout) {
3341 g_source_remove (priv->scroll_indicator_timeout);
3342 priv->scroll_indicator_timeout = 0;
3345 if (priv->idle_id) {
3348 priv->overshooting_x = 0;
3349 priv->overshooting_y = 0;
3351 if ((priv->overshot_dist_x>0)||(priv->overshot_dist_y>0)) {
3352 priv->overshot_dist_x = 0;
3353 priv->overshot_dist_y = 0;
3355 gtk_widget_queue_resize (GTK_WIDGET (area));
3358 g_signal_emit (area, pannable_area_signals[PANNING_FINISHED], 0);
3359 g_source_remove (priv->idle_id);
3365 * hildon_pannable_area_scroll_to_child:
3366 * @area: A #HildonPannableArea.
3367 * @child: A #GtkWidget, descendant of @area.
3369 * Smoothly scrolls until @child is visible inside @area. @child must
3370 * be a descendant of @area. If you need to scroll inside a scrollable
3371 * widget, e.g., #GtkTreeview, see hildon_pannable_area_scroll_to().
3373 * There is a precondition to this function: the widget must be
3374 * already realized. Check the hildon_pannable_area_jump_to_child() for
3375 * more tips regarding how to call this function during
3381 hildon_pannable_area_scroll_to_child (HildonPannableArea *area, GtkWidget *child)
3383 GtkWidget *bin_child;
3386 g_return_if_fail (GTK_WIDGET_REALIZED (area));
3387 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3388 g_return_if_fail (GTK_IS_WIDGET (child));
3389 g_return_if_fail (gtk_widget_is_ancestor (child, GTK_WIDGET (area)));
3391 if (GTK_BIN (area)->child == NULL)
3394 /* We need to get to check the child of the inside the area */
3395 bin_child = GTK_BIN (area)->child;
3397 /* we check if we added a viewport */
3398 if (GTK_IS_VIEWPORT (bin_child)) {
3399 bin_child = GTK_BIN (bin_child)->child;
3402 if (gtk_widget_translate_coordinates (child, bin_child, 0, 0, &x, &y))
3403 hildon_pannable_area_scroll_to (area, x, y);
3407 * hildon_pannable_area_jump_to_child:
3408 * @area: A #HildonPannableArea.
3409 * @child: A #GtkWidget, descendant of @area.
3411 * Jumps to make sure @child is visible inside @area. @child must
3412 * be a descendant of @area. If you want to move inside a scrollable
3413 * widget, like, #GtkTreeview, see hildon_pannable_area_scroll_to().
3415 * There is a precondition to this function: the widget must be
3416 * already realized. You can control if the widget is ready with the
3417 * GTK_WIDGET_REALIZED macro. If you want to call this function during
3418 * the initialization process of the widget do it inside a callback to
3419 * the ::realize signal, using g_signal_connect_after() function.
3424 hildon_pannable_area_jump_to_child (HildonPannableArea *area, GtkWidget *child)
3426 GtkWidget *bin_child;
3429 g_return_if_fail (GTK_WIDGET_REALIZED (area));
3430 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3431 g_return_if_fail (GTK_IS_WIDGET (child));
3432 g_return_if_fail (gtk_widget_is_ancestor (child, GTK_WIDGET (area)));
3434 if (gtk_bin_get_child (GTK_BIN (area)) == NULL)
3437 /* We need to get to check the child of the inside the area */
3438 bin_child = gtk_bin_get_child (GTK_BIN (area));
3440 /* we check if we added a viewport */
3441 if (GTK_IS_VIEWPORT (bin_child)) {
3442 bin_child = gtk_bin_get_child (GTK_BIN (bin_child));
3445 if (gtk_widget_translate_coordinates (child, bin_child, 0, 0, &x, &y))
3446 hildon_pannable_area_jump_to (area, x, y);
3450 * hildon_pannable_get_child_widget_at:
3451 * @area: A #HildonPannableArea.
3452 * @x: horizontal coordinate of the point
3453 * @y: vertical coordinate of the point
3455 * Get the widget at the point (x, y) inside the pannable area. In
3456 * case no widget found it returns NULL.
3458 * returns: the #GtkWidget if we find a widget, NULL in any other case
3463 hildon_pannable_get_child_widget_at (HildonPannableArea *area,
3464 gdouble x, gdouble y)
3466 GdkWindow *window = NULL;
3467 GtkWidget *child_widget = NULL;
3469 window = hildon_pannable_area_get_topmost
3470 (gtk_bin_get_child (GTK_BIN (area))->window,
3471 x, y, NULL, NULL, GDK_ALL_EVENTS_MASK);
3473 gdk_window_get_user_data (window, (gpointer) &child_widget);
3475 return child_widget;
3480 * hildon_pannable_area_get_hadjustment:
3481 * @area: A #HildonPannableArea.
3483 * Returns the horizontal adjustment. This adjustment is the internal
3484 * widget adjustment used to control the animations. Do not modify it
3485 * directly to change the position of the pannable, to do that use the
3486 * pannable API. If you modify the object directly it could cause
3487 * artifacts in the animations.
3489 * returns: The horizontal #GtkAdjustment
3494 hildon_pannable_area_get_hadjustment (HildonPannableArea *area)
3497 g_return_val_if_fail (HILDON_IS_PANNABLE_AREA (area), NULL);
3499 return area->priv->hadjust;
3503 * hildon_pannable_area_get_vadjustment:
3504 * @area: A #HildonPannableArea.
3506 * Returns the vertical adjustment. This adjustment is the internal
3507 * widget adjustment used to control the animations. Do not modify it
3508 * directly to change the position of the pannable, to do that use the
3509 * pannable API. If you modify the object directly it could cause
3510 * artifacts in the animations.
3512 * returns: The vertical #GtkAdjustment
3517 hildon_pannable_area_get_vadjustment (HildonPannableArea *area)
3519 g_return_val_if_fail (HILDON_IS_PANNABLE_AREA (area), NULL);
3521 return area->priv->vadjust;
3526 * hildon_pannable_area_get_size_request_policy:
3527 * @area: A #HildonPannableArea.
3529 * This function returns the current size request policy of the
3530 * widget. That policy controls the way the size_request is done in
3531 * the pannable area. Check
3532 * hildon_pannable_area_set_size_request_policy() for a more detailed
3535 * returns: the policy is currently being used in the widget
3536 * #HildonSizeRequestPolicy.
3540 HildonSizeRequestPolicy
3541 hildon_pannable_area_get_size_request_policy (HildonPannableArea *area)
3543 HildonPannableAreaPrivate *priv;
3545 g_return_val_if_fail (HILDON_IS_PANNABLE_AREA (area), FALSE);
3549 return priv->size_request_policy;
3553 * hildon_pannable_area_set_size_request_policy:
3554 * @area: A #HildonPannableArea.
3555 * @size_request_policy: One of the allowed #HildonSizeRequestPolicy
3557 * This function sets the pannable area size request policy. That
3558 * policy controls the way the size_request is done in the pannable
3559 * area. Pannable can use the size request of its children
3560 * (#HILDON_SIZE_REQUEST_CHILDREN) or the minimum size required for
3561 * the area itself (#HILDON_SIZE_REQUEST_MINIMUM), the latter is the
3562 * default. Recall this size depends on the scrolling policy you are
3563 * requesting to the pannable area, if you set #GTK_POLICY_NEVER this
3564 * parameter will not have any effect with
3565 * #HILDON_SIZE_REQUEST_MINIMUM set.
3569 * Deprecated: This method and the policy request is deprecated, DO
3570 * NOT use it in future code, the only policy properly supported in
3571 * gtk+ nowadays is the minimum size. Use #gtk_window_set_default_size
3572 * or #gtk_window_set_geometry_hints with the proper size in your case
3573 * to define the height of your dialogs.
3576 hildon_pannable_area_set_size_request_policy (HildonPannableArea *area,
3577 HildonSizeRequestPolicy size_request_policy)
3579 HildonPannableAreaPrivate *priv;
3581 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3585 if (priv->size_request_policy == size_request_policy)
3588 priv->size_request_policy = size_request_policy;
3590 gtk_widget_queue_resize (GTK_WIDGET (area));
3592 g_object_notify (G_OBJECT (area), "size-request-policy");
3596 * hildon_pannable_area_get_center_on_child_focus
3597 * @area: A #HildonPannableArea
3599 * Gets the @area #HildonPannableArea:center-on-child-focus property
3602 * See #HildonPannableArea:center-on-child-focus for more information.
3604 * Returns: the @area #HildonPannableArea:center-on-child-focus value
3609 hildon_pannable_area_get_center_on_child_focus (HildonPannableArea *area)
3611 g_return_val_if_fail (HILDON_IS_PANNABLE_AREA (area), FALSE);
3613 return area->priv->center_on_child_focus;
3617 * hildon_pannable_area_set_center_on_child_focus
3618 * @area: A #HildonPannableArea
3619 * @value: the new value
3621 * Sets the @area #HildonPannableArea:center-on-child-focus property
3624 * See #HildonPannableArea:center-on-child-focus for more information.
3629 hildon_pannable_area_set_center_on_child_focus (HildonPannableArea *area,
3632 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3634 area->priv->center_on_child_focus = value;