2 * This file is a part of hildon
4 * Copyright (C) 2008 Nokia Corporation, all rights reserved.
6 * Contact: Rodrigo Novo <rodrigo.novo@nokia.com>
8 * This widget is based on MokoFingerScroll from libmokoui
9 * OpenMoko Application Framework UI Library
10 * Authored by Chris Lord <chris@openedhand.com>
11 * Copyright (C) 2006-2007 OpenMoko Inc.
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU Lesser Public License as published by
15 * the Free Software Foundation; version 2 of the license.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU Lesser Public License for more details.
25 * SECTION: hildon-pannable-area
26 * @short_description: A scrolling widget designed for touch screens
27 * @see_also: #GtkScrolledWindow
29 * #HildonPannableArea is a container widget that can be "panned" (scrolled)
30 * up and down using the touchscreen with fingers. The widget has no scrollbars,
31 * but it rather shows small scroll indicators to give an idea of the part of the
32 * content that is visible at a time. The scroll indicators appear when a dragging
33 * motion is started on the pannable area.
35 * The scrolling is "kinetic", meaning the motion can be "flicked" and it will
36 * continue from the initial motion by gradually slowing down to an eventual stop.
37 * The motion can also be stopped immediately by pressing the touchscreen over the
41 #undef HILDON_DISABLE_DEPRECATED
44 #if USE_CAIRO_SCROLLBARS == 1
49 #include "hildon-pannable-area.h"
50 #include "hildon-marshalers.h"
51 #include "hildon-enum-types.h"
53 #define USE_CAIRO_SCROLLBARS 0
55 #define SCROLL_BAR_MIN_SIZE 5
56 #define RATIO_TOLERANCE 0.000001
57 #define SCROLL_FADE_TIMEOUT 100
58 #define MOTION_EVENTS_PER_SECOND 25
59 #define CURSOR_STOPPED_TIMEOUT 80
60 #define MAX_SPEED_THRESHOLD 250
61 #define PANNABLE_MAX_WIDTH 788
62 #define PANNABLE_MAX_HEIGHT 378
64 G_DEFINE_TYPE (HildonPannableArea, hildon_pannable_area, GTK_TYPE_BIN)
66 #define PANNABLE_AREA_PRIVATE(o) \
67 (G_TYPE_INSTANCE_GET_PRIVATE ((o), HILDON_TYPE_PANNABLE_AREA, \
68 HildonPannableAreaPrivate))
70 struct _HildonPannableAreaPrivate {
71 HildonPannableAreaMode mode;
72 HildonMovementMode mov_mode;
73 GdkWindow *event_window;
74 gdouble x; /* Used to store mouse co-ordinates of the first or */
75 gdouble y; /* previous events in a press-motion pair */
76 gdouble ex; /* Used to store mouse co-ordinates of the last */
77 gdouble ey; /* motion event in acceleration mode */
79 gboolean button_pressed;
80 guint32 last_time; /* Last event time, to stop infinite loops */
86 gdouble vmax_overshooting;
93 guint panning_threshold;
94 guint scrollbar_fade_delay;
97 guint direction_error_margin;
103 gint ix; /* Initial click mouse co-ordinates */
105 gint cx; /* Initial click child window mouse co-ordinates */
112 gint overshot_dist_x;
113 gint overshot_dist_y;
116 gdouble scroll_indicator_alpha;
117 gint motion_event_scroll_timeout;
118 gint scroll_indicator_timeout;
119 gint scroll_indicator_event_interrupt;
120 gint scroll_delay_counter;
123 gboolean initial_hint;
124 gboolean initial_effect;
125 gboolean low_friction_mode;
128 gboolean size_request_policy;
129 gboolean hscroll_visible;
130 gboolean vscroll_visible;
131 GdkRectangle hscroll_rect;
132 GdkRectangle vscroll_rect;
133 guint indicator_width;
135 GtkAdjustment *hadjust;
136 GtkAdjustment *vadjust;
140 GtkPolicyType vscrollbar_policy;
141 GtkPolicyType hscrollbar_policy;
143 GdkGC *scrollbars_gc;
144 GdkColor scroll_color;
156 static guint pannable_area_signals [LAST_SIGNAL] = { 0 };
164 PROP_VEL_MAX_OVERSHOOTING,
165 PROP_VELOCITY_FAST_FACTOR,
169 PROP_PANNING_THRESHOLD,
170 PROP_SCROLLBAR_FADE_DELAY,
173 PROP_DIRECTION_ERROR_MARGIN,
174 PROP_VSCROLLBAR_POLICY,
175 PROP_HSCROLLBAR_POLICY,
180 PROP_LOW_FRICTION_MODE,
181 PROP_SIZE_REQUEST_POLICY,
187 static void hildon_pannable_area_class_init (HildonPannableAreaClass * klass);
188 static void hildon_pannable_area_init (HildonPannableArea * area);
189 static void hildon_pannable_area_get_property (GObject * object,
193 static void hildon_pannable_area_set_property (GObject * object,
195 const GValue * value,
197 static void hildon_pannable_area_dispose (GObject * object);
198 static void hildon_pannable_area_realize (GtkWidget * widget);
199 static void hildon_pannable_area_unrealize (GtkWidget * widget);
200 static void hildon_pannable_area_size_request (GtkWidget * widget,
201 GtkRequisition * requisition);
202 static void hildon_pannable_area_size_allocate (GtkWidget * widget,
203 GtkAllocation * allocation);
204 static void hildon_pannable_area_child_allocate_calculate (GtkWidget * widget,
205 GtkAllocation * allocation,
206 GtkAllocation * child_allocation);
207 static void hildon_pannable_area_style_set (GtkWidget * widget,
208 GtkStyle * previous_style);
209 static void hildon_pannable_area_map (GtkWidget * widget);
210 static void hildon_pannable_area_unmap (GtkWidget * widget);
211 static void hildon_pannable_area_grab_notify (GtkWidget *widget,
212 gboolean was_grabbed,
214 #if USE_CAIRO_SCROLLBARS == 1
215 static void rgb_from_gdkcolor (GdkColor *color, gdouble *r, gdouble *g, gdouble *b);
216 #else /* USE_CAIRO_SCROLLBARS */
217 static void tranparency_color (GdkColor *color,
220 gdouble transparency);
221 #endif /* USE_CAIRO_SCROLLBARS */
222 static void hildon_pannable_draw_vscroll (GtkWidget * widget,
223 GdkColor *back_color,
224 GdkColor *scroll_color);
225 static void hildon_pannable_draw_hscroll (GtkWidget * widget,
226 GdkColor *back_color,
227 GdkColor *scroll_color);
228 static void hildon_pannable_area_initial_effect (GtkWidget * widget);
229 static void hildon_pannable_area_redraw (HildonPannableArea * area);
230 static void hildon_pannable_area_launch_fade_timeout (HildonPannableArea * area,
232 static void hildon_pannable_area_adjust_value_changed (HildonPannableArea * area,
234 static void hildon_pannable_area_adjust_changed (HildonPannableArea * area,
236 static gboolean hildon_pannable_area_scroll_indicator_fade(HildonPannableArea * area);
237 static gboolean hildon_pannable_area_expose_event (GtkWidget * widget,
238 GdkEventExpose * event);
239 static GdkWindow * hildon_pannable_area_get_topmost (GdkWindow * window,
241 gint * tx, gint * ty,
243 static void synth_crossing (GdkWindow * child,
245 gint x_root, gint y_root,
246 guint32 time, gboolean in);
247 static gboolean hildon_pannable_area_button_press_cb (GtkWidget * widget,
248 GdkEventButton * event);
249 static void hildon_pannable_area_refresh (HildonPannableArea * area);
250 static gboolean hildon_pannable_area_check_scrollbars (HildonPannableArea * area);
251 static void hildon_pannable_axis_scroll (HildonPannableArea *area,
252 GtkAdjustment *adjust,
260 static void hildon_pannable_area_scroll (HildonPannableArea *area,
261 gdouble x, gdouble y);
262 static gboolean hildon_pannable_area_timeout (HildonPannableArea * area);
263 static void hildon_pannable_area_calculate_velocity (gdouble *vel,
267 gdouble drag_inertia,
270 static gboolean hildon_pannable_area_motion_event_scroll_timeout (HildonPannableArea *area);
271 static void hildon_pannable_area_motion_event_scroll (HildonPannableArea *area,
272 gdouble x, gdouble y);
273 static void hildon_pannable_area_check_move (HildonPannableArea *area,
274 GdkEventMotion * event,
277 static void hildon_pannable_area_handle_move (HildonPannableArea *area,
278 GdkEventMotion * event,
281 static gboolean hildon_pannable_area_motion_notify_cb (GtkWidget * widget,
282 GdkEventMotion * event);
283 static gboolean hildon_pannable_leave_notify_event (GtkWidget *widget,
284 GdkEventCrossing *event);
285 static gboolean hildon_pannable_area_button_release_cb (GtkWidget * widget,
286 GdkEventButton * event);
287 static gboolean hildon_pannable_area_scroll_cb (GtkWidget *widget,
288 GdkEventScroll *event);
289 static void hildon_pannable_area_child_mapped (GtkWidget *widget,
292 static void hildon_pannable_area_add (GtkContainer *container, GtkWidget *child);
293 static void hildon_pannable_area_remove (GtkContainer *container, GtkWidget *child);
294 static void hildon_pannable_calculate_vel_factor (HildonPannableArea * self);
298 hildon_pannable_area_class_init (HildonPannableAreaClass * klass)
300 GObjectClass *object_class = G_OBJECT_CLASS (klass);
301 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
302 GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
305 g_type_class_add_private (klass, sizeof (HildonPannableAreaPrivate));
307 object_class->get_property = hildon_pannable_area_get_property;
308 object_class->set_property = hildon_pannable_area_set_property;
309 object_class->dispose = hildon_pannable_area_dispose;
311 widget_class->realize = hildon_pannable_area_realize;
312 widget_class->unrealize = hildon_pannable_area_unrealize;
313 widget_class->map = hildon_pannable_area_map;
314 widget_class->unmap = hildon_pannable_area_unmap;
315 widget_class->size_request = hildon_pannable_area_size_request;
316 widget_class->size_allocate = hildon_pannable_area_size_allocate;
317 widget_class->expose_event = hildon_pannable_area_expose_event;
318 widget_class->style_set = hildon_pannable_area_style_set;
319 widget_class->button_press_event = hildon_pannable_area_button_press_cb;
320 widget_class->button_release_event = hildon_pannable_area_button_release_cb;
321 widget_class->motion_notify_event = hildon_pannable_area_motion_notify_cb;
322 widget_class->leave_notify_event = hildon_pannable_leave_notify_event;
323 widget_class->scroll_event = hildon_pannable_area_scroll_cb;
325 container_class->add = hildon_pannable_area_add;
326 container_class->remove = hildon_pannable_area_remove;
328 klass->horizontal_movement = NULL;
329 klass->vertical_movement = NULL;
331 g_object_class_install_property (object_class,
333 g_param_spec_boolean ("enabled",
335 "Enable or disable finger-scroll.",
340 g_object_class_install_property (object_class,
341 PROP_VSCROLLBAR_POLICY,
342 g_param_spec_enum ("vscrollbar_policy",
344 "Visual policy of the vertical scrollbar",
345 GTK_TYPE_POLICY_TYPE,
346 GTK_POLICY_AUTOMATIC,
350 g_object_class_install_property (object_class,
351 PROP_HSCROLLBAR_POLICY,
352 g_param_spec_enum ("hscrollbar_policy",
354 "Visual policy of the horizontal scrollbar",
355 GTK_TYPE_POLICY_TYPE,
356 GTK_POLICY_AUTOMATIC,
360 g_object_class_install_property (object_class,
362 g_param_spec_enum ("mode",
364 "Change the finger-scrolling mode.",
365 HILDON_TYPE_PANNABLE_AREA_MODE,
366 HILDON_PANNABLE_AREA_MODE_AUTO,
370 g_object_class_install_property (object_class,
372 g_param_spec_flags ("mov_mode",
373 "Scroll movement mode",
374 "Controls if the widget can scroll vertically, horizontally or both",
375 HILDON_TYPE_MOVEMENT_MODE,
376 HILDON_MOVEMENT_MODE_VERT,
380 g_object_class_install_property (object_class,
382 g_param_spec_double ("velocity_min",
383 "Minimum scroll velocity",
384 "Minimum distance the child widget should scroll "
385 "per 'frame', in pixels per frame.",
390 g_object_class_install_property (object_class,
392 g_param_spec_double ("velocity_max",
393 "Maximum scroll velocity",
394 "Maximum distance the child widget should scroll "
395 "per 'frame', in pixels per frame.",
400 g_object_class_install_property (object_class,
401 PROP_VEL_MAX_OVERSHOOTING,
402 g_param_spec_double ("velocity_overshooting_max",
403 "Maximum scroll velocity when overshooting",
404 "Maximum distance the child widget should scroll "
405 "per 'frame', in pixels per frame when it overshoots after hitting the edge.",
410 g_object_class_install_property (object_class,
411 PROP_VELOCITY_FAST_FACTOR,
412 g_param_spec_double ("velocity_fast_factor",
413 "Fast velocity factor",
414 "Minimum velocity that is considered 'fast': "
415 "children widgets won't receive button presses. "
416 "Expressed as a fraction of the maximum velocity.",
421 g_object_class_install_property (object_class,
423 g_param_spec_double ("deceleration",
424 "Deceleration multiplier",
425 "The multiplier used when decelerating when in "
426 "acceleration scrolling mode.",
431 g_object_class_install_property (object_class,
433 g_param_spec_double ("drag_inertia",
434 "Inertia of the cursor dragging",
435 "Percentage of the calculated speed in each moment we are are going to use"
436 "to calculate the launch speed, the other part would be the speed"
437 "calculated previously",
442 g_object_class_install_property (object_class,
444 g_param_spec_uint ("sps",
445 "Scrolls per second",
446 "Amount of scroll events to generate per second.",
451 g_object_class_install_property (object_class,
452 PROP_PANNING_THRESHOLD,
453 g_param_spec_uint ("panning_threshold",
454 "Threshold to consider a motion event an scroll",
455 "Amount of pixels to consider a motion event an scroll, if it is less"
456 "it is a click detected incorrectly by the touch screen.",
461 g_object_class_install_property (object_class,
462 PROP_SCROLLBAR_FADE_DELAY,
463 g_param_spec_uint ("scrollbar_fade_delay",
464 "Time before starting to fade the scrollbar",
465 "Time the scrollbar is going to be visible if the widget is not in"
466 "action in miliseconds",
471 g_object_class_install_property (object_class,
473 g_param_spec_uint ("bounce_steps",
475 "Number of steps that is going to be used to bounce when hitting the"
476 "edge, the rubberband effect depends on it",
481 g_object_class_install_property (object_class,
483 g_param_spec_uint ("force",
484 "Multiplier of the calculated speed",
485 "Force applied to the movement, multiplies the calculated speed of the"
486 "user movement the cursor in the screen",
491 g_object_class_install_property (object_class,
492 PROP_DIRECTION_ERROR_MARGIN,
493 g_param_spec_uint ("direction_error_margin",
494 "Margin in the direction detection",
495 "After detecting the direction of the movement (horizontal or"
496 "vertical), we can add this margin of error to allow the movement in"
497 "the other direction even apparently it is not",
502 g_object_class_install_property (object_class,
504 g_param_spec_int ("vovershoot_max",
505 "Vertical overshoot distance",
506 "Space we allow the widget to pass over its vertical limits when"
507 "hitting the edges, set 0 in order to deactivate overshooting.",
512 g_object_class_install_property (object_class,
514 g_param_spec_int ("hovershoot_max",
515 "Horizontal overshoot distance",
516 "Space we allow the widget to pass over its horizontal limits when"
517 "hitting the edges, set 0 in order to deactivate overshooting.",
522 g_object_class_install_property (object_class,
524 g_param_spec_double ("scroll_time",
525 "Time to scroll to a position",
526 "The time to scroll to a position when calling the hildon_pannable_scroll_to function",
531 g_object_class_install_property (object_class,
533 g_param_spec_boolean ("initial-hint",
535 "Whether to hint the user about the pannability of the container.",
540 g_object_class_install_property (object_class,
541 PROP_LOW_FRICTION_MODE,
542 g_param_spec_boolean ("low-friction-mode",
543 "Do not decelerate the initial velocity",
544 "Avoid decelerating the panning movement, like no friction, the widget"
545 "will stop in the edges or if the user clicks.",
550 g_object_class_install_property (object_class,
551 PROP_SIZE_REQUEST_POLICY,
552 g_param_spec_enum ("size-request-policy",
553 "Size Requisition policy",
554 "Controls the size request policy of the widget",
555 HILDON_TYPE_SIZE_REQUEST_POLICY,
556 HILDON_SIZE_REQUEST_MINIMUM,
560 g_object_class_install_property (object_class,
562 g_param_spec_object ("hadjustment",
563 "Horizontal Adjustment",
564 "The GtkAdjustment for the horizontal position",
567 g_object_class_install_property (object_class,
569 g_param_spec_object ("vadjustment",
570 "Vertical Adjustment",
571 "The GtkAdjustment for the vertical position",
575 gtk_widget_class_install_style_property (widget_class,
578 "Width of the scroll indicators",
579 "Pixel width used to draw the scroll indicators.",
584 * HildonPannableArea::horizontal-movement:
585 * @hildonpannable: the object which received the signal
586 * @direction: the direction of the movement #HILDON_MOVEMENT_LEFT or #HILDON_MOVEMENT_RIGHT
587 * @initial_x: the x coordinate of the point where the user clicked to start the movement
588 * @initial_y: the y coordinate of the point where the user clicked to start the movement
590 * The horizontal-movement signal is emitted when the pannable area
591 * detects a horizontal movement. The detection does not mean the
592 * widget is going to move (i.e. maybe the children are smaller
593 * horizontally than the screen).
597 pannable_area_signals[HORIZONTAL_MOVEMENT] =
598 g_signal_new ("horizontal_movement",
599 G_TYPE_FROM_CLASS (object_class),
600 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
601 G_STRUCT_OFFSET (HildonPannableAreaClass, horizontal_movement),
603 _hildon_marshal_VOID__INT_DOUBLE_DOUBLE,
610 * HildonPannableArea::vertical-movement:
611 * @hildonpannable: the object which received the signal
612 * @direction: the direction of the movement #HILDON_MOVEMENT_UP or #HILDON_MOVEMENT_DOWN
613 * @initial_x: the x coordinate of the point where the user clicked to start the movement
614 * @initial_y: the y coordinate of the point where the user clicked to start the movement
616 * The vertical-movement signal is emitted when the pannable area
617 * detects a vertical movement. The detection does not mean the
618 * widget is going to move (i.e. maybe the children are smaller
619 * vertically than the screen).
623 pannable_area_signals[VERTICAL_MOVEMENT] =
624 g_signal_new ("vertical_movement",
625 G_TYPE_FROM_CLASS (object_class),
626 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
627 G_STRUCT_OFFSET (HildonPannableAreaClass, vertical_movement),
629 _hildon_marshal_VOID__INT_DOUBLE_DOUBLE,
636 * HildonPannableArea::panning-started:
637 * @hildonpannable: the pannable area object that is going to start
640 * This signal is emitted before the panning starts. Applications
641 * can return %TRUE to avoid the panning. The main difference with
642 * the vertical-movement and horizontal-movement signals is those
643 * gesture signals are launched no matter if the widget is going to
644 * move, this signal means the widget is going to start moving. It
645 * could even happen that the widget moves and there was no gesture
646 * (i.e. click meanwhile the pannable is overshooting).
648 * Returns: %TRUE to stop the panning launch. %FALSE to continue
653 pannable_area_signals[PANNING_STARTED] =
654 g_signal_new ("panning-started",
655 G_TYPE_FROM_CLASS (object_class),
659 _hildon_marshal_BOOLEAN__VOID,
663 * HildonPannableArea::panning-finished:
664 * @hildonpannable: the pannable area object that finished the
667 * This signal is emitted after the kinetic panning has
672 pannable_area_signals[PANNING_FINISHED] =
673 g_signal_new ("panning-finished",
674 G_TYPE_FROM_CLASS (object_class),
678 _hildon_marshal_VOID__VOID,
684 hildon_pannable_area_init (HildonPannableArea * area)
686 HildonPannableAreaPrivate *priv = PANNABLE_AREA_PRIVATE (area);
688 GTK_WIDGET_UNSET_FLAGS (area, GTK_NO_WINDOW);
693 priv->button_pressed = FALSE;
696 priv->vscroll_visible = TRUE;
697 priv->hscroll_visible = TRUE;
698 priv->indicator_width = 6;
699 priv->overshot_dist_x = 0;
700 priv->overshot_dist_y = 0;
701 priv->overshooting_y = 0;
702 priv->overshooting_x = 0;
706 priv->scroll_indicator_alpha = 0.0;
707 priv->scroll_indicator_timeout = 0;
708 priv->motion_event_scroll_timeout = 0;
709 priv->scroll_indicator_event_interrupt = 0;
710 priv->scroll_delay_counter = 0;
711 priv->scrollbar_fade_delay = 0;
712 priv->scroll_to_x = -1;
713 priv->scroll_to_y = -1;
714 priv->first_drag = TRUE;
715 priv->initial_effect = TRUE;
716 priv->child_width = 0;
717 priv->child_height = 0;
718 priv->last_in = TRUE;
722 gtk_style_lookup_color (GTK_WIDGET (area)->style,
723 "SecondaryTextColor", &priv->scroll_color);
726 GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
728 GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
730 g_object_ref_sink (G_OBJECT (priv->hadjust));
731 g_object_ref_sink (G_OBJECT (priv->vadjust));
733 g_signal_connect_swapped (priv->hadjust, "value-changed",
734 G_CALLBACK (hildon_pannable_area_adjust_value_changed), area);
735 g_signal_connect_swapped (priv->vadjust, "value-changed",
736 G_CALLBACK (hildon_pannable_area_adjust_value_changed), area);
737 g_signal_connect_swapped (priv->hadjust, "changed",
738 G_CALLBACK (hildon_pannable_area_adjust_changed), area);
739 g_signal_connect_swapped (priv->vadjust, "changed",
740 G_CALLBACK (hildon_pannable_area_adjust_changed), area);
741 g_signal_connect (area, "grab-notify",
742 G_CALLBACK (hildon_pannable_area_grab_notify), NULL);
746 hildon_pannable_area_get_property (GObject * object,
751 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (object)->priv;
753 switch (property_id) {
755 g_value_set_boolean (value, priv->enabled);
758 g_value_set_enum (value, priv->mode);
760 case PROP_MOVEMENT_MODE:
761 g_value_set_flags (value, priv->mov_mode);
763 case PROP_VELOCITY_MIN:
764 g_value_set_double (value, priv->vmin);
766 case PROP_VELOCITY_MAX:
767 g_value_set_double (value, priv->vmax);
769 case PROP_VEL_MAX_OVERSHOOTING:
770 g_value_set_double (value, priv->vmax_overshooting);
772 case PROP_VELOCITY_FAST_FACTOR:
773 g_value_set_double (value, priv->vfast_factor);
775 case PROP_DECELERATION:
776 g_value_set_double (value, priv->decel);
778 case PROP_DRAG_INERTIA:
779 g_value_set_double (value, priv->drag_inertia);
782 g_value_set_uint (value, priv->sps);
784 case PROP_PANNING_THRESHOLD:
785 g_value_set_uint (value, priv->panning_threshold);
787 case PROP_SCROLLBAR_FADE_DELAY:
788 /* convert to miliseconds */
789 g_value_set_uint (value, priv->scrollbar_fade_delay * SCROLL_FADE_TIMEOUT);
791 case PROP_BOUNCE_STEPS:
792 g_value_set_uint (value, priv->bounce_steps);
795 g_value_set_uint (value, priv->force);
797 case PROP_DIRECTION_ERROR_MARGIN:
798 g_value_set_uint (value, priv->direction_error_margin);
800 case PROP_VSCROLLBAR_POLICY:
801 g_value_set_enum (value, priv->vscrollbar_policy);
803 case PROP_HSCROLLBAR_POLICY:
804 g_value_set_enum (value, priv->hscrollbar_policy);
806 case PROP_VOVERSHOOT_MAX:
807 g_value_set_int (value, priv->vovershoot_max);
809 case PROP_HOVERSHOOT_MAX:
810 g_value_set_int (value, priv->hovershoot_max);
812 case PROP_SCROLL_TIME:
813 g_value_set_double (value, priv->scroll_time);
815 case PROP_INITIAL_HINT:
816 g_value_set_boolean (value, priv->initial_hint);
818 case PROP_LOW_FRICTION_MODE:
819 g_value_set_boolean (value, priv->low_friction_mode);
821 case PROP_SIZE_REQUEST_POLICY:
822 g_value_set_enum (value, priv->size_request_policy);
824 case PROP_HADJUSTMENT:
825 g_value_set_object (value,
826 hildon_pannable_area_get_hadjustment
827 (HILDON_PANNABLE_AREA (object)));
829 case PROP_VADJUSTMENT:
830 g_value_set_object (value,
831 hildon_pannable_area_get_vadjustment
832 (HILDON_PANNABLE_AREA (object)));
835 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
840 hildon_pannable_area_set_property (GObject * object,
842 const GValue * value,
845 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (object)->priv;
848 switch (property_id) {
850 enabled = g_value_get_boolean (value);
852 if ((priv->enabled != enabled) && (GTK_WIDGET_REALIZED (object))) {
854 gdk_window_raise (priv->event_window);
856 gdk_window_lower (priv->event_window);
859 priv->enabled = enabled;
862 priv->mode = g_value_get_enum (value);
864 case PROP_MOVEMENT_MODE:
865 priv->mov_mode = g_value_get_flags (value);
867 case PROP_VELOCITY_MIN:
868 priv->vmin = g_value_get_double (value);
870 case PROP_VELOCITY_MAX:
871 priv->vmax = g_value_get_double (value);
873 case PROP_VEL_MAX_OVERSHOOTING:
874 priv->vmax_overshooting = g_value_get_double (value);
876 case PROP_VELOCITY_FAST_FACTOR:
877 priv->vfast_factor = g_value_get_double (value);
879 case PROP_DECELERATION:
880 priv->decel = g_value_get_double (value);
881 hildon_pannable_calculate_vel_factor (HILDON_PANNABLE_AREA (object));
883 case PROP_DRAG_INERTIA:
884 priv->drag_inertia = g_value_get_double (value);
887 priv->sps = g_value_get_uint (value);
889 case PROP_PANNING_THRESHOLD:
891 GtkSettings *settings = gtk_settings_get_default ();
892 GtkSettingsValue svalue = { NULL, { 0, }, };
894 priv->panning_threshold = g_value_get_uint (value);
896 /* insure gtk dnd is the same we are using, not allowed
897 different thresholds in the same application */
898 svalue.origin = "panning_threshold";
899 g_value_init (&svalue.value, G_TYPE_LONG);
900 g_value_set_long (&svalue.value, priv->panning_threshold);
901 gtk_settings_set_property_value (settings, "gtk-dnd-drag-threshold", &svalue);
902 g_value_unset (&svalue.value);
905 case PROP_SCROLLBAR_FADE_DELAY:
906 /* convert to miliseconds */
907 priv->scrollbar_fade_delay = g_value_get_uint (value)/(SCROLL_FADE_TIMEOUT);
909 case PROP_BOUNCE_STEPS:
910 priv->bounce_steps = g_value_get_uint (value);
913 priv->force = g_value_get_uint (value);
915 case PROP_DIRECTION_ERROR_MARGIN:
916 priv->direction_error_margin = g_value_get_uint (value);
918 case PROP_VSCROLLBAR_POLICY:
919 priv->vscrollbar_policy = g_value_get_enum (value);
921 gtk_widget_queue_resize (GTK_WIDGET (object));
923 case PROP_HSCROLLBAR_POLICY:
924 priv->hscrollbar_policy = g_value_get_enum (value);
926 gtk_widget_queue_resize (GTK_WIDGET (object));
928 case PROP_VOVERSHOOT_MAX:
929 priv->vovershoot_max = g_value_get_int (value);
931 case PROP_HOVERSHOOT_MAX:
932 priv->hovershoot_max = g_value_get_int (value);
934 case PROP_SCROLL_TIME:
935 priv->scroll_time = g_value_get_double (value);
937 hildon_pannable_calculate_vel_factor (HILDON_PANNABLE_AREA (object));
939 case PROP_INITIAL_HINT:
940 priv->initial_hint = g_value_get_boolean (value);
942 case PROP_LOW_FRICTION_MODE:
943 priv->low_friction_mode = g_value_get_boolean (value);
945 case PROP_SIZE_REQUEST_POLICY:
946 hildon_pannable_area_set_size_request_policy (HILDON_PANNABLE_AREA (object),
947 g_value_get_enum (value));
951 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
956 hildon_pannable_area_dispose (GObject * object)
958 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (object)->priv;
959 GtkWidget *child = gtk_bin_get_child (GTK_BIN (object));
962 g_signal_emit (object, pannable_area_signals[PANNING_FINISHED], 0);
963 g_source_remove (priv->idle_id);
967 if (priv->scroll_indicator_timeout){
968 g_source_remove (priv->scroll_indicator_timeout);
969 priv->scroll_indicator_timeout = 0;
972 if (priv->motion_event_scroll_timeout){
973 g_source_remove (priv->motion_event_scroll_timeout);
974 priv->motion_event_scroll_timeout = 0;
978 g_signal_handlers_disconnect_by_func (child,
979 hildon_pannable_area_child_mapped,
983 g_signal_handlers_disconnect_by_func (object,
984 hildon_pannable_area_grab_notify,
988 g_signal_handlers_disconnect_by_func (priv->hadjust,
989 hildon_pannable_area_adjust_value_changed,
991 g_signal_handlers_disconnect_by_func (priv->hadjust,
992 hildon_pannable_area_adjust_changed,
994 g_object_unref (priv->hadjust);
995 priv->hadjust = NULL;
999 g_signal_handlers_disconnect_by_func (priv->vadjust,
1000 hildon_pannable_area_adjust_value_changed,
1002 g_signal_handlers_disconnect_by_func (priv->vadjust,
1003 hildon_pannable_area_adjust_changed,
1005 g_object_unref (priv->vadjust);
1006 priv->vadjust = NULL;
1009 if (G_OBJECT_CLASS (hildon_pannable_area_parent_class)->dispose)
1010 G_OBJECT_CLASS (hildon_pannable_area_parent_class)->dispose (object);
1014 hildon_pannable_area_realize (GtkWidget * widget)
1016 GdkWindowAttr attributes;
1017 gint attributes_mask;
1019 HildonPannableAreaPrivate *priv;
1021 priv = HILDON_PANNABLE_AREA (widget)->priv;
1023 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
1025 border_width = GTK_CONTAINER (widget)->border_width;
1027 attributes.x = widget->allocation.x + border_width;
1028 attributes.y = widget->allocation.y + border_width;
1029 attributes.width = MAX (widget->allocation.width - 2 * border_width, 0);
1030 attributes.height = MAX (widget->allocation.height - 2 * border_width, 0);
1031 attributes.window_type = GDK_WINDOW_CHILD;
1033 /* avoid using the hildon_window */
1034 attributes.visual = gtk_widget_get_visual (widget);
1035 attributes.colormap = gtk_widget_get_colormap (widget);
1036 attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK;
1037 attributes.wclass = GDK_INPUT_OUTPUT;
1039 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1041 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
1042 &attributes, attributes_mask);
1043 gdk_window_set_user_data (widget->window, widget);
1045 /* create the events window */
1048 attributes.event_mask = gtk_widget_get_events (widget)
1049 | GDK_BUTTON_MOTION_MASK
1050 | GDK_BUTTON_PRESS_MASK
1051 | GDK_BUTTON_RELEASE_MASK
1053 | GDK_POINTER_MOTION_HINT_MASK
1054 | GDK_EXPOSURE_MASK | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK;
1055 attributes.wclass = GDK_INPUT_ONLY;
1057 attributes_mask = GDK_WA_X | GDK_WA_Y;
1059 priv->event_window = gdk_window_new (widget->window,
1060 &attributes, attributes_mask);
1061 gdk_window_set_user_data (priv->event_window, widget);
1063 widget->style = gtk_style_attach (widget->style, widget->window);
1064 gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
1066 priv->scrollbars_gc = gdk_gc_new (GDK_DRAWABLE (widget->window));
1067 gdk_gc_copy (priv->scrollbars_gc, widget->style->fg_gc[GTK_STATE_INSENSITIVE]);
1071 hildon_pannable_area_unrealize (GtkWidget * widget)
1073 HildonPannableAreaPrivate *priv;
1075 priv = HILDON_PANNABLE_AREA (widget)->priv;
1077 if (priv->event_window != NULL) {
1078 gdk_window_set_user_data (priv->event_window, NULL);
1079 gdk_window_destroy (priv->event_window);
1080 priv->event_window = NULL;
1083 gdk_gc_unref (priv->scrollbars_gc);
1085 if (GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->unrealize)
1086 (*GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->unrealize)(widget);
1090 hildon_pannable_area_size_request (GtkWidget * widget,
1091 GtkRequisition * requisition)
1093 GtkRequisition child_requisition = {0};
1094 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1095 GtkWidget *child = gtk_bin_get_child (GTK_BIN (widget));
1097 if (child && GTK_WIDGET_VISIBLE (child))
1099 gtk_widget_size_request (child, &child_requisition);
1102 if (priv->hscrollbar_policy == GTK_POLICY_NEVER) {
1103 requisition->width = child_requisition.width;
1105 switch (priv->size_request_policy) {
1106 case HILDON_SIZE_REQUEST_CHILDREN:
1107 requisition->width = MIN (PANNABLE_MAX_WIDTH,
1108 child_requisition.width);
1110 case HILDON_SIZE_REQUEST_MINIMUM:
1112 requisition->width = priv->indicator_width;
1116 if (priv->vscrollbar_policy == GTK_POLICY_NEVER) {
1117 requisition->height = child_requisition.height;
1119 switch (priv->size_request_policy) {
1120 case HILDON_SIZE_REQUEST_CHILDREN:
1121 requisition->height = MIN (PANNABLE_MAX_HEIGHT,
1122 child_requisition.height);
1124 case HILDON_SIZE_REQUEST_MINIMUM:
1126 requisition->height = priv->indicator_width;
1130 requisition->width += 2 * GTK_CONTAINER (widget)->border_width;
1131 requisition->height += 2 * GTK_CONTAINER (widget)->border_width;
1135 hildon_pannable_area_child_allocate_calculate (GtkWidget * widget,
1136 GtkAllocation * allocation,
1137 GtkAllocation * child_allocation)
1140 HildonPannableAreaPrivate *priv;
1142 border_width = GTK_CONTAINER (widget)->border_width;
1144 priv = HILDON_PANNABLE_AREA (widget)->priv;
1146 child_allocation->x = 0;
1147 child_allocation->y = 0;
1148 child_allocation->width = MAX (allocation->width - 2 * border_width -
1149 (priv->vscroll_visible ? priv->vscroll_rect.width : 0), 0);
1150 child_allocation->height = MAX (allocation->height - 2 * border_width -
1151 (priv->hscroll_visible ? priv->hscroll_rect.height : 0), 0);
1153 if (priv->overshot_dist_y > 0) {
1154 child_allocation->y = MIN (child_allocation->y + priv->overshot_dist_y,
1155 child_allocation->height);
1156 child_allocation->height = MAX (child_allocation->height - priv->overshot_dist_y, 0);
1157 } else if (priv->overshot_dist_y < 0) {
1158 child_allocation->height = MAX (child_allocation->height + priv->overshot_dist_y, 0);
1161 if (priv->overshot_dist_x > 0) {
1162 child_allocation->x = MIN (child_allocation->x + priv->overshot_dist_x,
1163 child_allocation->width);
1164 child_allocation->width = MAX (child_allocation->width - priv->overshot_dist_x, 0);
1165 } else if (priv->overshot_dist_x < 0) {
1166 child_allocation->width = MAX (child_allocation->width + priv->overshot_dist_x, 0);
1171 hildon_pannable_area_size_allocate (GtkWidget * widget,
1172 GtkAllocation * allocation)
1174 GtkAllocation child_allocation;
1175 HildonPannableAreaPrivate *priv;
1176 GtkWidget *child = gtk_bin_get_child (GTK_BIN (widget));
1180 border_width = GTK_CONTAINER (widget)->border_width;
1182 widget->allocation = *allocation;
1184 priv = HILDON_PANNABLE_AREA (widget)->priv;
1186 if (GTK_WIDGET_REALIZED (widget)) {
1187 gdk_window_move_resize (widget->window,
1188 allocation->x + border_width,
1189 allocation->y + border_width,
1190 allocation->width - border_width * 2,
1191 allocation->height - border_width * 2);
1192 gdk_window_move_resize (priv->event_window,
1195 allocation->width - border_width * 2,
1196 allocation->height - border_width * 2);
1199 if (child && GTK_WIDGET_VISIBLE (child)) {
1201 hildon_pannable_area_child_allocate_calculate (widget,
1205 gtk_widget_size_allocate (child, &child_allocation);
1207 if (hildon_pannable_area_check_scrollbars (HILDON_PANNABLE_AREA (widget))) {
1208 hildon_pannable_area_child_allocate_calculate (widget,
1212 gtk_widget_size_allocate (child, &child_allocation);
1215 hv = priv->hadjust->value;
1216 vv = priv->vadjust->value;
1218 /* we have to do this after child size_allocate because page_size is
1219 * changed when we allocate the size of the children */
1220 if (priv->overshot_dist_y < 0) {
1221 priv->vadjust->value = priv->vadjust->upper - priv->vadjust->page_size;
1224 if (priv->overshot_dist_x < 0) {
1225 priv->hadjust->value = priv->hadjust->upper - priv->hadjust->page_size;
1228 if (hv != priv->hadjust->value)
1229 gtk_adjustment_value_changed (priv->hadjust);
1231 if (vv != priv->vadjust->value)
1232 gtk_adjustment_value_changed (priv->vadjust);
1235 hildon_pannable_area_check_scrollbars (HILDON_PANNABLE_AREA (widget));
1240 hildon_pannable_area_style_set (GtkWidget * widget,
1241 GtkStyle * previous_style)
1243 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1245 GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->
1246 style_set (widget, previous_style);
1248 gtk_style_lookup_color (widget->style, "SecondaryTextColor", &priv->scroll_color);
1249 gtk_widget_style_get (widget, "indicator-width", &priv->indicator_width, NULL);
1253 hildon_pannable_area_map (GtkWidget * widget)
1255 HildonPannableAreaPrivate *priv;
1257 priv = HILDON_PANNABLE_AREA (widget)->priv;
1259 gdk_window_show (widget->window);
1261 if (priv->event_window != NULL && !priv->enabled)
1262 gdk_window_show (priv->event_window);
1264 (*GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->map) (widget);
1266 if (priv->event_window != NULL && priv->enabled)
1267 gdk_window_show (priv->event_window);
1271 hildon_pannable_area_unmap (GtkWidget * widget)
1273 HildonPannableAreaPrivate *priv;
1275 priv = HILDON_PANNABLE_AREA (widget)->priv;
1277 if (priv->event_window != NULL)
1278 gdk_window_hide (priv->event_window);
1280 gdk_window_hide (widget->window);
1282 (*GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->unmap) (widget);
1286 hildon_pannable_area_grab_notify (GtkWidget *widget,
1287 gboolean was_grabbed,
1290 /* an internal widget has grabbed the focus and now has returned it,
1291 we have to do some release actions */
1293 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1295 priv->scroll_indicator_event_interrupt = 0;
1297 if ((!priv->scroll_indicator_timeout)&&(priv->scroll_indicator_alpha)>0.1) {
1298 priv->scroll_delay_counter = priv->scrollbar_fade_delay;
1300 hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget),
1301 priv->scroll_indicator_alpha);
1304 priv->last_type = 3;
1305 priv->moved = FALSE;
1309 #if USE_CAIRO_SCROLLBARS == 1
1312 rgb_from_gdkcolor (GdkColor *color, gdouble *r, gdouble *g, gdouble *b)
1314 *r = (color->red >> 8) / 255.0;
1315 *g = (color->green >> 8) / 255.0;
1316 *b = (color->blue >> 8) / 255.0;
1320 hildon_pannable_draw_vscroll (GtkWidget * widget,
1321 GdkColor *back_color,
1322 GdkColor *scroll_color)
1324 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1327 cairo_pattern_t *pattern;
1329 gint radius = (priv->vscroll_rect.width/2) - 1;
1331 cr = gdk_cairo_create(widget->window);
1333 /* Draw the background */
1334 rgb_from_gdkcolor (back_color, &r, &g, &b);
1335 cairo_set_source_rgb (cr, r, g, b);
1336 cairo_rectangle(cr, priv->vscroll_rect.x, priv->vscroll_rect.y,
1337 priv->vscroll_rect.width,
1338 priv->vscroll_rect.height);
1339 cairo_fill_preserve (cr);
1342 /* Calculate the scroll bar height and position */
1343 y = ((priv->vadjust->value - priv->vadjust->lower) / (priv->vadjust->upper - priv->vadjust->lower)) *
1344 (widget->allocation.height -
1345 (priv->hscroll_visible ? priv->indicator_width : 0));
1346 height = ((((priv->vadjust->value - priv->vadjust->lower) +
1347 priv->vadjust->page_size) /
1348 (priv->vadjust->upper - priv->vadjust->lower)) *
1349 (widget->allocation.height -
1350 (priv->hscroll_visible ? priv->indicator_width : 0))) - y;
1352 /* Set a minimum height */
1353 height = MAX (SCROLL_BAR_MIN_SIZE, height);
1355 /* Check the max y position */
1356 y = MIN (y, widget->allocation.height -
1357 (priv->hscroll_visible ? priv->hscroll_rect.height : 0) -
1360 /* Draw the scrollbar */
1361 rgb_from_gdkcolor (scroll_color, &r, &g, &b);
1363 pattern = cairo_pattern_create_linear(radius+1, y, radius+1,y + height);
1364 cairo_pattern_add_color_stop_rgb(pattern, 0, r, g, b);
1365 cairo_pattern_add_color_stop_rgb(pattern, 1, r/2, g/2, b/2);
1366 cairo_set_source(cr, pattern);
1368 cairo_pattern_destroy(pattern);
1370 cairo_arc(cr, priv->vscroll_rect.x + radius + 1, y + radius + 1, radius, G_PI, 0);
1371 cairo_line_to(cr, priv->vscroll_rect.x + (radius * 2) + 1, y + height - radius);
1372 cairo_arc(cr, priv->vscroll_rect.x + radius + 1, y + height - radius, radius, 0, G_PI);
1373 cairo_line_to(cr, priv->vscroll_rect.x + 1, y + height - radius);
1376 cairo_paint_with_alpha(cr, priv->scroll_indicator_alpha);
1382 hildon_pannable_draw_hscroll (GtkWidget * widget,
1383 GdkColor *back_color,
1384 GdkColor *scroll_color)
1386 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1389 cairo_pattern_t *pattern;
1391 gint radius = (priv->hscroll_rect.height/2) - 1;
1393 cr = gdk_cairo_create(widget->window);
1395 /* Draw the background */
1396 rgb_from_gdkcolor (back_color, &r, &g, &b);
1397 cairo_set_source_rgb (cr, r, g, b);
1398 cairo_rectangle(cr, priv->hscroll_rect.x, priv->hscroll_rect.y,
1399 priv->hscroll_rect.width,
1400 priv->hscroll_rect.height);
1401 cairo_fill_preserve (cr);
1404 /* calculate the scrollbar width and position */
1405 x = ((priv->hadjust->value - priv->hadjust->lower) / (priv->hadjust->upper - priv->hadjust->lower)) *
1406 (widget->allocation.width - (priv->vscroll_visible ? priv->indicator_width : 0));
1407 width =((((priv->hadjust->value - priv->hadjust->lower) +
1408 priv->hadjust->page_size) / (priv->hadjust->upper - priv->hadjust->lower)) *
1409 (widget->allocation.width -
1410 (priv->vscroll_visible ? priv->indicator_width : 0))) - x;
1412 /* Set a minimum width */
1413 width = MAX (SCROLL_BAR_MIN_SIZE, width);
1415 /* Check the max x position */
1416 x = MIN (x, widget->allocation.width -
1417 (priv->vscroll_visible ? priv->vscroll_rect.width : 0) -
1420 /* Draw the scrollbar */
1421 rgb_from_gdkcolor (scroll_color, &r, &g, &b);
1423 pattern = cairo_pattern_create_linear(x, radius+1, x+width, radius+1);
1424 cairo_pattern_add_color_stop_rgb(pattern, 0, r, g, b);
1425 cairo_pattern_add_color_stop_rgb(pattern, 1, r/2, g/2, b/2);
1426 cairo_set_source(cr, pattern);
1428 cairo_pattern_destroy(pattern);
1430 cairo_arc_negative(cr, x + radius + 1, priv->hscroll_rect.y + radius + 1, radius, 3*G_PI_2, G_PI_2);
1431 cairo_line_to(cr, x + width - radius, priv->hscroll_rect.y + (radius * 2) + 1);
1432 cairo_arc_negative(cr, x + width - radius, priv->hscroll_rect.y + radius + 1, radius, G_PI_2, 3*G_PI_2);
1433 cairo_line_to(cr, x + width - radius, priv->hscroll_rect.y + 1);
1436 cairo_paint_with_alpha(cr, priv->scroll_indicator_alpha);
1441 #else /* USE_CAIRO_SCROLLBARS */
1444 tranparency_color (GdkColor *color,
1447 gdouble transparency)
1451 diff = colora.red - colorb.red;
1452 color->red = colora.red-diff*transparency;
1454 diff = colora.green - colorb.green;
1455 color->green = colora.green-diff*transparency;
1457 diff = colora.blue - colorb.blue;
1458 color->blue = colora.blue-diff*transparency;
1462 hildon_pannable_draw_vscroll (GtkWidget *widget,
1463 GdkColor *back_color,
1464 GdkColor *scroll_color)
1466 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1468 GdkColor transp_color;
1469 GdkGC *gc = priv->scrollbars_gc;
1471 gdk_draw_rectangle (widget->window,
1472 widget->style->bg_gc[GTK_STATE_NORMAL],
1474 priv->vscroll_rect.x, priv->vscroll_rect.y,
1475 priv->vscroll_rect.width,
1476 priv->vscroll_rect.height);
1478 y = ((priv->vadjust->value - priv->vadjust->lower) / (priv->vadjust->upper - priv->vadjust->lower)) *
1479 (widget->allocation.height - (priv->hscroll_visible ? priv->indicator_width : 0));
1480 height = ((((priv->vadjust->value - priv->vadjust->lower) + priv->vadjust->page_size) /
1481 (priv->vadjust->upper - priv->vadjust->lower)) *
1482 (widget->allocation.height -
1483 (priv->hscroll_visible ? priv->indicator_width : 0))) - y;
1485 /* Set a minimum height */
1486 height = MAX (SCROLL_BAR_MIN_SIZE, height);
1488 /* Check the max y position */
1489 y = MIN (y, widget->allocation.height -
1490 (priv->hscroll_visible ? priv->hscroll_rect.height : 0) -
1493 if (priv->scroll_indicator_alpha == 1.0) {
1494 transp_color = priv->scroll_color;
1495 } else if (priv->scroll_indicator_alpha < 1.0) {
1496 tranparency_color (&transp_color, *back_color, *scroll_color,
1497 priv->scroll_indicator_alpha);
1499 gdk_gc_set_rgb_fg_color (gc, &transp_color);
1501 gdk_draw_rectangle (widget->window, gc,
1502 TRUE, priv->vscroll_rect.x, y,
1503 priv->vscroll_rect.width, height);
1507 hildon_pannable_draw_hscroll (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_INSENSITIVE],
1519 priv->hscroll_rect.x, priv->hscroll_rect.y,
1520 priv->hscroll_rect.width,
1521 priv->hscroll_rect.height);
1523 /* calculate the scrollbar width and position */
1524 x = ((priv->hadjust->value - priv->hadjust->lower) / (priv->hadjust->upper - priv->hadjust->lower)) *
1525 (widget->allocation.width - (priv->vscroll_visible ? priv->indicator_width : 0));
1526 width =((((priv->hadjust->value - priv->hadjust->lower) +
1527 priv->hadjust->page_size) / (priv->hadjust->upper - priv->hadjust->lower)) *
1528 (widget->allocation.width -
1529 (priv->vscroll_visible ? priv->indicator_width : 0))) - x;
1531 /* Set a minimum width */
1532 width = MAX (SCROLL_BAR_MIN_SIZE, width);
1534 /* Check the max x position */
1535 x = MIN (x, widget->allocation.width -
1536 (priv->vscroll_visible ? priv->vscroll_rect.width : 0) -
1539 if (priv->scroll_indicator_alpha == 1.0) {
1540 transp_color = priv->scroll_color;
1541 } else if (priv->scroll_indicator_alpha < 1.0) {
1542 tranparency_color (&transp_color, *back_color, *scroll_color,
1543 priv->scroll_indicator_alpha);
1545 gdk_gc_set_rgb_fg_color (gc, &transp_color);
1547 gdk_draw_rectangle (widget->window, gc,
1548 TRUE, x, priv->hscroll_rect.y, width,
1549 priv->hscroll_rect.height);
1552 #endif /* USE_CAIRO_SCROLLBARS */
1555 hildon_pannable_area_initial_effect (GtkWidget * widget)
1557 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1559 if (priv->initial_hint) {
1560 if (priv->vscroll_visible || priv->hscroll_visible) {
1562 priv->scroll_indicator_event_interrupt = 0;
1563 priv->scroll_delay_counter = priv->scrollbar_fade_delay;
1565 hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget), 1.0);
1571 hildon_pannable_area_launch_fade_timeout (HildonPannableArea * area,
1574 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (area)->priv;
1576 priv->scroll_indicator_alpha = alpha;
1578 if (!priv->scroll_indicator_timeout)
1579 priv->scroll_indicator_timeout =
1580 gdk_threads_add_timeout (SCROLL_FADE_TIMEOUT,
1581 (GSourceFunc) hildon_pannable_area_scroll_indicator_fade,
1586 hildon_pannable_area_adjust_changed (HildonPannableArea * area,
1589 if (GTK_WIDGET_REALIZED (area))
1590 hildon_pannable_area_refresh (area);
1594 hildon_pannable_area_adjust_value_changed (HildonPannableArea * area,
1597 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (area)->priv;
1599 gint x = priv->x_offset;
1600 gint y = priv->y_offset;
1602 priv->x_offset = priv->hadjust->value;
1603 xdiff = x - priv->x_offset;
1604 priv->y_offset = priv->vadjust->value;
1605 ydiff = y - priv->y_offset;
1607 if ((xdiff || ydiff) && GTK_WIDGET_DRAWABLE (area)) {
1608 hildon_pannable_area_redraw (area);
1610 if ((priv->vscroll_visible) || (priv->hscroll_visible)) {
1611 priv->scroll_indicator_event_interrupt = 0;
1612 priv->scroll_delay_counter = priv->scrollbar_fade_delay;
1614 hildon_pannable_area_launch_fade_timeout (area, 1.0);
1620 hildon_pannable_area_redraw (HildonPannableArea * area)
1622 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (area)->priv;
1624 /* Redraw scroll indicators */
1625 if (GTK_WIDGET_DRAWABLE (area)) {
1626 if (priv->hscroll_visible) {
1627 gdk_window_invalidate_rect (GTK_WIDGET (area)->window,
1628 &priv->hscroll_rect, FALSE);
1631 if (priv->vscroll_visible) {
1632 gdk_window_invalidate_rect (GTK_WIDGET (area)->window,
1633 &priv->vscroll_rect, FALSE);
1639 hildon_pannable_area_scroll_indicator_fade(HildonPannableArea * area)
1641 HildonPannableAreaPrivate *priv = area->priv;
1643 /* if moving do not fade out */
1644 if (((ABS (priv->vel_y)>priv->vmin)||
1645 (ABS (priv->vel_x)>priv->vmin))&&(!priv->button_pressed)) {
1650 if (priv->scroll_indicator_event_interrupt) {
1651 /* Stop a fade out, and fade back in */
1652 if (priv->scroll_indicator_alpha > 0.9) {
1653 priv->scroll_indicator_alpha = 1.0;
1654 priv->scroll_indicator_timeout = 0;
1658 priv->scroll_indicator_alpha += 0.2;
1659 hildon_pannable_area_redraw (area);
1665 if ((priv->scroll_indicator_alpha > 0.9) &&
1666 (priv->scroll_delay_counter > 0)) {
1667 priv->scroll_delay_counter--;
1672 if (!priv->scroll_indicator_event_interrupt) {
1673 /* Continue fade out */
1674 if (priv->scroll_indicator_alpha < 0.1) {
1675 priv->scroll_indicator_timeout = 0;
1676 priv->scroll_indicator_alpha = 0.0;
1680 priv->scroll_indicator_alpha -= 0.2;
1681 hildon_pannable_area_redraw (area);
1691 hildon_pannable_area_expose_event (GtkWidget * widget,
1692 GdkEventExpose * event)
1695 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1696 #if USE_CAIRO_SCROLLBARS == 1
1697 GdkColor back_color = widget->style->bg[GTK_STATE_NORMAL];
1698 GdkColor scroll_color = widget->style->base[GTK_STATE_SELECTED];
1699 #else /* USE_CAIRO_SCROLLBARS */
1700 GdkColor back_color = widget->style->bg[GTK_STATE_NORMAL];
1701 GdkColor scroll_color = priv->scroll_color;
1704 if (G_UNLIKELY (priv->initial_effect)) {
1705 hildon_pannable_area_initial_effect (widget);
1707 priv->initial_effect = FALSE;
1710 if (gtk_bin_get_child (GTK_BIN (widget))) {
1712 if (priv->scroll_indicator_alpha > 0.1) {
1713 if (priv->vscroll_visible) {
1714 hildon_pannable_draw_vscroll (widget, &back_color, &scroll_color);
1716 if (priv->hscroll_visible) {
1717 hildon_pannable_draw_hscroll (widget, &back_color, &scroll_color);
1721 /* draw overshooting rectangles */
1722 if (priv->overshot_dist_y > 0) {
1723 gint overshot_height;
1725 overshot_height = MIN (priv->overshot_dist_y, widget->allocation.height -
1726 (priv->hscroll_visible ? priv->hscroll_rect.height : 0));
1728 gdk_draw_rectangle (widget->window,
1729 widget->style->bg_gc[GTK_STATE_NORMAL],
1733 widget->allocation.width -
1734 (priv->vscroll_visible ? priv->vscroll_rect.width : 0),
1736 } else if (priv->overshot_dist_y < 0) {
1737 gint overshot_height;
1741 MAX (priv->overshot_dist_y,
1742 -(widget->allocation.height -
1743 (priv->hscroll_visible ? priv->hscroll_rect.height : 0)));
1745 overshot_y = MAX (widget->allocation.height +
1747 (priv->hscroll_visible ? priv->hscroll_rect.height : 0), 0);
1749 gdk_draw_rectangle (widget->window,
1750 widget->style->bg_gc[GTK_STATE_NORMAL],
1754 widget->allocation.width -
1755 priv->vscroll_rect.width,
1759 if (priv->overshot_dist_x > 0) {
1760 gint overshot_width;
1762 overshot_width = MIN (priv->overshot_dist_x, widget->allocation.width -
1763 (priv->vscroll_visible ? priv->vscroll_rect.width : 0));
1765 gdk_draw_rectangle (widget->window,
1766 widget->style->bg_gc[GTK_STATE_NORMAL],
1771 widget->allocation.height -
1772 (priv->hscroll_visible ? priv->hscroll_rect.height : 0));
1773 } else if (priv->overshot_dist_x < 0) {
1774 gint overshot_width;
1778 MAX (priv->overshot_dist_x,
1779 -(widget->allocation.width -
1780 (priv->vscroll_visible ? priv->vscroll_rect.width : 0)));
1782 overshot_x = MAX (widget->allocation.width +
1784 (priv->vscroll_visible ? priv->vscroll_rect.width : 0), 0);
1786 gdk_draw_rectangle (widget->window,
1787 widget->style->bg_gc[GTK_STATE_NORMAL],
1792 widget->allocation.height -
1793 priv->hscroll_rect.height);
1798 return GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->expose_event (widget, event);
1802 hildon_pannable_area_get_topmost (GdkWindow * window,
1804 gint * tx, gint * ty,
1807 /* Find the GdkWindow at the given point, by recursing from a given
1808 * parent GdkWindow. Optionally return the co-ordinates transformed
1809 * relative to the child window.
1812 GList *c, *children;
1813 GdkWindow *selected_window = NULL;
1815 gdk_drawable_get_size (GDK_DRAWABLE (window), &width, &height);
1816 if ((x < 0) || (x >= width) || (y < 0) || (y >= height))
1819 children = gdk_window_peek_children (window);
1826 selected_window = window;
1829 for (c = children; c; c = c->next) {
1830 GdkWindow *child = (GdkWindow *) c->data;
1833 gdk_drawable_get_size (GDK_DRAWABLE (child), &width, &height);
1834 gdk_window_get_position (child, &wx, &wy);
1836 if ((x >= wx) && (x < (wx + width)) && (y >= wy) && (y < (wy + height)) &&
1837 (gdk_window_is_visible (child))) {
1839 if (gdk_window_peek_children (child)) {
1840 selected_window = hildon_pannable_area_get_topmost (child, x-wx, y-wy,
1842 if (!selected_window) {
1847 selected_window = child;
1850 if ((gdk_window_get_events (child)&mask)) {
1855 selected_window = child;
1861 return selected_window;
1865 synth_crossing (GdkWindow * child,
1867 gint x_root, gint y_root,
1868 guint32 time, gboolean in)
1870 GdkEventCrossing *crossing_event;
1871 GdkEventType type = in ? GDK_ENTER_NOTIFY : GDK_LEAVE_NOTIFY;
1873 /* Send synthetic enter event */
1874 crossing_event = (GdkEventCrossing *) gdk_event_new (type);
1875 ((GdkEventAny *) crossing_event)->type = type;
1876 ((GdkEventAny *) crossing_event)->window = g_object_ref (child);
1877 ((GdkEventAny *) crossing_event)->send_event = FALSE;
1878 crossing_event->subwindow = g_object_ref (child);
1879 crossing_event->time = time;
1880 crossing_event->x = x;
1881 crossing_event->y = y;
1882 crossing_event->x_root = x_root;
1883 crossing_event->y_root = y_root;
1884 crossing_event->mode = GDK_CROSSING_NORMAL;
1885 crossing_event->detail = GDK_NOTIFY_UNKNOWN;
1886 crossing_event->focus = FALSE;
1887 crossing_event->state = 0;
1888 gdk_event_put ((GdkEvent *) crossing_event);
1889 gdk_event_free ((GdkEvent *) crossing_event);
1893 hildon_pannable_area_button_press_cb (GtkWidget * widget,
1894 GdkEventButton * event)
1897 HildonPannableArea *area = HILDON_PANNABLE_AREA (widget);
1898 HildonPannableAreaPrivate *priv = area->priv;
1900 if ((!priv->enabled) || (event->button != 1) ||
1901 ((event->time == priv->last_time) &&
1902 (priv->last_type == 1)) || (gtk_bin_get_child (GTK_BIN (widget)) == NULL))
1905 priv->scroll_indicator_event_interrupt = 1;
1907 hildon_pannable_area_launch_fade_timeout (area,
1908 priv->scroll_indicator_alpha);
1910 priv->last_time = event->time;
1911 priv->last_type = 1;
1913 priv->scroll_to_x = -1;
1914 priv->scroll_to_y = -1;
1916 if (priv->button_pressed && priv->child) {
1917 /* Widget stole focus on last click, send crossing-out event */
1918 synth_crossing (priv->child, 0, 0, event->x_root, event->y_root,
1919 event->time, FALSE);
1927 /* Don't allow a click if we're still moving fast */
1928 if ((ABS (priv->vel_x) <= (priv->vmax * priv->vfast_factor)) &&
1929 (ABS (priv->vel_y) <= (priv->vmax * priv->vfast_factor)))
1931 hildon_pannable_area_get_topmost (gtk_bin_get_child (GTK_BIN (widget))->window,
1932 event->x, event->y, &x, &y, GDK_BUTTON_PRESS_MASK);
1936 priv->button_pressed = TRUE;
1938 /* Stop scrolling on mouse-down (so you can flick, then hold to stop) */
1941 if (priv->idle_id) {
1942 g_source_remove (priv->idle_id);
1944 g_signal_emit (area, pannable_area_signals[PANNING_FINISHED], 0);
1949 gdk_drawable_get_size (priv->child, &priv->child_width,
1950 &priv->child_height);
1951 priv->last_in = TRUE;
1953 g_object_add_weak_pointer ((GObject *) priv->child,
1954 (gpointer) & priv->child);
1956 event = (GdkEventButton *) gdk_event_copy ((GdkEvent *) event);
1962 synth_crossing (priv->child, x, y, event->x_root,
1963 event->y_root, event->time, TRUE);
1965 /* Send synthetic click (button press/release) event */
1966 ((GdkEventAny *) event)->window = g_object_ref (priv->child);
1968 gdk_event_put ((GdkEvent *) event);
1969 gdk_event_free ((GdkEvent *) event);
1977 hildon_pannable_area_check_scrollbars (HildonPannableArea * area)
1979 HildonPannableAreaPrivate *priv = area->priv;
1980 gboolean prev_hscroll_visible, prev_vscroll_visible;
1982 prev_hscroll_visible = priv->hscroll_visible;
1983 prev_vscroll_visible = priv->vscroll_visible;
1985 if (!gtk_bin_get_child (GTK_BIN (area))) {
1986 priv->vscroll_visible = FALSE;
1987 priv->hscroll_visible = FALSE;
1989 switch (priv->hscrollbar_policy) {
1990 case GTK_POLICY_ALWAYS:
1991 priv->hscroll_visible = TRUE;
1993 case GTK_POLICY_NEVER:
1994 priv->hscroll_visible = FALSE;
1997 priv->hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
1998 priv->hadjust->page_size);
2001 switch (priv->vscrollbar_policy) {
2002 case GTK_POLICY_ALWAYS:
2003 priv->vscroll_visible = TRUE;
2005 case GTK_POLICY_NEVER:
2006 priv->vscroll_visible = FALSE;
2009 priv->vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
2010 priv->vadjust->page_size);
2013 /* Store the vscroll/hscroll areas for redrawing */
2014 if (priv->vscroll_visible) {
2015 GtkAllocation *allocation = >K_WIDGET (area)->allocation;
2016 priv->vscroll_rect.x = allocation->width - priv->indicator_width;
2017 priv->vscroll_rect.y = 0;
2018 priv->vscroll_rect.width = priv->indicator_width;
2019 priv->vscroll_rect.height = allocation->height -
2020 (priv->hscroll_visible ? priv->indicator_width : 0);
2022 if (priv->hscroll_visible) {
2023 GtkAllocation *allocation = >K_WIDGET (area)->allocation;
2024 priv->hscroll_rect.y = allocation->height - priv->indicator_width;
2025 priv->hscroll_rect.x = 0;
2026 priv->hscroll_rect.height = priv->indicator_width;
2027 priv->hscroll_rect.width = allocation->width -
2028 (priv->vscroll_visible ? priv->indicator_width : 0);
2032 return ((priv->hscroll_visible != prev_hscroll_visible) ||
2033 (priv->vscroll_visible != prev_vscroll_visible));
2037 hildon_pannable_area_refresh (HildonPannableArea * area)
2039 if (GTK_WIDGET_DRAWABLE (area) &&
2040 hildon_pannable_area_check_scrollbars (area)) {
2041 HildonPannableAreaPrivate *priv = area->priv;
2043 gtk_widget_queue_resize (GTK_WIDGET (area));
2045 if ((priv->vscroll_visible) || (priv->hscroll_visible)) {
2046 priv->scroll_indicator_event_interrupt = 0;
2047 priv->scroll_delay_counter = area->priv->scrollbar_fade_delay;
2049 hildon_pannable_area_launch_fade_timeout (area, 1.0);
2052 hildon_pannable_area_redraw (area);
2056 /* Scroll by a particular amount (in pixels). Optionally, return if
2057 * the scroll on a particular axis was successful.
2060 hildon_pannable_axis_scroll (HildonPannableArea *area,
2061 GtkAdjustment *adjust,
2065 gint *overshot_dist,
2071 HildonPannableAreaPrivate *priv = area->priv;
2073 dist = gtk_adjustment_get_value (adjust) - inc;
2076 * We use overshot_dist to define the distance of the current overshoot,
2077 * and overshooting to define the direction/whether or not we are overshot
2079 if (!(*overshooting)) {
2081 /* Initiation of the overshoot happens when the finger is released
2082 * and the current position of the pannable contents are out of range
2084 if (dist < adjust->lower) {
2087 dist = adjust->lower;
2089 if (overshoot_max!=0) {
2092 *overshot_dist = CLAMP (*overshot_dist + *vel, 0, overshoot_max);
2093 *vel = MIN (priv->vmax_overshooting, *vel);
2094 gtk_widget_queue_resize (GTK_WIDGET (area));
2098 } else if (dist > adjust->upper - adjust->page_size) {
2101 dist = adjust->upper - adjust->page_size;
2103 if (overshoot_max!=0) {
2106 *overshot_dist = CLAMP (*overshot_dist + *vel, -overshoot_max, 0);
2107 *vel = MAX (-priv->vmax_overshooting, *vel);
2108 gtk_widget_queue_resize (GTK_WIDGET (area));
2113 if ((*scroll_to) != -1) {
2114 if (((inc < 0)&&(*scroll_to <= dist))||
2115 ((inc > 0)&&(*scroll_to >= dist))) {
2123 adjust->value = dist;
2125 if (!priv->button_pressed) {
2127 /* When the overshoot has started we continue for
2128 * PROP_BOUNCE_STEPS more steps into the overshoot before we
2129 * reverse direction. The deceleration factor is calculated
2130 * based on the percentage distance from the first item with
2131 * each iteration, therefore always returning us to the
2132 * top/bottom most element
2134 if (*overshot_dist > 0) {
2136 if ((*overshooting < priv->bounce_steps) && (*vel > 0)) {
2138 *vel = (((gdouble)*overshot_dist)/overshoot_max) * (*vel);
2139 } else if ((*overshooting >= priv->bounce_steps) && (*vel > 0)) {
2141 } else if ((*overshooting > 1) && (*vel < 0)) {
2142 /* we add the MIN in order to avoid very small speeds */
2143 *vel = MIN ((((gdouble)*overshot_dist)*0.4) * -1, -2.0);
2146 *overshot_dist = CLAMP (*overshot_dist + *vel, 0, overshoot_max);
2148 gtk_widget_queue_resize (GTK_WIDGET (area));
2150 } else if (*overshot_dist < 0) {
2152 if ((*overshooting < priv->bounce_steps) && (*vel < 0)) {
2154 *vel = (((gdouble)*overshot_dist)/overshoot_max) * (*vel) * -1;
2155 } else if ((*overshooting >= priv->bounce_steps) && (*vel < 0)) {
2157 } else if ((*overshooting > 1) && (*vel > 0)) {
2158 /* we add the MAX in order to avoid very small speeds */
2159 *vel = MAX ((((gdouble)*overshot_dist)*0.4) * -1, 2.0);
2162 *overshot_dist = CLAMP (*overshot_dist + (*vel), -overshoot_max, 0);
2164 gtk_widget_queue_resize (GTK_WIDGET (area));
2169 gtk_widget_queue_resize (GTK_WIDGET (area));
2173 gint overshot_dist_old = *overshot_dist;
2175 if (*overshot_dist > 0) {
2176 *overshot_dist = CLAMP ((*overshot_dist) + inc, 0, overshoot_max);
2177 } else if (*overshot_dist < 0) {
2178 *overshot_dist = CLAMP ((*overshot_dist) + inc, -1 * overshoot_max, 0);
2181 adjust->value = CLAMP (dist,
2187 if (*overshot_dist != overshot_dist_old)
2188 gtk_widget_queue_resize (GTK_WIDGET (area));
2194 hildon_pannable_area_scroll (HildonPannableArea *area,
2195 gdouble x, gdouble y)
2198 HildonPannableAreaPrivate *priv = area->priv;
2199 gboolean hscroll_visible, vscroll_visible;
2202 if (gtk_bin_get_child (GTK_BIN (area)) == NULL)
2205 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
2206 priv->vadjust->page_size);
2207 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
2208 priv->hadjust->page_size);
2213 hv = priv->hadjust->value;
2214 vv = priv->vadjust->value;
2216 if (vscroll_visible) {
2217 hildon_pannable_axis_scroll (area, priv->vadjust, &priv->vel_y, y,
2218 &priv->overshooting_y, &priv->overshot_dist_y,
2219 &priv->scroll_to_y, priv->vovershoot_max, &sy);
2224 if (hscroll_visible) {
2225 hildon_pannable_axis_scroll (area, priv->hadjust, &priv->vel_x, x,
2226 &priv->overshooting_x, &priv->overshot_dist_x,
2227 &priv->scroll_to_x, priv->hovershoot_max, &sx);
2232 if (hv != priv->hadjust->value)
2233 gtk_adjustment_value_changed (priv->hadjust);
2235 if (vv != priv->vadjust->value)
2236 gtk_adjustment_value_changed (priv->vadjust);
2238 /* If the scroll on a particular axis wasn't succesful, reset the
2239 * initial scroll position to the new mouse co-ordinate. This means
2240 * when you get to the top of the page, dragging down works immediately.
2242 if (priv->mode == HILDON_PANNABLE_AREA_MODE_ACCEL) {
2254 hildon_pannable_area_timeout (HildonPannableArea * area)
2256 HildonPannableAreaPrivate *priv = area->priv;
2258 if ((!priv->enabled) || (priv->mode == HILDON_PANNABLE_AREA_MODE_PUSH)) {
2260 g_signal_emit (area, pannable_area_signals[PANNING_FINISHED], 0);
2265 if (!priv->button_pressed) {
2266 /* Decelerate gradually when pointer is raised */
2267 if ((!priv->overshot_dist_y) &&
2268 (!priv->overshot_dist_x)) {
2270 /* in case we move to a specific point do not decelerate when arriving */
2271 if ((priv->scroll_to_x != -1)||(priv->scroll_to_y != -1)) {
2273 if (ABS (priv->vel_x) >= 1.5) {
2274 priv->vel_x *= priv->decel;
2277 if (ABS (priv->vel_y) >= 1.5) {
2278 priv->vel_y *= priv->decel;
2282 if ((!priv->low_friction_mode) ||
2283 ((priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ) &&
2284 (ABS (priv->vel_x) < 0.8*priv->vmax)))
2285 priv->vel_x *= priv->decel;
2287 if ((!priv->low_friction_mode) ||
2288 ((priv->mov_mode&HILDON_MOVEMENT_MODE_VERT) &&
2289 (ABS (priv->vel_y) < 0.8*priv->vmax)))
2290 priv->vel_y *= priv->decel;
2292 if ((ABS (priv->vel_x) < 1.0) && (ABS (priv->vel_y) < 1.0)) {
2297 g_signal_emit (area, pannable_area_signals[PANNING_FINISHED], 0);
2303 } else if (priv->mode == HILDON_PANNABLE_AREA_MODE_AUTO) {
2309 hildon_pannable_area_scroll (area, priv->vel_x, priv->vel_y);
2315 hildon_pannable_area_calculate_velocity (gdouble *vel,
2319 gdouble drag_inertia,
2325 if (ABS (dist) >= RATIO_TOLERANCE) {
2326 rawvel = (dist / ABS (delta)) * force;
2327 *vel = *vel * (1 - drag_inertia) +
2328 rawvel * drag_inertia;
2329 *vel = *vel > 0 ? MIN (*vel, vmax)
2330 : MAX (*vel, -1 * vmax);
2335 hildon_pannable_area_motion_event_scroll_timeout (HildonPannableArea *area)
2337 HildonPannableAreaPrivate *priv = area->priv;
2339 if ((priv->motion_x != 0)||(priv->motion_y != 0))
2340 hildon_pannable_area_scroll (area, priv->motion_x, priv->motion_y);
2342 priv->motion_event_scroll_timeout = 0;
2348 hildon_pannable_area_motion_event_scroll (HildonPannableArea *area,
2349 gdouble x, gdouble y)
2351 HildonPannableAreaPrivate *priv = area->priv;
2353 if (priv->motion_event_scroll_timeout) {
2355 priv->motion_x += x;
2356 priv->motion_y += y;
2360 /* we do not delay the first event but the next ones */
2361 hildon_pannable_area_scroll (area, x, y);
2366 priv->motion_event_scroll_timeout = gdk_threads_add_timeout
2367 ((gint) (1000.0 / (gdouble) MOTION_EVENTS_PER_SECOND),
2368 (GSourceFunc) hildon_pannable_area_motion_event_scroll_timeout, area);
2373 hildon_pannable_area_check_move (HildonPannableArea *area,
2374 GdkEventMotion * event,
2378 HildonPannableAreaPrivate *priv = area->priv;
2380 if (priv->first_drag && (!priv->moved) &&
2381 ((ABS (*x) > (priv->panning_threshold))
2382 || (ABS (*y) > (priv->panning_threshold)))) {
2387 if (priv->first_drag) {
2388 gboolean vscroll_visible;
2389 gboolean hscroll_visible;
2391 if (ABS (priv->iy - event->y) >=
2392 ABS (priv->ix - event->x)) {
2394 g_signal_emit (area,
2395 pannable_area_signals[VERTICAL_MOVEMENT],
2396 0, (priv->iy > event->y) ?
2397 HILDON_MOVEMENT_UP :
2398 HILDON_MOVEMENT_DOWN,
2399 (gdouble)priv->ix, (gdouble)priv->iy);
2401 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
2402 priv->vadjust->page_size);
2404 if (!((vscroll_visible)&&
2405 (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT))) {
2407 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
2408 priv->hadjust->page_size);
2410 /* even in case we do not have to move we check if this
2411 could be a fake horizontal movement */
2412 if (!((hscroll_visible)&&
2413 (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ)) ||
2414 (ABS (priv->iy - event->y) -
2415 ABS (priv->ix - event->x) >= priv->direction_error_margin))
2416 priv->moved = FALSE;
2420 g_signal_emit (area,
2421 pannable_area_signals[HORIZONTAL_MOVEMENT],
2422 0, (priv->ix > event->x) ?
2423 HILDON_MOVEMENT_LEFT :
2424 HILDON_MOVEMENT_RIGHT,
2425 (gdouble)priv->ix, (gdouble)priv->iy);
2427 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
2428 priv->hadjust->page_size);
2430 if (!((hscroll_visible)&&
2431 (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ))) {
2433 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
2434 priv->vadjust->page_size);
2436 /* even in case we do not have to move we check if this
2437 could be a fake vertical movement */
2438 if (!((vscroll_visible) &&
2439 (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT)) ||
2440 (ABS (priv->ix - event->x) -
2441 ABS (priv->iy - event->y) >= priv->direction_error_margin))
2442 priv->moved = FALSE;
2446 if ((priv->moved)&&(priv->child)) {
2449 pos_x = priv->cx + (event->x - priv->ix);
2450 pos_y = priv->cy + (event->y - priv->iy);
2452 synth_crossing (priv->child, pos_x, pos_y, event->x_root,
2453 event->y_root, event->time, FALSE);
2457 gboolean result_val;
2459 g_signal_emit (area,
2460 pannable_area_signals[PANNING_STARTED],
2463 priv->moved = !result_val;
2467 priv->first_drag = FALSE;
2469 if ((priv->mode != HILDON_PANNABLE_AREA_MODE_PUSH) &&
2470 (priv->mode != HILDON_PANNABLE_AREA_MODE_AUTO)) {
2473 priv->idle_id = gdk_threads_add_timeout ((gint)
2474 (1000.0 / (gdouble) priv->sps),
2476 hildon_pannable_area_timeout, area);
2482 hildon_pannable_area_handle_move (HildonPannableArea *area,
2483 GdkEventMotion * event,
2487 HildonPannableAreaPrivate *priv = area->priv;
2490 switch (priv->mode) {
2491 case HILDON_PANNABLE_AREA_MODE_PUSH:
2492 /* Scroll by the amount of pixels the cursor has moved
2493 * since the last motion event.
2495 hildon_pannable_area_motion_event_scroll (area, *x, *y);
2499 case HILDON_PANNABLE_AREA_MODE_ACCEL:
2500 /* Set acceleration relative to the initial click */
2501 priv->ex = event->x;
2502 priv->ey = event->y;
2503 priv->vel_x = ((*x > 0) ? 1 : -1) *
2505 (gdouble) GTK_WIDGET (area)->allocation.width) *
2506 (priv->vmax - priv->vmin)) + priv->vmin);
2507 priv->vel_y = ((*y > 0) ? 1 : -1) *
2509 (gdouble) GTK_WIDGET (area)->allocation.height) *
2510 (priv->vmax - priv->vmin)) + priv->vmin);
2512 case HILDON_PANNABLE_AREA_MODE_AUTO:
2514 delta = event->time - priv->last_time;
2516 if (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT) {
2517 gdouble dist = event->y - priv->y;
2519 hildon_pannable_area_calculate_velocity (&priv->vel_y,
2531 if (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ) {
2532 gdouble dist = event->x - priv->x;
2534 hildon_pannable_area_calculate_velocity (&priv->vel_x,
2546 hildon_pannable_area_motion_event_scroll (area, *x, *y);
2548 if (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ)
2550 if (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT)
2560 hildon_pannable_area_motion_notify_cb (GtkWidget * widget,
2561 GdkEventMotion * event)
2563 HildonPannableArea *area = HILDON_PANNABLE_AREA (widget);
2564 HildonPannableAreaPrivate *priv = area->priv;
2567 if (gtk_bin_get_child (GTK_BIN (widget)) == NULL)
2570 if ((!priv->enabled) || (!priv->button_pressed) ||
2571 ((event->time == priv->last_time) && (priv->last_type == 2))) {
2572 gdk_window_get_pointer (widget->window, NULL, NULL, 0);
2576 if (priv->last_type == 1) {
2577 priv->first_drag = TRUE;
2580 x = event->x - priv->x;
2581 y = event->y - priv->y;
2584 hildon_pannable_area_check_move (area, event, &x, &y);
2588 hildon_pannable_area_handle_move (area, event, &x, &y);
2589 } else if (priv->child) {
2593 pos_x = priv->cx + (event->x - priv->ix);
2594 pos_y = priv->cy + (event->y - priv->iy);
2596 in = (((0 <= pos_x)&&(priv->child_width >= pos_x)) &&
2597 ((0 <= pos_y)&&(priv->child_height >= pos_y)));
2599 if (((!priv->last_in)&&in)||((priv->last_in)&&(!in))) {
2601 synth_crossing (priv->child, pos_x, pos_y, event->x_root,
2602 event->y_root, event->time, in);
2608 priv->last_time = event->time;
2609 priv->last_type = 2;
2612 /* Send motion notify to child */
2613 event = (GdkEventMotion *) gdk_event_copy ((GdkEvent *) event);
2614 event->x = priv->cx + (event->x - priv->ix);
2615 event->y = priv->cy + (event->y - priv->iy);
2616 event->window = g_object_ref (priv->child);
2617 gdk_event_put ((GdkEvent *) event);
2618 gdk_event_free ((GdkEvent *) event);
2621 gdk_window_get_pointer (widget->window, NULL, NULL, 0);
2627 hildon_pannable_leave_notify_event (GtkWidget *widget,
2628 GdkEventCrossing *event)
2630 HildonPannableArea *area = HILDON_PANNABLE_AREA (widget);
2631 HildonPannableAreaPrivate *priv = area->priv;
2633 if ((priv->child)&&(priv->last_in)) {
2634 priv->last_in = FALSE;
2636 synth_crossing (priv->child, 0, 0, event->x_root,
2637 event->y_root, event->time, FALSE);
2644 hildon_pannable_area_button_release_cb (GtkWidget * widget,
2645 GdkEventButton * event)
2647 HildonPannableArea *area = HILDON_PANNABLE_AREA (widget);
2648 HildonPannableAreaPrivate *priv = area->priv;
2653 if (((event->time == priv->last_time) && (priv->last_type == 3))
2654 || (gtk_bin_get_child (GTK_BIN (widget)) == NULL)
2655 || (!priv->button_pressed) || (!priv->enabled) || (event->button != 1))
2658 /* if last event was a motion-notify we have to check the movement
2659 and launch the animation */
2660 if (priv->last_type == 2) {
2662 dx = event->x - priv->x;
2663 dy = event->y - priv->y;
2665 hildon_pannable_area_check_move (area, (GdkEventMotion *) event, &dx, &dy);
2668 gdouble delta = event->time - priv->last_time;
2670 hildon_pannable_area_handle_move (area, (GdkEventMotion *) event, &dx, &dy);
2672 /* move all the way to the last position now */
2673 if (priv->motion_event_scroll_timeout) {
2674 g_source_remove (priv->motion_event_scroll_timeout);
2675 hildon_pannable_area_motion_event_scroll_timeout (HILDON_PANNABLE_AREA (widget));
2680 if ((ABS (dx) < 4.0) && (delta >= CURSOR_STOPPED_TIMEOUT))
2683 if ((ABS (dy) < 4.0) && (delta >= CURSOR_STOPPED_TIMEOUT))
2688 /* If overshoot has been initiated with a finger down, on release set max speed */
2689 if (priv->overshot_dist_y != 0) {
2690 priv->overshooting_y = priv->bounce_steps; /* Hack to stop a bounce in the finger down case */
2691 priv->vel_y = priv->vmax_overshooting;
2694 if (priv->overshot_dist_x != 0) {
2695 priv->overshooting_x = priv->bounce_steps; /* Hack to stop a bounce in the finger down case */
2696 priv->vel_x = priv->vmax_overshooting;
2699 priv->button_pressed = FALSE;
2701 if ((ABS (priv->vel_y) >= priv->vmin) ||
2702 (ABS (priv->vel_x) >= priv->vmin)) {
2704 /* we have to move because we are in overshooting position*/
2706 gboolean result_val;
2708 g_signal_emit (area,
2709 pannable_area_signals[PANNING_STARTED],
2713 priv->scroll_indicator_alpha = 1.0;
2715 if (ABS (priv->vel_x) > MAX_SPEED_THRESHOLD)
2716 priv->vel_x = (priv->vel_x > 0) ? priv->vmax : -priv->vmax;
2718 if (ABS (priv->vel_y) > MAX_SPEED_THRESHOLD)
2719 priv->vel_y = (priv->vel_y > 0) ? priv->vmax : -priv->vmax;
2722 priv->idle_id = gdk_threads_add_timeout ((gint) (1000.0 / (gdouble) priv->sps),
2724 hildon_pannable_area_timeout, widget);
2727 g_signal_emit (widget, pannable_area_signals[PANNING_FINISHED], 0);
2730 priv->scroll_indicator_event_interrupt = 0;
2731 priv->scroll_delay_counter = priv->scrollbar_fade_delay;
2733 hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget),
2734 priv->scroll_indicator_alpha);
2736 priv->last_time = event->time;
2737 priv->last_type = 3;
2740 priv->moved = FALSE;
2745 hildon_pannable_area_get_topmost (gtk_bin_get_child (GTK_BIN (widget))->window,
2746 event->x, event->y, &x, &y, GDK_BUTTON_RELEASE_MASK);
2748 event = (GdkEventButton *) gdk_event_copy ((GdkEvent *) event);
2752 /* Leave the widget if we've moved - This doesn't break selection,
2753 * but stops buttons from being clicked.
2755 if ((child != priv->child) || (priv->moved)) {
2756 /* Send synthetic leave event */
2757 synth_crossing (priv->child, x, y, event->x_root,
2758 event->y_root, event->time, FALSE);
2759 /* insure no click will happen for widgets that do not handle
2763 /* Send synthetic button release event */
2764 ((GdkEventAny *) event)->window = g_object_ref (priv->child);
2765 gdk_event_put ((GdkEvent *) event);
2767 /* Send synthetic button release event */
2768 ((GdkEventAny *) event)->window = g_object_ref (child);
2769 gdk_event_put ((GdkEvent *) event);
2770 /* Send synthetic leave event */
2771 synth_crossing (priv->child, x, y, event->x_root,
2772 event->y_root, event->time, FALSE);
2774 g_object_remove_weak_pointer ((GObject *) priv->child,
2775 (gpointer) & priv->child);
2777 priv->moved = FALSE;
2778 gdk_event_free ((GdkEvent *) event);
2783 /* utility event handler */
2785 hildon_pannable_area_scroll_cb (GtkWidget *widget,
2786 GdkEventScroll *event)
2788 GtkAdjustment *adj = NULL;
2789 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
2791 if ((!priv->enabled) ||
2792 (gtk_bin_get_child (GTK_BIN (widget)) == NULL))
2795 priv->scroll_indicator_event_interrupt = 0;
2796 priv->scroll_delay_counter = priv->scrollbar_fade_delay + 20;
2798 hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget), 1.0);
2800 /* Stop inertial scrolling */
2801 if (priv->idle_id) {
2804 priv->overshooting_x = 0;
2805 priv->overshooting_y = 0;
2807 if ((priv->overshot_dist_x>0)||(priv->overshot_dist_y>0)) {
2808 priv->overshot_dist_x = 0;
2809 priv->overshot_dist_y = 0;
2811 gtk_widget_queue_resize (GTK_WIDGET (widget));
2814 g_signal_emit (widget, pannable_area_signals[PANNING_FINISHED], 0);
2816 g_source_remove (priv->idle_id);
2820 if (event->direction == GDK_SCROLL_UP || event->direction == GDK_SCROLL_DOWN)
2821 adj = priv->vadjust;
2823 adj = priv->hadjust;
2827 gdouble delta, new_value;
2829 /* from gtkrange.c calculate delta*/
2830 delta = pow (adj->page_size, 2.0 / 3.0);
2832 if (event->direction == GDK_SCROLL_UP ||
2833 event->direction == GDK_SCROLL_LEFT)
2836 new_value = CLAMP (adj->value + delta, adj->lower, adj->upper - adj->page_size);
2838 gtk_adjustment_set_value (adj, new_value);
2845 hildon_pannable_area_child_mapped (GtkWidget *widget,
2849 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (user_data)->priv;
2851 if (priv->event_window != NULL && priv->enabled)
2852 gdk_window_raise (priv->event_window);
2856 hildon_pannable_area_add (GtkContainer *container, GtkWidget *child)
2858 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (container)->priv;
2860 g_return_if_fail (gtk_bin_get_child (GTK_BIN (container)) == NULL);
2862 gtk_widget_set_parent (child, GTK_WIDGET (container));
2863 GTK_BIN (container)->child = child;
2865 g_signal_connect_after (child, "map-event",
2866 G_CALLBACK (hildon_pannable_area_child_mapped),
2869 if (!gtk_widget_set_scroll_adjustments (child, priv->hadjust, priv->vadjust)) {
2870 g_warning ("%s: cannot add non scrollable widget, "
2871 "wrap it in a viewport", __FUNCTION__);
2876 hildon_pannable_area_remove (GtkContainer *container, GtkWidget *child)
2878 g_return_if_fail (HILDON_IS_PANNABLE_AREA (container));
2879 g_return_if_fail (child != NULL);
2880 g_return_if_fail (gtk_bin_get_child (GTK_BIN (container)) == child);
2882 gtk_widget_set_scroll_adjustments (child, NULL, NULL);
2884 g_signal_handlers_disconnect_by_func (child,
2885 hildon_pannable_area_child_mapped,
2888 /* chain parent class handler to remove child */
2889 GTK_CONTAINER_CLASS (hildon_pannable_area_parent_class)->remove (container, child);
2893 * This method calculates a factor necessary to determine the initial distance
2894 * to jump in hildon_pannable_area_scroll_to(). For fixed time and frames per
2895 * second, we know in how many frames 'n' we need to reach the destination
2896 * point. We know that, for a distance d,
2898 * d = d_0 + d_1 + ... + d_n
2900 * where d_i is the distance travelled in the i-th frame and decel_factor is
2901 * the deceleration factor. This can be rewritten as
2903 * d = d_0 + (d_0 * decel_factor) + ... + (d_n-1 * decel_factor),
2905 * since the distance travelled on each frame is the distance travelled in the
2906 * previous frame reduced by the deceleration factor. Reducing this and
2907 * factoring d_0 out, we get
2909 * d = d_0 (1 + decel_factor + ... + decel_factor^(n-1)).
2911 * Since the sum is independent of the distance to be travelled, we can define
2914 * vel_factor = 1 + decel_factor + ... + decel_factor^(n-1).
2916 * That's the gem we calculate in this method.
2919 hildon_pannable_calculate_vel_factor (HildonPannableArea * self)
2921 HildonPannableAreaPrivate *priv = self->priv;
2926 n = ceil (priv->sps * priv->scroll_time);
2928 for (i = 1; i < n && fct_i >= RATIO_TOLERANCE; i++) {
2929 fct_i *= priv->decel;
2933 priv->vel_factor = fct;
2937 * hildon_pannable_area_new:
2939 * Create a new pannable area widget
2941 * Returns: the newly created #HildonPannableArea
2947 hildon_pannable_area_new (void)
2949 return g_object_new (HILDON_TYPE_PANNABLE_AREA, NULL);
2953 * hildon_pannable_area_new_full:
2954 * @mode: #HildonPannableAreaMode
2955 * @enabled: Value for the enabled property
2956 * @vel_min: Value for the velocity-min property
2957 * @vel_max: Value for the velocity-max property
2958 * @decel: Value for the deceleration property
2959 * @sps: Value for the sps property
2961 * Create a new #HildonPannableArea widget and set various properties
2963 * returns: the newly create #HildonPannableArea
2969 hildon_pannable_area_new_full (gint mode, gboolean enabled,
2970 gdouble vel_min, gdouble vel_max,
2971 gdouble decel, guint sps)
2973 return g_object_new (HILDON_TYPE_PANNABLE_AREA,
2976 "velocity_min", vel_min,
2977 "velocity_max", vel_max,
2978 "deceleration", decel, "sps", sps, NULL);
2982 * hildon_pannable_area_add_with_viewport:
2983 * @area: A #HildonPannableArea
2984 * @child: Child widget to add to the viewport
2986 * Convenience function used to add a child to a #GtkViewport, and add the
2987 * viewport to the scrolled window.
2993 hildon_pannable_area_add_with_viewport (HildonPannableArea * area,
2997 GtkWidget *viewport;
2999 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3000 g_return_if_fail (GTK_IS_WIDGET (child));
3001 g_return_if_fail (child->parent == NULL);
3003 bin = GTK_BIN (area);
3005 if (bin->child != NULL)
3007 g_return_if_fail (GTK_IS_VIEWPORT (bin->child));
3008 g_return_if_fail (GTK_BIN (bin->child)->child == NULL);
3010 viewport = bin->child;
3014 HildonPannableAreaPrivate *priv = area->priv;
3016 viewport = gtk_viewport_new (priv->hadjust,
3018 gtk_viewport_set_shadow_type (GTK_VIEWPORT (viewport), GTK_SHADOW_NONE);
3019 gtk_container_add (GTK_CONTAINER (area), viewport);
3022 gtk_widget_show (viewport);
3023 gtk_container_add (GTK_CONTAINER (viewport), child);
3027 * hildon_pannable_area_scroll_to:
3028 * @area: A #HildonPannableArea.
3029 * @x: The x coordinate of the destination point or -1 to ignore this axis.
3030 * @y: The y coordinate of the destination point or -1 to ignore this axis.
3032 * Smoothly scrolls @area to ensure that (@x, @y) is a visible point
3033 * on the widget. To move in only one coordinate, you must set the other one
3034 * to -1. Notice that, in %HILDON_PANNABLE_AREA_MODE_PUSH mode, this function
3035 * works just like hildon_pannable_area_jump_to().
3037 * This function is useful if you need to present the user with a particular
3038 * element inside a scrollable widget, like #GtkTreeView. For instance,
3039 * the following example shows how to scroll inside a #GtkTreeView to
3040 * make visible an item, indicated by the #GtkTreeIter @iter.
3044 * GtkTreePath *path;
3045 * GdkRectangle *rect;
3047 * path = gtk_tree_model_get_path (model, &iter);
3048 * gtk_tree_view_get_background_area (GTK_TREE_VIEW (treeview),
3049 * path, NULL, &rect);
3050 * gtk_tree_view_convert_bin_window_to_tree_coords (GTK_TREE_VIEW (treeview),
3051 * 0, rect.y, NULL, &y);
3052 * hildon_pannable_area_scroll_to (panarea, -1, y);
3053 * gtk_tree_path_free (path);
3057 * If you want to present a child widget in simpler scenarios,
3058 * use hildon_pannable_area_scroll_to_child() instead.
3060 * There is a precondition to this function: the widget must be
3061 * already realized. Check the hildon_pannable_area_jump_to_child() for
3062 * more tips regarding how to call this function during
3068 hildon_pannable_area_scroll_to (HildonPannableArea *area,
3069 const gint x, const gint y)
3071 HildonPannableAreaPrivate *priv;
3073 gint dist_x, dist_y;
3074 gboolean hscroll_visible, vscroll_visible;
3076 g_return_if_fail (GTK_WIDGET_REALIZED (area));
3077 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3081 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
3082 priv->vadjust->page_size);
3083 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
3084 priv->hadjust->page_size);
3086 if (((!vscroll_visible)&&(!hscroll_visible)) ||
3087 (x == -1 && y == -1)) {
3091 if (priv->mode == HILDON_PANNABLE_AREA_MODE_PUSH)
3092 hildon_pannable_area_jump_to (area, x, y);
3094 width = priv->hadjust->upper - priv->hadjust->lower;
3095 height = priv->vadjust->upper - priv->vadjust->lower;
3097 g_return_if_fail (x < width || y < height);
3099 if ((x > -1)&&(hscroll_visible)) {
3100 priv->scroll_to_x = x - priv->hadjust->page_size/2;
3101 dist_x = priv->scroll_to_x - priv->hadjust->value;
3103 priv->scroll_to_x = -1;
3105 priv->vel_x = - dist_x/priv->vel_factor;
3108 priv->scroll_to_x = -1;
3111 if ((y > -1)&&(vscroll_visible)) {
3112 priv->scroll_to_y = y - priv->vadjust->page_size/2;
3113 dist_y = priv->scroll_to_y - priv->vadjust->value;
3115 priv->scroll_to_y = -1;
3117 priv->vel_y = - dist_y/priv->vel_factor;
3120 priv->scroll_to_y = y;
3123 if ((priv->scroll_to_y == -1) && (priv->scroll_to_y == -1)) {
3127 hildon_pannable_area_launch_fade_timeout (area, 1.0);
3130 priv->idle_id = gdk_threads_add_timeout ((gint) (1000.0 / (gdouble) priv->sps),
3132 hildon_pannable_area_timeout, area);
3136 * hildon_pannable_area_jump_to:
3137 * @area: A #HildonPannableArea.
3138 * @x: The x coordinate of the destination point or -1 to ignore this axis.
3139 * @y: The y coordinate of the destination point or -1 to ignore this axis.
3141 * Jumps the position of @area to ensure that (@x, @y) is a visible
3142 * point in the widget. In order to move in only one coordinate, you
3143 * must set the other one to -1. See hildon_pannable_area_scroll_to()
3144 * function for an example of how to calculate the position of
3145 * children in scrollable widgets like #GtkTreeview.
3147 * There is a precondition to this function: the widget must be
3148 * already realized. Check the hildon_pannable_area_jump_to_child() for
3149 * more tips regarding how to call this function during
3155 hildon_pannable_area_jump_to (HildonPannableArea *area,
3156 const gint x, const gint y)
3158 HildonPannableAreaPrivate *priv;
3162 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3163 g_return_if_fail (GTK_WIDGET_REALIZED (area));
3164 g_return_if_fail (x >= -1 && y >= -1);
3166 if (x == -1 && y == -1) {
3172 width = priv->hadjust->upper - priv->hadjust->lower;
3173 height = priv->vadjust->upper - priv->vadjust->lower;
3175 g_return_if_fail (x < width || y < height);
3177 hv = priv->hadjust->value;
3178 vv = priv->vadjust->value;
3181 gdouble jump_to = x - priv->hadjust->page_size/2;
3183 priv->hadjust->value = CLAMP (jump_to,
3184 priv->hadjust->lower,
3185 priv->hadjust->upper -
3186 priv->hadjust->page_size);
3190 gdouble jump_to = y - priv->vadjust->page_size/2;
3192 priv->vadjust->value = CLAMP (jump_to,
3193 priv->vadjust->lower,
3194 priv->vadjust->upper -
3195 priv->vadjust->page_size);
3198 if (hv != priv->hadjust->value)
3199 gtk_adjustment_value_changed (priv->hadjust);
3201 if (vv != priv->vadjust->value)
3202 gtk_adjustment_value_changed (priv->vadjust);
3204 priv->scroll_indicator_alpha = 1.0;
3206 if (priv->scroll_indicator_timeout) {
3207 g_source_remove (priv->scroll_indicator_timeout);
3208 priv->scroll_indicator_timeout = 0;
3211 if (priv->idle_id) {
3214 priv->overshooting_x = 0;
3215 priv->overshooting_y = 0;
3217 if ((priv->overshot_dist_x>0)||(priv->overshot_dist_y>0)) {
3218 priv->overshot_dist_x = 0;
3219 priv->overshot_dist_y = 0;
3221 gtk_widget_queue_resize (GTK_WIDGET (area));
3224 g_signal_emit (area, pannable_area_signals[PANNING_FINISHED], 0);
3225 g_source_remove (priv->idle_id);
3231 * hildon_pannable_area_scroll_to_child:
3232 * @area: A #HildonPannableArea.
3233 * @child: A #GtkWidget, descendant of @area.
3235 * Smoothly scrolls until @child is visible inside @area. @child must
3236 * be a descendant of @area. If you need to scroll inside a scrollable
3237 * widget, e.g., #GtkTreeview, see hildon_pannable_area_scroll_to().
3239 * There is a precondition to this function: the widget must be
3240 * already realized. Check the hildon_pannable_area_jump_to_child() for
3241 * more tips regarding how to call this function during
3247 hildon_pannable_area_scroll_to_child (HildonPannableArea *area, GtkWidget *child)
3249 GtkWidget *bin_child;
3252 g_return_if_fail (GTK_WIDGET_REALIZED (area));
3253 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3254 g_return_if_fail (GTK_IS_WIDGET (child));
3255 g_return_if_fail (gtk_widget_is_ancestor (child, GTK_WIDGET (area)));
3257 if (GTK_BIN (area)->child == NULL)
3260 /* We need to get to check the child of the inside the area */
3261 bin_child = GTK_BIN (area)->child;
3263 /* we check if we added a viewport */
3264 if (GTK_IS_VIEWPORT (bin_child)) {
3265 bin_child = GTK_BIN (bin_child)->child;
3268 if (gtk_widget_translate_coordinates (child, bin_child, 0, 0, &x, &y))
3269 hildon_pannable_area_scroll_to (area, x, y);
3273 * hildon_pannable_area_jump_to_child:
3274 * @area: A #HildonPannableArea.
3275 * @child: A #GtkWidget, descendant of @area.
3277 * Jumps to make sure @child is visible inside @area. @child must
3278 * be a descendant of @area. If you want to move inside a scrollable
3279 * widget, like, #GtkTreeview, see hildon_pannable_area_scroll_to().
3281 * There is a precondition to this function: the widget must be
3282 * already realized. You can control if the widget is ready with the
3283 * GTK_WIDGET_REALIZED macro. If you want to call this function during
3284 * the initialization process of the widget do it inside a callback to
3285 * the ::realize signal, using g_signal_connect_after() function.
3290 hildon_pannable_area_jump_to_child (HildonPannableArea *area, GtkWidget *child)
3292 GtkWidget *bin_child;
3295 g_return_if_fail (GTK_WIDGET_REALIZED (area));
3296 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3297 g_return_if_fail (GTK_IS_WIDGET (child));
3298 g_return_if_fail (gtk_widget_is_ancestor (child, GTK_WIDGET (area)));
3300 if (gtk_bin_get_child (GTK_BIN (area)) == NULL)
3303 /* We need to get to check the child of the inside the area */
3304 bin_child = gtk_bin_get_child (GTK_BIN (area));
3306 /* we check if we added a viewport */
3307 if (GTK_IS_VIEWPORT (bin_child)) {
3308 bin_child = gtk_bin_get_child (GTK_BIN (bin_child));
3311 if (gtk_widget_translate_coordinates (child, bin_child, 0, 0, &x, &y))
3312 hildon_pannable_area_jump_to (area, x, y);
3316 * hildon_pannable_get_child_widget_at:
3317 * @area: A #HildonPannableArea.
3318 * @x: horizontal coordinate of the point
3319 * @y: vertical coordinate of the point
3321 * Get the widget at the point (x, y) inside the pannable area. In
3322 * case no widget found it returns NULL.
3324 * returns: the #GtkWidget if we find a widget, NULL in any other case
3329 hildon_pannable_get_child_widget_at (HildonPannableArea *area,
3330 gdouble x, gdouble y)
3332 GdkWindow *window = NULL;
3333 GtkWidget *child_widget = NULL;
3335 window = hildon_pannable_area_get_topmost
3336 (gtk_bin_get_child (GTK_BIN (area))->window,
3337 x, y, NULL, NULL, GDK_ALL_EVENTS_MASK);
3339 gdk_window_get_user_data (window, (gpointer) &child_widget);
3341 return child_widget;
3346 * hildon_pannable_area_get_hadjustment:
3347 * @area: A #HildonPannableArea.
3349 * Returns the horizontal adjustment. This adjustment is the internal
3350 * widget adjustment used to control the animations. Do not modify it
3351 * directly to change the position of the pannable, to do that use the
3352 * pannable API. If you modify the object directly it could cause
3353 * artifacts in the animations.
3355 * returns: The horizontal #GtkAdjustment
3360 hildon_pannable_area_get_hadjustment (HildonPannableArea *area)
3363 g_return_val_if_fail (HILDON_IS_PANNABLE_AREA (area), NULL);
3365 return area->priv->hadjust;
3369 * hildon_pannable_area_get_vadjustment:
3370 * @area: A #HildonPannableArea.
3372 * Returns the vertical adjustment. This adjustment is the internal
3373 * widget adjustment used to control the animations. Do not modify it
3374 * directly to change the position of the pannable, to do that use the
3375 * pannable API. If you modify the object directly it could cause
3376 * artifacts in the animations.
3378 * returns: The vertical #GtkAdjustment
3383 hildon_pannable_area_get_vadjustment (HildonPannableArea *area)
3385 g_return_val_if_fail (HILDON_IS_PANNABLE_AREA (area), NULL);
3387 return area->priv->vadjust;
3392 * hildon_pannable_area_get_size_request_policy:
3393 * @area: A #HildonPannableArea.
3395 * This function returns the current size request policy of the
3396 * widget. That policy controls the way the size_request is done in
3397 * the pannable area. Check
3398 * hildon_pannable_area_set_size_request_policy() for a more detailed
3401 * returns: the policy is currently being used in the widget
3402 * #HildonSizeRequestPolicy.
3406 HildonSizeRequestPolicy
3407 hildon_pannable_area_get_size_request_policy (HildonPannableArea *area)
3409 HildonPannableAreaPrivate *priv;
3411 g_return_val_if_fail (HILDON_IS_PANNABLE_AREA (area), FALSE);
3415 return priv->size_request_policy;
3419 * hildon_pannable_area_set_size_request_policy:
3420 * @area: A #HildonPannableArea.
3421 * @size_request_policy: One of the allowed #HildonSizeRequestPolicy
3423 * This function sets the pannable area size request policy. That
3424 * policy controls the way the size_request is done in the pannable
3425 * area. Pannable can use the size request of its children
3426 * (#HILDON_SIZE_REQUEST_CHILDREN) or the minimum size required for
3427 * the area itself (#HILDON_SIZE_REQUEST_MINIMUM), the latter is the
3428 * default. Recall this size depends on the scrolling policy you are
3429 * requesting to the pannable area, if you set #GTK_POLICY_NEVER this
3430 * parameter will not have any effect with
3431 * #HILDON_SIZE_REQUEST_MINIMUM set.
3435 * Deprecated: This method and the policy request is deprecated, DO
3436 * NOT use it in future code, the only policy properly supported in
3437 * gtk+ nowadays is the minimum size. Use #gtk_window_set_default_size
3438 * or #gtk_window_set_geometry_hints with the proper size in your case
3439 * to define the height of your dialogs.
3442 hildon_pannable_area_set_size_request_policy (HildonPannableArea *area,
3443 HildonSizeRequestPolicy size_request_policy)
3445 HildonPannableAreaPrivate *priv;
3447 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3451 if (priv->size_request_policy == size_request_policy)
3454 priv->size_request_policy = size_request_policy;
3456 gtk_widget_queue_resize (GTK_WIDGET (area));
3458 g_object_notify (G_OBJECT (area), "size-request-policy");