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;
155 static guint pannable_area_signals [LAST_SIGNAL] = { 0 };
163 PROP_VEL_MAX_OVERSHOOTING,
164 PROP_VELOCITY_FAST_FACTOR,
168 PROP_PANNING_THRESHOLD,
169 PROP_SCROLLBAR_FADE_DELAY,
172 PROP_DIRECTION_ERROR_MARGIN,
173 PROP_VSCROLLBAR_POLICY,
174 PROP_HSCROLLBAR_POLICY,
179 PROP_LOW_FRICTION_MODE,
180 PROP_SIZE_REQUEST_POLICY,
186 static void hildon_pannable_area_class_init (HildonPannableAreaClass * klass);
187 static void hildon_pannable_area_init (HildonPannableArea * area);
188 static void hildon_pannable_area_get_property (GObject * object,
192 static void hildon_pannable_area_set_property (GObject * object,
194 const GValue * value,
196 static void hildon_pannable_area_dispose (GObject * object);
197 static void hildon_pannable_area_realize (GtkWidget * widget);
198 static void hildon_pannable_area_unrealize (GtkWidget * widget);
199 static void hildon_pannable_area_size_request (GtkWidget * widget,
200 GtkRequisition * requisition);
201 static void hildon_pannable_area_size_allocate (GtkWidget * widget,
202 GtkAllocation * allocation);
203 static void hildon_pannable_area_child_allocate_calculate (GtkWidget * widget,
204 GtkAllocation * allocation,
205 GtkAllocation * child_allocation);
206 static void hildon_pannable_area_style_set (GtkWidget * widget,
207 GtkStyle * previous_style);
208 static void hildon_pannable_area_map (GtkWidget * widget);
209 static void hildon_pannable_area_unmap (GtkWidget * widget);
210 static void hildon_pannable_area_grab_notify (GtkWidget *widget,
211 gboolean was_grabbed,
213 #if USE_CAIRO_SCROLLBARS == 1
214 static void rgb_from_gdkcolor (GdkColor *color, gdouble *r, gdouble *g, gdouble *b);
215 #else /* USE_CAIRO_SCROLLBARS */
216 static void tranparency_color (GdkColor *color,
219 gdouble transparency);
220 #endif /* USE_CAIRO_SCROLLBARS */
221 static void hildon_pannable_draw_vscroll (GtkWidget * widget,
222 GdkColor *back_color,
223 GdkColor *scroll_color);
224 static void hildon_pannable_draw_hscroll (GtkWidget * widget,
225 GdkColor *back_color,
226 GdkColor *scroll_color);
227 static void hildon_pannable_area_initial_effect (GtkWidget * widget);
228 static void hildon_pannable_area_redraw (HildonPannableArea * area);
229 static void hildon_pannable_area_launch_fade_timeout (HildonPannableArea * area,
231 static void hildon_pannable_area_adjust_value_changed (HildonPannableArea * area,
233 static void hildon_pannable_area_adjust_changed (HildonPannableArea * area,
235 static gboolean hildon_pannable_area_scroll_indicator_fade(HildonPannableArea * area);
236 static gboolean hildon_pannable_area_expose_event (GtkWidget * widget,
237 GdkEventExpose * event);
238 static GdkWindow * hildon_pannable_area_get_topmost (GdkWindow * window,
240 gint * tx, gint * ty,
242 static void synth_crossing (GdkWindow * child,
244 gint x_root, gint y_root,
245 guint32 time, gboolean in);
246 static gboolean hildon_pannable_area_button_press_cb (GtkWidget * widget,
247 GdkEventButton * event);
248 static void hildon_pannable_area_refresh (HildonPannableArea * area);
249 static gboolean hildon_pannable_area_check_scrollbars (HildonPannableArea * area);
250 static void hildon_pannable_axis_scroll (HildonPannableArea *area,
251 GtkAdjustment *adjust,
259 static void hildon_pannable_area_scroll (HildonPannableArea *area,
260 gdouble x, gdouble y);
261 static gboolean hildon_pannable_area_timeout (HildonPannableArea * area);
262 static void hildon_pannable_area_calculate_velocity (gdouble *vel,
266 gdouble drag_inertia,
269 static gboolean hildon_pannable_area_motion_event_scroll_timeout (HildonPannableArea *area);
270 static void hildon_pannable_area_motion_event_scroll (HildonPannableArea *area,
271 gdouble x, gdouble y);
272 static void hildon_pannable_area_check_move (HildonPannableArea *area,
273 GdkEventMotion * event,
276 static void hildon_pannable_area_handle_move (HildonPannableArea *area,
277 GdkEventMotion * event,
280 static gboolean hildon_pannable_area_motion_notify_cb (GtkWidget * widget,
281 GdkEventMotion * event);
282 static gboolean hildon_pannable_leave_notify_event (GtkWidget *widget,
283 GdkEventCrossing *event);
284 static gboolean hildon_pannable_area_button_release_cb (GtkWidget * widget,
285 GdkEventButton * event);
286 static gboolean hildon_pannable_area_scroll_cb (GtkWidget *widget,
287 GdkEventScroll *event);
288 static void hildon_pannable_area_child_mapped (GtkWidget *widget,
291 static void hildon_pannable_area_add (GtkContainer *container, GtkWidget *child);
292 static void hildon_pannable_area_remove (GtkContainer *container, GtkWidget *child);
293 static void hildon_pannable_calculate_vel_factor (HildonPannableArea * self);
297 hildon_pannable_area_class_init (HildonPannableAreaClass * klass)
299 GObjectClass *object_class = G_OBJECT_CLASS (klass);
300 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
301 GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
304 g_type_class_add_private (klass, sizeof (HildonPannableAreaPrivate));
306 object_class->get_property = hildon_pannable_area_get_property;
307 object_class->set_property = hildon_pannable_area_set_property;
308 object_class->dispose = hildon_pannable_area_dispose;
310 widget_class->realize = hildon_pannable_area_realize;
311 widget_class->unrealize = hildon_pannable_area_unrealize;
312 widget_class->map = hildon_pannable_area_map;
313 widget_class->unmap = hildon_pannable_area_unmap;
314 widget_class->size_request = hildon_pannable_area_size_request;
315 widget_class->size_allocate = hildon_pannable_area_size_allocate;
316 widget_class->expose_event = hildon_pannable_area_expose_event;
317 widget_class->style_set = hildon_pannable_area_style_set;
318 widget_class->button_press_event = hildon_pannable_area_button_press_cb;
319 widget_class->button_release_event = hildon_pannable_area_button_release_cb;
320 widget_class->motion_notify_event = hildon_pannable_area_motion_notify_cb;
321 widget_class->leave_notify_event = hildon_pannable_leave_notify_event;
322 widget_class->scroll_event = hildon_pannable_area_scroll_cb;
324 container_class->add = hildon_pannable_area_add;
325 container_class->remove = hildon_pannable_area_remove;
327 klass->horizontal_movement = NULL;
328 klass->vertical_movement = NULL;
330 g_object_class_install_property (object_class,
332 g_param_spec_boolean ("enabled",
334 "Enable or disable finger-scroll.",
339 g_object_class_install_property (object_class,
340 PROP_VSCROLLBAR_POLICY,
341 g_param_spec_enum ("vscrollbar_policy",
343 "Visual policy of the vertical scrollbar",
344 GTK_TYPE_POLICY_TYPE,
345 GTK_POLICY_AUTOMATIC,
349 g_object_class_install_property (object_class,
350 PROP_HSCROLLBAR_POLICY,
351 g_param_spec_enum ("hscrollbar_policy",
353 "Visual policy of the horizontal scrollbar",
354 GTK_TYPE_POLICY_TYPE,
355 GTK_POLICY_AUTOMATIC,
359 g_object_class_install_property (object_class,
361 g_param_spec_enum ("mode",
363 "Change the finger-scrolling mode.",
364 HILDON_TYPE_PANNABLE_AREA_MODE,
365 HILDON_PANNABLE_AREA_MODE_AUTO,
369 g_object_class_install_property (object_class,
371 g_param_spec_flags ("mov_mode",
372 "Scroll movement mode",
373 "Controls if the widget can scroll vertically, horizontally or both",
374 HILDON_TYPE_MOVEMENT_MODE,
375 HILDON_MOVEMENT_MODE_VERT,
379 g_object_class_install_property (object_class,
381 g_param_spec_double ("velocity_min",
382 "Minimum scroll velocity",
383 "Minimum distance the child widget should scroll "
384 "per 'frame', in pixels per frame.",
389 g_object_class_install_property (object_class,
391 g_param_spec_double ("velocity_max",
392 "Maximum scroll velocity",
393 "Maximum distance the child widget should scroll "
394 "per 'frame', in pixels per frame.",
399 g_object_class_install_property (object_class,
400 PROP_VEL_MAX_OVERSHOOTING,
401 g_param_spec_double ("velocity_overshooting_max",
402 "Maximum scroll velocity when overshooting",
403 "Maximum distance the child widget should scroll "
404 "per 'frame', in pixels per frame when it overshoots after hitting the edge.",
409 g_object_class_install_property (object_class,
410 PROP_VELOCITY_FAST_FACTOR,
411 g_param_spec_double ("velocity_fast_factor",
412 "Fast velocity factor",
413 "Minimum velocity that is considered 'fast': "
414 "children widgets won't receive button presses. "
415 "Expressed as a fraction of the maximum velocity.",
420 g_object_class_install_property (object_class,
422 g_param_spec_double ("deceleration",
423 "Deceleration multiplier",
424 "The multiplier used when decelerating when in "
425 "acceleration scrolling mode.",
430 g_object_class_install_property (object_class,
432 g_param_spec_double ("drag_inertia",
433 "Inertia of the cursor dragging",
434 "Percentage of the calculated speed in each moment we are are going to use"
435 "to calculate the launch speed, the other part would be the speed"
436 "calculated previously",
441 g_object_class_install_property (object_class,
443 g_param_spec_uint ("sps",
444 "Scrolls per second",
445 "Amount of scroll events to generate per second.",
450 g_object_class_install_property (object_class,
451 PROP_PANNING_THRESHOLD,
452 g_param_spec_uint ("panning_threshold",
453 "Threshold to consider a motion event an scroll",
454 "Amount of pixels to consider a motion event an scroll, if it is less"
455 "it is a click detected incorrectly by the touch screen.",
460 g_object_class_install_property (object_class,
461 PROP_SCROLLBAR_FADE_DELAY,
462 g_param_spec_uint ("scrollbar_fade_delay",
463 "Time before starting to fade the scrollbar",
464 "Time the scrollbar is going to be visible if the widget is not in"
465 "action in miliseconds",
470 g_object_class_install_property (object_class,
472 g_param_spec_uint ("bounce_steps",
474 "Number of steps that is going to be used to bounce when hitting the"
475 "edge, the rubberband effect depends on it",
480 g_object_class_install_property (object_class,
482 g_param_spec_uint ("force",
483 "Multiplier of the calculated speed",
484 "Force applied to the movement, multiplies the calculated speed of the"
485 "user movement the cursor in the screen",
490 g_object_class_install_property (object_class,
491 PROP_DIRECTION_ERROR_MARGIN,
492 g_param_spec_uint ("direction_error_margin",
493 "Margin in the direction detection",
494 "After detecting the direction of the movement (horizontal or"
495 "vertical), we can add this margin of error to allow the movement in"
496 "the other direction even apparently it is not",
501 g_object_class_install_property (object_class,
503 g_param_spec_int ("vovershoot_max",
504 "Vertical overshoot distance",
505 "Space we allow the widget to pass over its vertical limits when"
506 "hitting the edges, set 0 in order to deactivate overshooting.",
511 g_object_class_install_property (object_class,
513 g_param_spec_int ("hovershoot_max",
514 "Horizontal overshoot distance",
515 "Space we allow the widget to pass over its horizontal limits when"
516 "hitting the edges, set 0 in order to deactivate overshooting.",
521 g_object_class_install_property (object_class,
523 g_param_spec_double ("scroll_time",
524 "Time to scroll to a position",
525 "The time to scroll to a position when calling the hildon_pannable_scroll_to function",
530 g_object_class_install_property (object_class,
532 g_param_spec_boolean ("initial-hint",
534 "Whether to hint the user about the pannability of the container.",
539 g_object_class_install_property (object_class,
540 PROP_LOW_FRICTION_MODE,
541 g_param_spec_boolean ("low-friction-mode",
542 "Do not decelerate the initial velocity",
543 "Avoid decelerating the panning movement, like no friction, the widget"
544 "will stop in the edges or if the user clicks.",
549 g_object_class_install_property (object_class,
550 PROP_SIZE_REQUEST_POLICY,
551 g_param_spec_enum ("size-request-policy",
552 "Size Requisition policy",
553 "Controls the size request policy of the widget",
554 HILDON_TYPE_SIZE_REQUEST_POLICY,
555 HILDON_SIZE_REQUEST_MINIMUM,
559 g_object_class_install_property (object_class,
561 g_param_spec_object ("hadjustment",
562 "Horizontal Adjustment",
563 "The GtkAdjustment for the horizontal position",
566 g_object_class_install_property (object_class,
568 g_param_spec_object ("vadjustment",
569 "Vertical Adjustment",
570 "The GtkAdjustment for the vertical position",
574 gtk_widget_class_install_style_property (widget_class,
577 "Width of the scroll indicators",
578 "Pixel width used to draw the scroll indicators.",
583 * HildonPannableArea::horizontal-movement:
584 * @hildonpannable: the object which received the signal
585 * @direction: the direction of the movement #HILDON_MOVEMENT_LEFT or #HILDON_MOVEMENT_RIGHT
586 * @initial_x: the x coordinate of the point where the user clicked to start the movement
587 * @initial_y: the y coordinate of the point where the user clicked to start the movement
589 * The horizontal-movement signal is emitted when the pannable area
590 * detects a horizontal movement. The detection does not mean the
591 * widget is going to move (i.e. maybe the children are smaller
592 * horizontally than the screen).
596 pannable_area_signals[HORIZONTAL_MOVEMENT] =
597 g_signal_new ("horizontal_movement",
598 G_TYPE_FROM_CLASS (object_class),
599 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
600 G_STRUCT_OFFSET (HildonPannableAreaClass, horizontal_movement),
602 _hildon_marshal_VOID__INT_DOUBLE_DOUBLE,
609 * HildonPannableArea::vertical-movement:
610 * @hildonpannable: the object which received the signal
611 * @direction: the direction of the movement #HILDON_MOVEMENT_UP or #HILDON_MOVEMENT_DOWN
612 * @initial_x: the x coordinate of the point where the user clicked to start the movement
613 * @initial_y: the y coordinate of the point where the user clicked to start the movement
615 * The vertical-movement signal is emitted when the pannable area
616 * detects a vertical movement. The detection does not mean the
617 * widget is going to move (i.e. maybe the children are smaller
618 * vertically than the screen).
622 pannable_area_signals[VERTICAL_MOVEMENT] =
623 g_signal_new ("vertical_movement",
624 G_TYPE_FROM_CLASS (object_class),
625 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
626 G_STRUCT_OFFSET (HildonPannableAreaClass, vertical_movement),
628 _hildon_marshal_VOID__INT_DOUBLE_DOUBLE,
635 * HildonPannableArea::panning-started:
636 * @hildonpannable: the pannable area object that is going to start
639 * This signal is emitted before the panning starts. Applications
640 * can return %TRUE to avoid the panning. The main difference with
641 * the vertical-movement and horizontal-movement signals is those
642 * gesture signals are launched no matter if the widget is going to
643 * move, this signal means the widget is going to start moving. It
644 * could even happen that the widget moves and there was no gesture
645 * (i.e. click meanwhile the pannable is overshooting).
647 * Returns: %TRUE to stop the panning launch. %FALSE to continue
652 pannable_area_signals[PANNING_STARTED] =
653 g_signal_new ("panning-started",
654 G_TYPE_FROM_CLASS (object_class),
658 _hildon_marshal_BOOLEAN__VOID,
662 * HildonPannableArea::panning-finished:
663 * @hildonpannable: the pannable area object that finished the
666 * This signal is emitted after the kinetic panning has
671 pannable_area_signals[PANNING_FINISHED] =
672 g_signal_new ("panning-finished",
673 G_TYPE_FROM_CLASS (object_class),
677 _hildon_marshal_VOID__VOID,
683 hildon_pannable_area_init (HildonPannableArea * area)
685 HildonPannableAreaPrivate *priv = PANNABLE_AREA_PRIVATE (area);
687 GTK_WIDGET_UNSET_FLAGS (area, GTK_NO_WINDOW);
692 priv->button_pressed = FALSE;
695 priv->vscroll_visible = TRUE;
696 priv->hscroll_visible = TRUE;
697 priv->indicator_width = 6;
698 priv->overshot_dist_x = 0;
699 priv->overshot_dist_y = 0;
700 priv->overshooting_y = 0;
701 priv->overshooting_x = 0;
705 priv->scroll_indicator_alpha = 0.0;
706 priv->scroll_indicator_timeout = 0;
707 priv->motion_event_scroll_timeout = 0;
708 priv->scroll_indicator_event_interrupt = 0;
709 priv->scroll_delay_counter = 0;
710 priv->scrollbar_fade_delay = 0;
711 priv->scroll_to_x = -1;
712 priv->scroll_to_y = -1;
713 priv->first_drag = TRUE;
714 priv->initial_effect = TRUE;
715 priv->child_width = 0;
716 priv->child_height = 0;
717 priv->last_in = TRUE;
722 GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
724 GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
726 g_object_ref_sink (G_OBJECT (priv->hadjust));
727 g_object_ref_sink (G_OBJECT (priv->vadjust));
729 g_signal_connect_swapped (priv->hadjust, "value-changed",
730 G_CALLBACK (hildon_pannable_area_adjust_value_changed), area);
731 g_signal_connect_swapped (priv->vadjust, "value-changed",
732 G_CALLBACK (hildon_pannable_area_adjust_value_changed), area);
733 g_signal_connect_swapped (priv->hadjust, "changed",
734 G_CALLBACK (hildon_pannable_area_adjust_changed), area);
735 g_signal_connect_swapped (priv->vadjust, "changed",
736 G_CALLBACK (hildon_pannable_area_adjust_changed), area);
737 g_signal_connect (area, "grab-notify",
738 G_CALLBACK (hildon_pannable_area_grab_notify), NULL);
742 hildon_pannable_area_get_property (GObject * object,
747 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (object)->priv;
749 switch (property_id) {
751 g_value_set_boolean (value, priv->enabled);
754 g_value_set_enum (value, priv->mode);
756 case PROP_MOVEMENT_MODE:
757 g_value_set_flags (value, priv->mov_mode);
759 case PROP_VELOCITY_MIN:
760 g_value_set_double (value, priv->vmin);
762 case PROP_VELOCITY_MAX:
763 g_value_set_double (value, priv->vmax);
765 case PROP_VEL_MAX_OVERSHOOTING:
766 g_value_set_double (value, priv->vmax_overshooting);
768 case PROP_VELOCITY_FAST_FACTOR:
769 g_value_set_double (value, priv->vfast_factor);
771 case PROP_DECELERATION:
772 g_value_set_double (value, priv->decel);
774 case PROP_DRAG_INERTIA:
775 g_value_set_double (value, priv->drag_inertia);
778 g_value_set_uint (value, priv->sps);
780 case PROP_PANNING_THRESHOLD:
781 g_value_set_uint (value, priv->panning_threshold);
783 case PROP_SCROLLBAR_FADE_DELAY:
784 /* convert to miliseconds */
785 g_value_set_uint (value, priv->scrollbar_fade_delay * SCROLL_FADE_TIMEOUT);
787 case PROP_BOUNCE_STEPS:
788 g_value_set_uint (value, priv->bounce_steps);
791 g_value_set_uint (value, priv->force);
793 case PROP_DIRECTION_ERROR_MARGIN:
794 g_value_set_uint (value, priv->direction_error_margin);
796 case PROP_VSCROLLBAR_POLICY:
797 g_value_set_enum (value, priv->vscrollbar_policy);
799 case PROP_HSCROLLBAR_POLICY:
800 g_value_set_enum (value, priv->hscrollbar_policy);
802 case PROP_VOVERSHOOT_MAX:
803 g_value_set_int (value, priv->vovershoot_max);
805 case PROP_HOVERSHOOT_MAX:
806 g_value_set_int (value, priv->hovershoot_max);
808 case PROP_SCROLL_TIME:
809 g_value_set_double (value, priv->scroll_time);
811 case PROP_INITIAL_HINT:
812 g_value_set_boolean (value, priv->initial_hint);
814 case PROP_LOW_FRICTION_MODE:
815 g_value_set_boolean (value, priv->low_friction_mode);
817 case PROP_SIZE_REQUEST_POLICY:
818 g_value_set_enum (value, priv->size_request_policy);
820 case PROP_HADJUSTMENT:
821 g_value_set_object (value,
822 hildon_pannable_area_get_hadjustment
823 (HILDON_PANNABLE_AREA (object)));
825 case PROP_VADJUSTMENT:
826 g_value_set_object (value,
827 hildon_pannable_area_get_vadjustment
828 (HILDON_PANNABLE_AREA (object)));
831 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
836 hildon_pannable_area_set_property (GObject * object,
838 const GValue * value,
841 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (object)->priv;
844 switch (property_id) {
846 enabled = g_value_get_boolean (value);
848 if ((priv->enabled != enabled) && (GTK_WIDGET_REALIZED (object))) {
850 gdk_window_raise (priv->event_window);
852 gdk_window_lower (priv->event_window);
855 priv->enabled = enabled;
858 priv->mode = g_value_get_enum (value);
860 case PROP_MOVEMENT_MODE:
861 priv->mov_mode = g_value_get_flags (value);
863 case PROP_VELOCITY_MIN:
864 priv->vmin = g_value_get_double (value);
866 case PROP_VELOCITY_MAX:
867 priv->vmax = g_value_get_double (value);
869 case PROP_VEL_MAX_OVERSHOOTING:
870 priv->vmax_overshooting = g_value_get_double (value);
872 case PROP_VELOCITY_FAST_FACTOR:
873 priv->vfast_factor = g_value_get_double (value);
875 case PROP_DECELERATION:
876 priv->decel = g_value_get_double (value);
877 hildon_pannable_calculate_vel_factor (HILDON_PANNABLE_AREA (object));
879 case PROP_DRAG_INERTIA:
880 priv->drag_inertia = g_value_get_double (value);
883 priv->sps = g_value_get_uint (value);
885 case PROP_PANNING_THRESHOLD:
887 GtkSettings *settings = gtk_settings_get_default ();
888 GtkSettingsValue svalue = { NULL, { 0, }, };
890 priv->panning_threshold = g_value_get_uint (value);
892 /* insure gtk dnd is the same we are using, not allowed
893 different thresholds in the same application */
894 svalue.origin = "panning_threshold";
895 g_value_init (&svalue.value, G_TYPE_LONG);
896 g_value_set_long (&svalue.value, priv->panning_threshold);
897 gtk_settings_set_property_value (settings, "gtk-dnd-drag-threshold", &svalue);
898 g_value_unset (&svalue.value);
901 case PROP_SCROLLBAR_FADE_DELAY:
902 /* convert to miliseconds */
903 priv->scrollbar_fade_delay = g_value_get_uint (value)/(SCROLL_FADE_TIMEOUT);
905 case PROP_BOUNCE_STEPS:
906 priv->bounce_steps = g_value_get_uint (value);
909 priv->force = g_value_get_uint (value);
911 case PROP_DIRECTION_ERROR_MARGIN:
912 priv->direction_error_margin = g_value_get_uint (value);
914 case PROP_VSCROLLBAR_POLICY:
915 priv->vscrollbar_policy = g_value_get_enum (value);
917 gtk_widget_queue_resize (GTK_WIDGET (object));
919 case PROP_HSCROLLBAR_POLICY:
920 priv->hscrollbar_policy = g_value_get_enum (value);
922 gtk_widget_queue_resize (GTK_WIDGET (object));
924 case PROP_VOVERSHOOT_MAX:
925 priv->vovershoot_max = g_value_get_int (value);
927 case PROP_HOVERSHOOT_MAX:
928 priv->hovershoot_max = g_value_get_int (value);
930 case PROP_SCROLL_TIME:
931 priv->scroll_time = g_value_get_double (value);
933 hildon_pannable_calculate_vel_factor (HILDON_PANNABLE_AREA (object));
935 case PROP_INITIAL_HINT:
936 priv->initial_hint = g_value_get_boolean (value);
938 case PROP_LOW_FRICTION_MODE:
939 priv->low_friction_mode = g_value_get_boolean (value);
941 case PROP_SIZE_REQUEST_POLICY:
942 hildon_pannable_area_set_size_request_policy (HILDON_PANNABLE_AREA (object),
943 g_value_get_enum (value));
947 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
952 hildon_pannable_area_dispose (GObject * object)
954 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (object)->priv;
955 GtkWidget *child = gtk_bin_get_child (GTK_BIN (object));
958 g_signal_emit (object, pannable_area_signals[PANNING_FINISHED], 0);
959 g_source_remove (priv->idle_id);
963 if (priv->scroll_indicator_timeout){
964 g_source_remove (priv->scroll_indicator_timeout);
965 priv->scroll_indicator_timeout = 0;
968 if (priv->motion_event_scroll_timeout){
969 g_source_remove (priv->motion_event_scroll_timeout);
970 priv->motion_event_scroll_timeout = 0;
974 g_signal_handlers_disconnect_by_func (child,
975 hildon_pannable_area_child_mapped,
979 g_signal_handlers_disconnect_by_func (object,
980 hildon_pannable_area_grab_notify,
984 g_signal_handlers_disconnect_by_func (priv->hadjust,
985 hildon_pannable_area_adjust_value_changed,
987 g_signal_handlers_disconnect_by_func (priv->hadjust,
988 hildon_pannable_area_adjust_changed,
990 g_object_unref (priv->hadjust);
991 priv->hadjust = NULL;
995 g_signal_handlers_disconnect_by_func (priv->vadjust,
996 hildon_pannable_area_adjust_value_changed,
998 g_signal_handlers_disconnect_by_func (priv->vadjust,
999 hildon_pannable_area_adjust_changed,
1001 g_object_unref (priv->vadjust);
1002 priv->vadjust = NULL;
1005 if (G_OBJECT_CLASS (hildon_pannable_area_parent_class)->dispose)
1006 G_OBJECT_CLASS (hildon_pannable_area_parent_class)->dispose (object);
1010 hildon_pannable_area_realize (GtkWidget * widget)
1012 GdkWindowAttr attributes;
1013 gint attributes_mask;
1015 HildonPannableAreaPrivate *priv;
1017 priv = HILDON_PANNABLE_AREA (widget)->priv;
1019 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
1021 border_width = GTK_CONTAINER (widget)->border_width;
1023 attributes.x = widget->allocation.x + border_width;
1024 attributes.y = widget->allocation.y + border_width;
1025 attributes.width = MAX (widget->allocation.width - 2 * border_width, 0);
1026 attributes.height = MAX (widget->allocation.height - 2 * border_width, 0);
1027 attributes.window_type = GDK_WINDOW_CHILD;
1029 /* avoid using the hildon_window */
1030 attributes.visual = gtk_widget_get_visual (widget);
1031 attributes.colormap = gtk_widget_get_colormap (widget);
1032 attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK;
1033 attributes.wclass = GDK_INPUT_OUTPUT;
1035 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1037 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
1038 &attributes, attributes_mask);
1039 gdk_window_set_user_data (widget->window, widget);
1041 /* create the events window */
1044 attributes.event_mask = gtk_widget_get_events (widget)
1045 | GDK_BUTTON_MOTION_MASK
1046 | GDK_BUTTON_PRESS_MASK
1047 | GDK_BUTTON_RELEASE_MASK
1049 | GDK_POINTER_MOTION_HINT_MASK
1050 | GDK_EXPOSURE_MASK | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK;
1051 attributes.wclass = GDK_INPUT_ONLY;
1053 attributes_mask = GDK_WA_X | GDK_WA_Y;
1055 priv->event_window = gdk_window_new (widget->window,
1056 &attributes, attributes_mask);
1057 gdk_window_set_user_data (priv->event_window, widget);
1059 widget->style = gtk_style_attach (widget->style, widget->window);
1060 gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
1062 priv->scrollbars_gc = gdk_gc_new (GDK_DRAWABLE (widget->window));
1063 gdk_gc_copy (priv->scrollbars_gc, widget->style->fg_gc[GTK_STATE_INSENSITIVE]);
1067 hildon_pannable_area_unrealize (GtkWidget * widget)
1069 HildonPannableAreaPrivate *priv;
1071 priv = HILDON_PANNABLE_AREA (widget)->priv;
1073 if (priv->event_window != NULL) {
1074 gdk_window_set_user_data (priv->event_window, NULL);
1075 gdk_window_destroy (priv->event_window);
1076 priv->event_window = NULL;
1079 gdk_gc_unref (priv->scrollbars_gc);
1081 if (GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->unrealize)
1082 (*GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->unrealize)(widget);
1086 hildon_pannable_area_size_request (GtkWidget * widget,
1087 GtkRequisition * requisition)
1089 GtkRequisition child_requisition = {0};
1090 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1091 GtkWidget *child = gtk_bin_get_child (GTK_BIN (widget));
1093 if (child && GTK_WIDGET_VISIBLE (child))
1095 gtk_widget_size_request (child, &child_requisition);
1098 if (priv->hscrollbar_policy == GTK_POLICY_NEVER) {
1099 requisition->width = child_requisition.width;
1101 switch (priv->size_request_policy) {
1102 case HILDON_SIZE_REQUEST_CHILDREN:
1103 requisition->width = MIN (PANNABLE_MAX_WIDTH,
1104 child_requisition.width);
1106 case HILDON_SIZE_REQUEST_MINIMUM:
1108 requisition->width = priv->indicator_width;
1112 if (priv->vscrollbar_policy == GTK_POLICY_NEVER) {
1113 requisition->height = child_requisition.height;
1115 switch (priv->size_request_policy) {
1116 case HILDON_SIZE_REQUEST_CHILDREN:
1117 requisition->height = MIN (PANNABLE_MAX_HEIGHT,
1118 child_requisition.height);
1120 case HILDON_SIZE_REQUEST_MINIMUM:
1122 requisition->height = priv->indicator_width;
1126 requisition->width += 2 * GTK_CONTAINER (widget)->border_width;
1127 requisition->height += 2 * GTK_CONTAINER (widget)->border_width;
1131 hildon_pannable_area_child_allocate_calculate (GtkWidget * widget,
1132 GtkAllocation * allocation,
1133 GtkAllocation * child_allocation)
1136 HildonPannableAreaPrivate *priv;
1138 border_width = GTK_CONTAINER (widget)->border_width;
1140 priv = HILDON_PANNABLE_AREA (widget)->priv;
1142 child_allocation->x = 0;
1143 child_allocation->y = 0;
1144 child_allocation->width = MAX (allocation->width - 2 * border_width -
1145 (priv->vscroll_visible ? priv->vscroll_rect.width : 0), 0);
1146 child_allocation->height = MAX (allocation->height - 2 * border_width -
1147 (priv->hscroll_visible ? priv->hscroll_rect.height : 0), 0);
1149 if (priv->overshot_dist_y > 0) {
1150 child_allocation->y = MIN (child_allocation->y + priv->overshot_dist_y,
1151 child_allocation->height);
1152 child_allocation->height = MAX (child_allocation->height - priv->overshot_dist_y, 0);
1153 } else if (priv->overshot_dist_y < 0) {
1154 child_allocation->height = MAX (child_allocation->height + priv->overshot_dist_y, 0);
1157 if (priv->overshot_dist_x > 0) {
1158 child_allocation->x = MIN (child_allocation->x + priv->overshot_dist_x,
1159 child_allocation->width);
1160 child_allocation->width = MAX (child_allocation->width - priv->overshot_dist_x, 0);
1161 } else if (priv->overshot_dist_x < 0) {
1162 child_allocation->width = MAX (child_allocation->width + priv->overshot_dist_x, 0);
1167 hildon_pannable_area_size_allocate (GtkWidget * widget,
1168 GtkAllocation * allocation)
1170 GtkAllocation child_allocation;
1171 HildonPannableAreaPrivate *priv;
1172 GtkWidget *child = gtk_bin_get_child (GTK_BIN (widget));
1176 border_width = GTK_CONTAINER (widget)->border_width;
1178 widget->allocation = *allocation;
1180 priv = HILDON_PANNABLE_AREA (widget)->priv;
1182 if (GTK_WIDGET_REALIZED (widget)) {
1183 gdk_window_move_resize (widget->window,
1184 allocation->x + border_width,
1185 allocation->y + border_width,
1186 allocation->width - border_width * 2,
1187 allocation->height - border_width * 2);
1188 gdk_window_move_resize (priv->event_window,
1191 allocation->width - border_width * 2,
1192 allocation->height - border_width * 2);
1195 if (child && GTK_WIDGET_VISIBLE (child)) {
1197 hildon_pannable_area_child_allocate_calculate (widget,
1201 gtk_widget_size_allocate (child, &child_allocation);
1203 if (hildon_pannable_area_check_scrollbars (HILDON_PANNABLE_AREA (widget))) {
1204 hildon_pannable_area_child_allocate_calculate (widget,
1208 gtk_widget_size_allocate (child, &child_allocation);
1211 hv = priv->hadjust->value;
1212 vv = priv->vadjust->value;
1214 /* we have to do this after child size_allocate because page_size is
1215 * changed when we allocate the size of the children */
1216 if (priv->overshot_dist_y < 0) {
1217 priv->vadjust->value = priv->vadjust->upper - priv->vadjust->page_size;
1220 if (priv->overshot_dist_x < 0) {
1221 priv->hadjust->value = priv->hadjust->upper - priv->hadjust->page_size;
1224 if (hv != priv->hadjust->value)
1225 gtk_adjustment_value_changed (priv->hadjust);
1227 if (vv != priv->vadjust->value)
1228 gtk_adjustment_value_changed (priv->vadjust);
1231 hildon_pannable_area_check_scrollbars (HILDON_PANNABLE_AREA (widget));
1236 hildon_pannable_area_style_set (GtkWidget * widget,
1237 GtkStyle * previous_style)
1239 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1241 GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->
1242 style_set (widget, previous_style);
1244 gtk_widget_style_get (widget, "indicator-width", &priv->indicator_width, NULL);
1248 hildon_pannable_area_map (GtkWidget * widget)
1250 HildonPannableAreaPrivate *priv;
1252 priv = HILDON_PANNABLE_AREA (widget)->priv;
1254 gdk_window_show (widget->window);
1256 if (priv->event_window != NULL && !priv->enabled)
1257 gdk_window_show (priv->event_window);
1259 (*GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->map) (widget);
1261 if (priv->event_window != NULL && priv->enabled)
1262 gdk_window_show (priv->event_window);
1266 hildon_pannable_area_unmap (GtkWidget * widget)
1268 HildonPannableAreaPrivate *priv;
1270 priv = HILDON_PANNABLE_AREA (widget)->priv;
1272 if (priv->event_window != NULL)
1273 gdk_window_hide (priv->event_window);
1275 gdk_window_hide (widget->window);
1277 (*GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->unmap) (widget);
1281 hildon_pannable_area_grab_notify (GtkWidget *widget,
1282 gboolean was_grabbed,
1285 /* an internal widget has grabbed the focus and now has returned it,
1286 we have to do some release actions */
1288 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1290 priv->scroll_indicator_event_interrupt = 0;
1292 if ((!priv->scroll_indicator_timeout)&&(priv->scroll_indicator_alpha)>0.1) {
1293 priv->scroll_delay_counter = priv->scrollbar_fade_delay;
1295 hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget),
1296 priv->scroll_indicator_alpha);
1299 priv->last_type = 3;
1300 priv->moved = FALSE;
1304 #if USE_CAIRO_SCROLLBARS == 1
1307 rgb_from_gdkcolor (GdkColor *color, gdouble *r, gdouble *g, gdouble *b)
1309 *r = (color->red >> 8) / 255.0;
1310 *g = (color->green >> 8) / 255.0;
1311 *b = (color->blue >> 8) / 255.0;
1315 hildon_pannable_draw_vscroll (GtkWidget * widget,
1316 GdkColor *back_color,
1317 GdkColor *scroll_color)
1319 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1322 cairo_pattern_t *pattern;
1324 gint radius = (priv->vscroll_rect.width/2) - 1;
1326 cr = gdk_cairo_create(widget->window);
1328 /* Draw the background */
1329 rgb_from_gdkcolor (back_color, &r, &g, &b);
1330 cairo_set_source_rgb (cr, r, g, b);
1331 cairo_rectangle(cr, priv->vscroll_rect.x, priv->vscroll_rect.y,
1332 priv->vscroll_rect.width,
1333 priv->vscroll_rect.height);
1334 cairo_fill_preserve (cr);
1337 /* Calculate the scroll bar height and position */
1338 y = ((priv->vadjust->value - priv->vadjust->lower) / (priv->vadjust->upper - priv->vadjust->lower)) *
1339 (widget->allocation.height -
1340 (priv->hscroll_visible ? priv->indicator_width : 0));
1341 height = ((((priv->vadjust->value - priv->vadjust->lower) +
1342 priv->vadjust->page_size) /
1343 (priv->vadjust->upper - priv->vadjust->lower)) *
1344 (widget->allocation.height -
1345 (priv->hscroll_visible ? priv->indicator_width : 0))) - y;
1347 /* Set a minimum height */
1348 height = MAX (SCROLL_BAR_MIN_SIZE, height);
1350 /* Check the max y position */
1351 y = MIN (y, widget->allocation.height -
1352 (priv->hscroll_visible ? priv->hscroll_rect.height : 0) -
1355 /* Draw the scrollbar */
1356 rgb_from_gdkcolor (scroll_color, &r, &g, &b);
1358 pattern = cairo_pattern_create_linear(radius+1, y, radius+1,y + height);
1359 cairo_pattern_add_color_stop_rgb(pattern, 0, r, g, b);
1360 cairo_pattern_add_color_stop_rgb(pattern, 1, r/2, g/2, b/2);
1361 cairo_set_source(cr, pattern);
1363 cairo_pattern_destroy(pattern);
1365 cairo_arc(cr, priv->vscroll_rect.x + radius + 1, y + radius + 1, radius, G_PI, 0);
1366 cairo_line_to(cr, priv->vscroll_rect.x + (radius * 2) + 1, y + height - radius);
1367 cairo_arc(cr, priv->vscroll_rect.x + radius + 1, y + height - radius, radius, 0, G_PI);
1368 cairo_line_to(cr, priv->vscroll_rect.x + 1, y + height - radius);
1371 cairo_paint_with_alpha(cr, priv->scroll_indicator_alpha);
1377 hildon_pannable_draw_hscroll (GtkWidget * widget,
1378 GdkColor *back_color,
1379 GdkColor *scroll_color)
1381 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1384 cairo_pattern_t *pattern;
1386 gint radius = (priv->hscroll_rect.height/2) - 1;
1388 cr = gdk_cairo_create(widget->window);
1390 /* Draw the background */
1391 rgb_from_gdkcolor (back_color, &r, &g, &b);
1392 cairo_set_source_rgb (cr, r, g, b);
1393 cairo_rectangle(cr, priv->hscroll_rect.x, priv->hscroll_rect.y,
1394 priv->hscroll_rect.width,
1395 priv->hscroll_rect.height);
1396 cairo_fill_preserve (cr);
1399 /* calculate the scrollbar width and position */
1400 x = ((priv->hadjust->value - priv->hadjust->lower) / (priv->hadjust->upper - priv->hadjust->lower)) *
1401 (widget->allocation.width - (priv->vscroll_visible ? priv->indicator_width : 0));
1402 width =((((priv->hadjust->value - priv->hadjust->lower) +
1403 priv->hadjust->page_size) / (priv->hadjust->upper - priv->hadjust->lower)) *
1404 (widget->allocation.width -
1405 (priv->vscroll_visible ? priv->indicator_width : 0))) - x;
1407 /* Set a minimum width */
1408 width = MAX (SCROLL_BAR_MIN_SIZE, width);
1410 /* Check the max x position */
1411 x = MIN (x, widget->allocation.width -
1412 (priv->vscroll_visible ? priv->vscroll_rect.width : 0) -
1415 /* Draw the scrollbar */
1416 rgb_from_gdkcolor (scroll_color, &r, &g, &b);
1418 pattern = cairo_pattern_create_linear(x, radius+1, x+width, radius+1);
1419 cairo_pattern_add_color_stop_rgb(pattern, 0, r, g, b);
1420 cairo_pattern_add_color_stop_rgb(pattern, 1, r/2, g/2, b/2);
1421 cairo_set_source(cr, pattern);
1423 cairo_pattern_destroy(pattern);
1425 cairo_arc_negative(cr, x + radius + 1, priv->hscroll_rect.y + radius + 1, radius, 3*G_PI_2, G_PI_2);
1426 cairo_line_to(cr, x + width - radius, priv->hscroll_rect.y + (radius * 2) + 1);
1427 cairo_arc_negative(cr, x + width - radius, priv->hscroll_rect.y + radius + 1, radius, G_PI_2, 3*G_PI_2);
1428 cairo_line_to(cr, x + width - radius, priv->hscroll_rect.y + 1);
1431 cairo_paint_with_alpha(cr, priv->scroll_indicator_alpha);
1436 #else /* USE_CAIRO_SCROLLBARS */
1439 tranparency_color (GdkColor *color,
1442 gdouble transparency)
1446 diff = colora.red - colorb.red;
1447 color->red = colora.red-diff*transparency;
1449 diff = colora.green - colorb.green;
1450 color->green = colora.green-diff*transparency;
1452 diff = colora.blue - colorb.blue;
1453 color->blue = colora.blue-diff*transparency;
1457 hildon_pannable_draw_vscroll (GtkWidget *widget,
1458 GdkColor *back_color,
1459 GdkColor *scroll_color)
1461 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1463 GdkColor transp_color;
1464 GdkGC *gc = widget->style->fg_gc[GTK_STATE_INSENSITIVE];
1466 gdk_draw_rectangle (widget->window,
1467 widget->style->bg_gc[GTK_STATE_NORMAL],
1469 priv->vscroll_rect.x, priv->vscroll_rect.y,
1470 priv->vscroll_rect.width,
1471 priv->vscroll_rect.height);
1473 y = ((priv->vadjust->value - priv->vadjust->lower) / (priv->vadjust->upper - priv->vadjust->lower)) *
1474 (widget->allocation.height - (priv->hscroll_visible ? priv->indicator_width : 0));
1475 height = ((((priv->vadjust->value - priv->vadjust->lower) + priv->vadjust->page_size) /
1476 (priv->vadjust->upper - priv->vadjust->lower)) *
1477 (widget->allocation.height -
1478 (priv->hscroll_visible ? priv->indicator_width : 0))) - y;
1480 /* Set a minimum height */
1481 height = MAX (SCROLL_BAR_MIN_SIZE, height);
1483 /* Check the max y position */
1484 y = MIN (y, widget->allocation.height -
1485 (priv->hscroll_visible ? priv->hscroll_rect.height : 0) -
1488 if (priv->scroll_indicator_alpha < 1.0) {
1489 tranparency_color (&transp_color, *back_color, *scroll_color,
1490 priv->scroll_indicator_alpha);
1492 gdk_gc_set_rgb_fg_color (priv->scrollbars_gc, &transp_color);
1494 gc = priv->scrollbars_gc;
1497 gdk_draw_rectangle (widget->window, gc,
1498 TRUE, priv->vscroll_rect.x, y,
1499 priv->vscroll_rect.width, height);
1503 hildon_pannable_draw_hscroll (GtkWidget *widget,
1504 GdkColor *back_color,
1505 GdkColor *scroll_color)
1507 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1509 GdkColor transp_color;
1510 GdkGC *gc = widget->style->fg_gc[GTK_STATE_INSENSITIVE];
1512 gdk_draw_rectangle (widget->window,
1513 widget->style->bg_gc[GTK_STATE_INSENSITIVE],
1515 priv->hscroll_rect.x, priv->hscroll_rect.y,
1516 priv->hscroll_rect.width,
1517 priv->hscroll_rect.height);
1519 /* calculate the scrollbar width and position */
1520 x = ((priv->hadjust->value - priv->hadjust->lower) / (priv->hadjust->upper - priv->hadjust->lower)) *
1521 (widget->allocation.width - (priv->vscroll_visible ? priv->indicator_width : 0));
1522 width =((((priv->hadjust->value - priv->hadjust->lower) +
1523 priv->hadjust->page_size) / (priv->hadjust->upper - priv->hadjust->lower)) *
1524 (widget->allocation.width -
1525 (priv->vscroll_visible ? priv->indicator_width : 0))) - x;
1527 /* Set a minimum width */
1528 width = MAX (SCROLL_BAR_MIN_SIZE, width);
1530 /* Check the max x position */
1531 x = MIN (x, widget->allocation.width -
1532 (priv->vscroll_visible ? priv->vscroll_rect.width : 0) -
1535 if (priv->scroll_indicator_alpha < 1.0) {
1536 tranparency_color (&transp_color, *back_color, *scroll_color,
1537 priv->scroll_indicator_alpha);
1539 gdk_gc_set_rgb_fg_color (priv->scrollbars_gc, &transp_color);
1541 gc = priv->scrollbars_gc;
1544 gdk_draw_rectangle (widget->window, gc,
1545 TRUE, x, priv->hscroll_rect.y, width,
1546 priv->hscroll_rect.height);
1549 #endif /* USE_CAIRO_SCROLLBARS */
1552 hildon_pannable_area_initial_effect (GtkWidget * widget)
1554 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1556 if (priv->initial_hint) {
1557 if (priv->vscroll_visible || priv->hscroll_visible) {
1559 priv->scroll_indicator_event_interrupt = 0;
1560 priv->scroll_delay_counter = priv->scrollbar_fade_delay;
1562 hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget), 1.0);
1568 hildon_pannable_area_launch_fade_timeout (HildonPannableArea * area,
1571 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (area)->priv;
1573 priv->scroll_indicator_alpha = alpha;
1575 if (!priv->scroll_indicator_timeout)
1576 priv->scroll_indicator_timeout =
1577 gdk_threads_add_timeout (SCROLL_FADE_TIMEOUT,
1578 (GSourceFunc) hildon_pannable_area_scroll_indicator_fade,
1583 hildon_pannable_area_adjust_changed (HildonPannableArea * area,
1586 if (GTK_WIDGET_REALIZED (area))
1587 hildon_pannable_area_refresh (area);
1591 hildon_pannable_area_adjust_value_changed (HildonPannableArea * area,
1594 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (area)->priv;
1596 gint x = priv->x_offset;
1597 gint y = priv->y_offset;
1599 priv->x_offset = priv->hadjust->value;
1600 xdiff = x - priv->x_offset;
1601 priv->y_offset = priv->vadjust->value;
1602 ydiff = y - priv->y_offset;
1604 if ((xdiff || ydiff) && GTK_WIDGET_DRAWABLE (area)) {
1605 hildon_pannable_area_redraw (area);
1607 if ((priv->vscroll_visible) || (priv->hscroll_visible)) {
1608 priv->scroll_indicator_event_interrupt = 0;
1609 priv->scroll_delay_counter = priv->scrollbar_fade_delay;
1611 hildon_pannable_area_launch_fade_timeout (area, 1.0);
1617 hildon_pannable_area_redraw (HildonPannableArea * area)
1619 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (area)->priv;
1621 /* Redraw scroll indicators */
1622 if (GTK_WIDGET_DRAWABLE (area)) {
1623 if (priv->hscroll_visible) {
1624 gdk_window_invalidate_rect (GTK_WIDGET (area)->window,
1625 &priv->hscroll_rect, FALSE);
1628 if (priv->vscroll_visible) {
1629 gdk_window_invalidate_rect (GTK_WIDGET (area)->window,
1630 &priv->vscroll_rect, FALSE);
1636 hildon_pannable_area_scroll_indicator_fade(HildonPannableArea * area)
1638 HildonPannableAreaPrivate *priv = area->priv;
1640 /* if moving do not fade out */
1641 if (((ABS (priv->vel_y)>priv->vmin)||
1642 (ABS (priv->vel_x)>priv->vmin))&&(!priv->button_pressed)) {
1647 if (priv->scroll_indicator_event_interrupt) {
1648 /* Stop a fade out, and fade back in */
1649 if (priv->scroll_indicator_alpha > 0.9) {
1650 priv->scroll_indicator_alpha = 1.0;
1651 priv->scroll_indicator_timeout = 0;
1655 priv->scroll_indicator_alpha += 0.2;
1656 hildon_pannable_area_redraw (area);
1662 if ((priv->scroll_indicator_alpha > 0.9) &&
1663 (priv->scroll_delay_counter > 0)) {
1664 priv->scroll_delay_counter--;
1669 if (!priv->scroll_indicator_event_interrupt) {
1670 /* Continue fade out */
1671 if (priv->scroll_indicator_alpha < 0.1) {
1672 priv->scroll_indicator_timeout = 0;
1673 priv->scroll_indicator_alpha = 0.0;
1677 priv->scroll_indicator_alpha -= 0.2;
1678 hildon_pannable_area_redraw (area);
1688 hildon_pannable_area_expose_event (GtkWidget * widget,
1689 GdkEventExpose * event)
1692 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1693 #if USE_CAIRO_SCROLLBARS == 1
1694 GdkColor back_color = widget->style->bg[GTK_STATE_NORMAL];
1695 GdkColor scroll_color = widget->style->base[GTK_STATE_SELECTED];
1696 #else /* USE_CAIRO_SCROLLBARS */
1697 GdkColor back_color = widget->style->bg[GTK_STATE_NORMAL];
1698 GdkColor scroll_color = widget->style->fg[GTK_STATE_INSENSITIVE];
1701 if (G_UNLIKELY (priv->initial_effect)) {
1702 hildon_pannable_area_initial_effect (widget);
1704 priv->initial_effect = FALSE;
1707 if (gtk_bin_get_child (GTK_BIN (widget))) {
1709 if (priv->scroll_indicator_alpha > 0.1) {
1710 if (priv->vscroll_visible) {
1711 hildon_pannable_draw_vscroll (widget, &back_color, &scroll_color);
1713 if (priv->hscroll_visible) {
1714 hildon_pannable_draw_hscroll (widget, &back_color, &scroll_color);
1718 /* draw overshooting rectangles */
1719 if (priv->overshot_dist_y > 0) {
1720 gint overshot_height;
1722 overshot_height = MIN (priv->overshot_dist_y, widget->allocation.height -
1723 (priv->hscroll_visible ? priv->hscroll_rect.height : 0));
1725 gdk_draw_rectangle (widget->window,
1726 widget->style->bg_gc[GTK_STATE_NORMAL],
1730 widget->allocation.width -
1731 (priv->vscroll_visible ? priv->vscroll_rect.width : 0),
1733 } else if (priv->overshot_dist_y < 0) {
1734 gint overshot_height;
1738 MAX (priv->overshot_dist_y,
1739 -(widget->allocation.height -
1740 (priv->hscroll_visible ? priv->hscroll_rect.height : 0)));
1742 overshot_y = MAX (widget->allocation.height +
1744 (priv->hscroll_visible ? priv->hscroll_rect.height : 0), 0);
1746 gdk_draw_rectangle (widget->window,
1747 widget->style->bg_gc[GTK_STATE_NORMAL],
1751 widget->allocation.width -
1752 priv->vscroll_rect.width,
1756 if (priv->overshot_dist_x > 0) {
1757 gint overshot_width;
1759 overshot_width = MIN (priv->overshot_dist_x, widget->allocation.width -
1760 (priv->vscroll_visible ? priv->vscroll_rect.width : 0));
1762 gdk_draw_rectangle (widget->window,
1763 widget->style->bg_gc[GTK_STATE_NORMAL],
1768 widget->allocation.height -
1769 (priv->hscroll_visible ? priv->hscroll_rect.height : 0));
1770 } else if (priv->overshot_dist_x < 0) {
1771 gint overshot_width;
1775 MAX (priv->overshot_dist_x,
1776 -(widget->allocation.width -
1777 (priv->vscroll_visible ? priv->vscroll_rect.width : 0)));
1779 overshot_x = MAX (widget->allocation.width +
1781 (priv->vscroll_visible ? priv->vscroll_rect.width : 0), 0);
1783 gdk_draw_rectangle (widget->window,
1784 widget->style->bg_gc[GTK_STATE_NORMAL],
1789 widget->allocation.height -
1790 priv->hscroll_rect.height);
1795 return GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->expose_event (widget, event);
1799 hildon_pannable_area_get_topmost (GdkWindow * window,
1801 gint * tx, gint * ty,
1804 /* Find the GdkWindow at the given point, by recursing from a given
1805 * parent GdkWindow. Optionally return the co-ordinates transformed
1806 * relative to the child window.
1809 GList *c, *children;
1810 GdkWindow *selected_window = NULL;
1812 gdk_drawable_get_size (GDK_DRAWABLE (window), &width, &height);
1813 if ((x < 0) || (x >= width) || (y < 0) || (y >= height))
1816 children = gdk_window_peek_children (window);
1823 selected_window = window;
1826 for (c = children; c; c = c->next) {
1827 GdkWindow *child = (GdkWindow *) c->data;
1830 gdk_drawable_get_size (GDK_DRAWABLE (child), &width, &height);
1831 gdk_window_get_position (child, &wx, &wy);
1833 if ((x >= wx) && (x < (wx + width)) && (y >= wy) && (y < (wy + height)) &&
1834 (gdk_window_is_visible (child))) {
1836 if (gdk_window_peek_children (child)) {
1837 selected_window = hildon_pannable_area_get_topmost (child, x-wx, y-wy,
1839 if (!selected_window) {
1844 selected_window = child;
1847 if ((gdk_window_get_events (child)&mask)) {
1852 selected_window = child;
1858 return selected_window;
1862 synth_crossing (GdkWindow * child,
1864 gint x_root, gint y_root,
1865 guint32 time, gboolean in)
1867 GdkEventCrossing *crossing_event;
1868 GdkEventType type = in ? GDK_ENTER_NOTIFY : GDK_LEAVE_NOTIFY;
1870 /* Send synthetic enter event */
1871 crossing_event = (GdkEventCrossing *) gdk_event_new (type);
1872 ((GdkEventAny *) crossing_event)->type = type;
1873 ((GdkEventAny *) crossing_event)->window = g_object_ref (child);
1874 ((GdkEventAny *) crossing_event)->send_event = FALSE;
1875 crossing_event->subwindow = g_object_ref (child);
1876 crossing_event->time = time;
1877 crossing_event->x = x;
1878 crossing_event->y = y;
1879 crossing_event->x_root = x_root;
1880 crossing_event->y_root = y_root;
1881 crossing_event->mode = GDK_CROSSING_NORMAL;
1882 crossing_event->detail = GDK_NOTIFY_UNKNOWN;
1883 crossing_event->focus = FALSE;
1884 crossing_event->state = 0;
1885 gdk_event_put ((GdkEvent *) crossing_event);
1886 gdk_event_free ((GdkEvent *) crossing_event);
1890 hildon_pannable_area_button_press_cb (GtkWidget * widget,
1891 GdkEventButton * event)
1894 HildonPannableArea *area = HILDON_PANNABLE_AREA (widget);
1895 HildonPannableAreaPrivate *priv = area->priv;
1897 if ((!priv->enabled) || (event->button != 1) ||
1898 ((event->time == priv->last_time) &&
1899 (priv->last_type == 1)) || (gtk_bin_get_child (GTK_BIN (widget)) == NULL))
1902 priv->scroll_indicator_event_interrupt = 1;
1904 hildon_pannable_area_launch_fade_timeout (area,
1905 priv->scroll_indicator_alpha);
1907 priv->last_time = event->time;
1908 priv->last_type = 1;
1910 priv->scroll_to_x = -1;
1911 priv->scroll_to_y = -1;
1913 if (priv->button_pressed && priv->child) {
1914 /* Widget stole focus on last click, send crossing-out event */
1915 synth_crossing (priv->child, 0, 0, event->x_root, event->y_root,
1916 event->time, FALSE);
1924 /* Don't allow a click if we're still moving fast */
1925 if ((ABS (priv->vel_x) <= (priv->vmax * priv->vfast_factor)) &&
1926 (ABS (priv->vel_y) <= (priv->vmax * priv->vfast_factor)))
1928 hildon_pannable_area_get_topmost (gtk_bin_get_child (GTK_BIN (widget))->window,
1929 event->x, event->y, &x, &y, GDK_BUTTON_PRESS_MASK);
1933 priv->button_pressed = TRUE;
1935 /* Stop scrolling on mouse-down (so you can flick, then hold to stop) */
1938 if (priv->idle_id) {
1939 g_source_remove (priv->idle_id);
1941 g_signal_emit (area, pannable_area_signals[PANNING_FINISHED], 0);
1946 gdk_drawable_get_size (priv->child, &priv->child_width,
1947 &priv->child_height);
1948 priv->last_in = TRUE;
1950 g_object_add_weak_pointer ((GObject *) priv->child,
1951 (gpointer) & priv->child);
1953 event = (GdkEventButton *) gdk_event_copy ((GdkEvent *) event);
1959 synth_crossing (priv->child, x, y, event->x_root,
1960 event->y_root, event->time, TRUE);
1962 /* Send synthetic click (button press/release) event */
1963 ((GdkEventAny *) event)->window = g_object_ref (priv->child);
1965 gdk_event_put ((GdkEvent *) event);
1966 gdk_event_free ((GdkEvent *) event);
1974 hildon_pannable_area_check_scrollbars (HildonPannableArea * area)
1976 HildonPannableAreaPrivate *priv = area->priv;
1977 gboolean prev_hscroll_visible, prev_vscroll_visible;
1979 prev_hscroll_visible = priv->hscroll_visible;
1980 prev_vscroll_visible = priv->vscroll_visible;
1982 if (!gtk_bin_get_child (GTK_BIN (area))) {
1983 priv->vscroll_visible = FALSE;
1984 priv->hscroll_visible = FALSE;
1986 switch (priv->hscrollbar_policy) {
1987 case GTK_POLICY_ALWAYS:
1988 priv->hscroll_visible = TRUE;
1990 case GTK_POLICY_NEVER:
1991 priv->hscroll_visible = FALSE;
1994 priv->hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
1995 priv->hadjust->page_size);
1998 switch (priv->vscrollbar_policy) {
1999 case GTK_POLICY_ALWAYS:
2000 priv->vscroll_visible = TRUE;
2002 case GTK_POLICY_NEVER:
2003 priv->vscroll_visible = FALSE;
2006 priv->vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
2007 priv->vadjust->page_size);
2010 /* Store the vscroll/hscroll areas for redrawing */
2011 if (priv->vscroll_visible) {
2012 GtkAllocation *allocation = >K_WIDGET (area)->allocation;
2013 priv->vscroll_rect.x = allocation->width - priv->indicator_width;
2014 priv->vscroll_rect.y = 0;
2015 priv->vscroll_rect.width = priv->indicator_width;
2016 priv->vscroll_rect.height = allocation->height -
2017 (priv->hscroll_visible ? priv->indicator_width : 0);
2019 if (priv->hscroll_visible) {
2020 GtkAllocation *allocation = >K_WIDGET (area)->allocation;
2021 priv->hscroll_rect.y = allocation->height - priv->indicator_width;
2022 priv->hscroll_rect.x = 0;
2023 priv->hscroll_rect.height = priv->indicator_width;
2024 priv->hscroll_rect.width = allocation->width -
2025 (priv->vscroll_visible ? priv->indicator_width : 0);
2029 return ((priv->hscroll_visible != prev_hscroll_visible) ||
2030 (priv->vscroll_visible != prev_vscroll_visible));
2034 hildon_pannable_area_refresh (HildonPannableArea * area)
2036 if (GTK_WIDGET_DRAWABLE (area) &&
2037 hildon_pannable_area_check_scrollbars (area)) {
2038 HildonPannableAreaPrivate *priv = area->priv;
2040 gtk_widget_queue_resize (GTK_WIDGET (area));
2042 if ((priv->vscroll_visible) || (priv->hscroll_visible)) {
2043 priv->scroll_indicator_event_interrupt = 0;
2044 priv->scroll_delay_counter = area->priv->scrollbar_fade_delay;
2046 hildon_pannable_area_launch_fade_timeout (area, 1.0);
2049 hildon_pannable_area_redraw (area);
2053 /* Scroll by a particular amount (in pixels). Optionally, return if
2054 * the scroll on a particular axis was successful.
2057 hildon_pannable_axis_scroll (HildonPannableArea *area,
2058 GtkAdjustment *adjust,
2062 gint *overshot_dist,
2068 HildonPannableAreaPrivate *priv = area->priv;
2070 dist = gtk_adjustment_get_value (adjust) - inc;
2073 * We use overshot_dist to define the distance of the current overshoot,
2074 * and overshooting to define the direction/whether or not we are overshot
2076 if (!(*overshooting)) {
2078 /* Initiation of the overshoot happens when the finger is released
2079 * and the current position of the pannable contents are out of range
2081 if (dist < adjust->lower) {
2084 dist = adjust->lower;
2086 if (overshoot_max!=0) {
2089 *overshot_dist = CLAMP (*overshot_dist + *vel, 0, overshoot_max);
2090 *vel = MIN (priv->vmax_overshooting, *vel);
2091 gtk_widget_queue_resize (GTK_WIDGET (area));
2095 } else if (dist > adjust->upper - adjust->page_size) {
2098 dist = adjust->upper - adjust->page_size;
2100 if (overshoot_max!=0) {
2103 *overshot_dist = CLAMP (*overshot_dist + *vel, -overshoot_max, 0);
2104 *vel = MAX (-priv->vmax_overshooting, *vel);
2105 gtk_widget_queue_resize (GTK_WIDGET (area));
2110 if ((*scroll_to) != -1) {
2111 if (((inc < 0)&&(*scroll_to <= dist))||
2112 ((inc > 0)&&(*scroll_to >= dist))) {
2120 adjust->value = dist;
2122 if (!priv->button_pressed) {
2124 /* When the overshoot has started we continue for
2125 * PROP_BOUNCE_STEPS more steps into the overshoot before we
2126 * reverse direction. The deceleration factor is calculated
2127 * based on the percentage distance from the first item with
2128 * each iteration, therefore always returning us to the
2129 * top/bottom most element
2131 if (*overshot_dist > 0) {
2133 if ((*overshooting < priv->bounce_steps) && (*vel > 0)) {
2135 *vel = (((gdouble)*overshot_dist)/overshoot_max) * (*vel);
2136 } else if ((*overshooting >= priv->bounce_steps) && (*vel > 0)) {
2138 } else if ((*overshooting > 1) && (*vel < 0)) {
2139 /* we add the MIN in order to avoid very small speeds */
2140 *vel = MIN ((((gdouble)*overshot_dist)*0.4) * -1, -2.0);
2143 *overshot_dist = CLAMP (*overshot_dist + *vel, 0, overshoot_max);
2145 gtk_widget_queue_resize (GTK_WIDGET (area));
2147 } else if (*overshot_dist < 0) {
2149 if ((*overshooting < priv->bounce_steps) && (*vel < 0)) {
2151 *vel = (((gdouble)*overshot_dist)/overshoot_max) * (*vel) * -1;
2152 } else if ((*overshooting >= priv->bounce_steps) && (*vel < 0)) {
2154 } else if ((*overshooting > 1) && (*vel > 0)) {
2155 /* we add the MAX in order to avoid very small speeds */
2156 *vel = MAX ((((gdouble)*overshot_dist)*0.4) * -1, 2.0);
2159 *overshot_dist = CLAMP (*overshot_dist + (*vel), -overshoot_max, 0);
2161 gtk_widget_queue_resize (GTK_WIDGET (area));
2166 gtk_widget_queue_resize (GTK_WIDGET (area));
2170 gint overshot_dist_old = *overshot_dist;
2172 if (*overshot_dist > 0) {
2173 *overshot_dist = CLAMP ((*overshot_dist) + inc, 0, overshoot_max);
2174 } else if (*overshot_dist < 0) {
2175 *overshot_dist = CLAMP ((*overshot_dist) + inc, -1 * overshoot_max, 0);
2178 adjust->value = CLAMP (dist,
2184 if (*overshot_dist != overshot_dist_old)
2185 gtk_widget_queue_resize (GTK_WIDGET (area));
2191 hildon_pannable_area_scroll (HildonPannableArea *area,
2192 gdouble x, gdouble y)
2195 HildonPannableAreaPrivate *priv = area->priv;
2196 gboolean hscroll_visible, vscroll_visible;
2199 if (gtk_bin_get_child (GTK_BIN (area)) == NULL)
2202 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
2203 priv->vadjust->page_size);
2204 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
2205 priv->hadjust->page_size);
2210 hv = priv->hadjust->value;
2211 vv = priv->vadjust->value;
2213 if (vscroll_visible) {
2214 hildon_pannable_axis_scroll (area, priv->vadjust, &priv->vel_y, y,
2215 &priv->overshooting_y, &priv->overshot_dist_y,
2216 &priv->scroll_to_y, priv->vovershoot_max, &sy);
2221 if (hscroll_visible) {
2222 hildon_pannable_axis_scroll (area, priv->hadjust, &priv->vel_x, x,
2223 &priv->overshooting_x, &priv->overshot_dist_x,
2224 &priv->scroll_to_x, priv->hovershoot_max, &sx);
2229 if (hv != priv->hadjust->value)
2230 gtk_adjustment_value_changed (priv->hadjust);
2232 if (vv != priv->vadjust->value)
2233 gtk_adjustment_value_changed (priv->vadjust);
2235 /* If the scroll on a particular axis wasn't succesful, reset the
2236 * initial scroll position to the new mouse co-ordinate. This means
2237 * when you get to the top of the page, dragging down works immediately.
2239 if (priv->mode == HILDON_PANNABLE_AREA_MODE_ACCEL) {
2251 hildon_pannable_area_timeout (HildonPannableArea * area)
2253 HildonPannableAreaPrivate *priv = area->priv;
2255 if ((!priv->enabled) || (priv->mode == HILDON_PANNABLE_AREA_MODE_PUSH)) {
2257 g_signal_emit (area, pannable_area_signals[PANNING_FINISHED], 0);
2262 if (!priv->button_pressed) {
2263 /* Decelerate gradually when pointer is raised */
2264 if ((!priv->overshot_dist_y) &&
2265 (!priv->overshot_dist_x)) {
2267 /* in case we move to a specific point do not decelerate when arriving */
2268 if ((priv->scroll_to_x != -1)||(priv->scroll_to_y != -1)) {
2270 if (ABS (priv->vel_x) >= 1.5) {
2271 priv->vel_x *= priv->decel;
2274 if (ABS (priv->vel_y) >= 1.5) {
2275 priv->vel_y *= priv->decel;
2279 if ((!priv->low_friction_mode) ||
2280 ((priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ) &&
2281 (ABS (priv->vel_x) < 0.8*priv->vmax)))
2282 priv->vel_x *= priv->decel;
2284 if ((!priv->low_friction_mode) ||
2285 ((priv->mov_mode&HILDON_MOVEMENT_MODE_VERT) &&
2286 (ABS (priv->vel_y) < 0.8*priv->vmax)))
2287 priv->vel_y *= priv->decel;
2289 if ((ABS (priv->vel_x) < 1.0) && (ABS (priv->vel_y) < 1.0)) {
2294 g_signal_emit (area, pannable_area_signals[PANNING_FINISHED], 0);
2300 } else if (priv->mode == HILDON_PANNABLE_AREA_MODE_AUTO) {
2306 hildon_pannable_area_scroll (area, priv->vel_x, priv->vel_y);
2312 hildon_pannable_area_calculate_velocity (gdouble *vel,
2316 gdouble drag_inertia,
2322 if (ABS (dist) >= RATIO_TOLERANCE) {
2323 rawvel = (dist / ABS (delta)) * force;
2324 *vel = *vel * (1 - drag_inertia) +
2325 rawvel * drag_inertia;
2326 *vel = *vel > 0 ? MIN (*vel, vmax)
2327 : MAX (*vel, -1 * vmax);
2332 hildon_pannable_area_motion_event_scroll_timeout (HildonPannableArea *area)
2334 HildonPannableAreaPrivate *priv = area->priv;
2336 if ((priv->motion_x != 0)||(priv->motion_y != 0))
2337 hildon_pannable_area_scroll (area, priv->motion_x, priv->motion_y);
2339 priv->motion_event_scroll_timeout = 0;
2345 hildon_pannable_area_motion_event_scroll (HildonPannableArea *area,
2346 gdouble x, gdouble y)
2348 HildonPannableAreaPrivate *priv = area->priv;
2350 if (priv->motion_event_scroll_timeout) {
2352 priv->motion_x += x;
2353 priv->motion_y += y;
2357 /* we do not delay the first event but the next ones */
2358 hildon_pannable_area_scroll (area, x, y);
2363 priv->motion_event_scroll_timeout = gdk_threads_add_timeout
2364 ((gint) (1000.0 / (gdouble) MOTION_EVENTS_PER_SECOND),
2365 (GSourceFunc) hildon_pannable_area_motion_event_scroll_timeout, area);
2370 hildon_pannable_area_check_move (HildonPannableArea *area,
2371 GdkEventMotion * event,
2375 HildonPannableAreaPrivate *priv = area->priv;
2377 if (priv->first_drag && (!priv->moved) &&
2378 ((ABS (*x) > (priv->panning_threshold))
2379 || (ABS (*y) > (priv->panning_threshold)))) {
2384 if (priv->first_drag) {
2385 gboolean vscroll_visible;
2386 gboolean hscroll_visible;
2388 if (ABS (priv->iy - event->y) >=
2389 ABS (priv->ix - event->x)) {
2391 g_signal_emit (area,
2392 pannable_area_signals[VERTICAL_MOVEMENT],
2393 0, (priv->iy > event->y) ?
2394 HILDON_MOVEMENT_UP :
2395 HILDON_MOVEMENT_DOWN,
2396 (gdouble)priv->ix, (gdouble)priv->iy);
2398 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
2399 priv->vadjust->page_size);
2401 if (!((vscroll_visible)&&
2402 (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT))) {
2404 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
2405 priv->hadjust->page_size);
2407 /* even in case we do not have to move we check if this
2408 could be a fake horizontal movement */
2409 if (!((hscroll_visible)&&
2410 (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ)) ||
2411 (ABS (priv->iy - event->y) -
2412 ABS (priv->ix - event->x) >= priv->direction_error_margin))
2413 priv->moved = FALSE;
2417 g_signal_emit (area,
2418 pannable_area_signals[HORIZONTAL_MOVEMENT],
2419 0, (priv->ix > event->x) ?
2420 HILDON_MOVEMENT_LEFT :
2421 HILDON_MOVEMENT_RIGHT,
2422 (gdouble)priv->ix, (gdouble)priv->iy);
2424 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
2425 priv->hadjust->page_size);
2427 if (!((hscroll_visible)&&
2428 (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ))) {
2430 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
2431 priv->vadjust->page_size);
2433 /* even in case we do not have to move we check if this
2434 could be a fake vertical movement */
2435 if (!((vscroll_visible) &&
2436 (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT)) ||
2437 (ABS (priv->ix - event->x) -
2438 ABS (priv->iy - event->y) >= priv->direction_error_margin))
2439 priv->moved = FALSE;
2443 if ((priv->moved)&&(priv->child)) {
2446 pos_x = priv->cx + (event->x - priv->ix);
2447 pos_y = priv->cy + (event->y - priv->iy);
2449 synth_crossing (priv->child, pos_x, pos_y, event->x_root,
2450 event->y_root, event->time, FALSE);
2454 gboolean result_val;
2456 g_signal_emit (area,
2457 pannable_area_signals[PANNING_STARTED],
2460 priv->moved = !result_val;
2464 priv->first_drag = FALSE;
2466 if ((priv->mode != HILDON_PANNABLE_AREA_MODE_PUSH) &&
2467 (priv->mode != HILDON_PANNABLE_AREA_MODE_AUTO)) {
2470 priv->idle_id = gdk_threads_add_timeout ((gint)
2471 (1000.0 / (gdouble) priv->sps),
2473 hildon_pannable_area_timeout, area);
2479 hildon_pannable_area_handle_move (HildonPannableArea *area,
2480 GdkEventMotion * event,
2484 HildonPannableAreaPrivate *priv = area->priv;
2487 switch (priv->mode) {
2488 case HILDON_PANNABLE_AREA_MODE_PUSH:
2489 /* Scroll by the amount of pixels the cursor has moved
2490 * since the last motion event.
2492 hildon_pannable_area_motion_event_scroll (area, *x, *y);
2496 case HILDON_PANNABLE_AREA_MODE_ACCEL:
2497 /* Set acceleration relative to the initial click */
2498 priv->ex = event->x;
2499 priv->ey = event->y;
2500 priv->vel_x = ((*x > 0) ? 1 : -1) *
2502 (gdouble) GTK_WIDGET (area)->allocation.width) *
2503 (priv->vmax - priv->vmin)) + priv->vmin);
2504 priv->vel_y = ((*y > 0) ? 1 : -1) *
2506 (gdouble) GTK_WIDGET (area)->allocation.height) *
2507 (priv->vmax - priv->vmin)) + priv->vmin);
2509 case HILDON_PANNABLE_AREA_MODE_AUTO:
2511 delta = event->time - priv->last_time;
2513 if (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT) {
2514 gdouble dist = event->y - priv->y;
2516 hildon_pannable_area_calculate_velocity (&priv->vel_y,
2528 if (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ) {
2529 gdouble dist = event->x - priv->x;
2531 hildon_pannable_area_calculate_velocity (&priv->vel_x,
2543 hildon_pannable_area_motion_event_scroll (area, *x, *y);
2545 if (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ)
2547 if (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT)
2557 hildon_pannable_area_motion_notify_cb (GtkWidget * widget,
2558 GdkEventMotion * event)
2560 HildonPannableArea *area = HILDON_PANNABLE_AREA (widget);
2561 HildonPannableAreaPrivate *priv = area->priv;
2564 if (gtk_bin_get_child (GTK_BIN (widget)) == NULL)
2567 if ((!priv->enabled) || (!priv->button_pressed) ||
2568 ((event->time == priv->last_time) && (priv->last_type == 2))) {
2569 gdk_window_get_pointer (widget->window, NULL, NULL, 0);
2573 if (priv->last_type == 1) {
2574 priv->first_drag = TRUE;
2577 x = event->x - priv->x;
2578 y = event->y - priv->y;
2581 hildon_pannable_area_check_move (area, event, &x, &y);
2585 hildon_pannable_area_handle_move (area, event, &x, &y);
2586 } else if (priv->child) {
2590 pos_x = priv->cx + (event->x - priv->ix);
2591 pos_y = priv->cy + (event->y - priv->iy);
2593 in = (((0 <= pos_x)&&(priv->child_width >= pos_x)) &&
2594 ((0 <= pos_y)&&(priv->child_height >= pos_y)));
2596 if (((!priv->last_in)&&in)||((priv->last_in)&&(!in))) {
2598 synth_crossing (priv->child, pos_x, pos_y, event->x_root,
2599 event->y_root, event->time, in);
2605 priv->last_time = event->time;
2606 priv->last_type = 2;
2609 /* Send motion notify to child */
2610 event = (GdkEventMotion *) gdk_event_copy ((GdkEvent *) event);
2611 event->x = priv->cx + (event->x - priv->ix);
2612 event->y = priv->cy + (event->y - priv->iy);
2613 event->window = g_object_ref (priv->child);
2614 gdk_event_put ((GdkEvent *) event);
2615 gdk_event_free ((GdkEvent *) event);
2618 gdk_window_get_pointer (widget->window, NULL, NULL, 0);
2624 hildon_pannable_leave_notify_event (GtkWidget *widget,
2625 GdkEventCrossing *event)
2627 HildonPannableArea *area = HILDON_PANNABLE_AREA (widget);
2628 HildonPannableAreaPrivate *priv = area->priv;
2630 if ((priv->child)&&(priv->last_in)) {
2631 priv->last_in = FALSE;
2633 synth_crossing (priv->child, 0, 0, event->x_root,
2634 event->y_root, event->time, FALSE);
2641 hildon_pannable_area_button_release_cb (GtkWidget * widget,
2642 GdkEventButton * event)
2644 HildonPannableArea *area = HILDON_PANNABLE_AREA (widget);
2645 HildonPannableAreaPrivate *priv = area->priv;
2650 if (((event->time == priv->last_time) && (priv->last_type == 3))
2651 || (gtk_bin_get_child (GTK_BIN (widget)) == NULL)
2652 || (!priv->button_pressed) || (!priv->enabled) || (event->button != 1))
2655 /* if last event was a motion-notify we have to check the movement
2656 and launch the animation */
2657 if (priv->last_type == 2) {
2659 dx = event->x - priv->x;
2660 dy = event->y - priv->y;
2662 hildon_pannable_area_check_move (area, (GdkEventMotion *) event, &dx, &dy);
2665 gdouble delta = event->time - priv->last_time;
2667 hildon_pannable_area_handle_move (area, (GdkEventMotion *) event, &dx, &dy);
2669 /* move all the way to the last position now */
2670 if (priv->motion_event_scroll_timeout) {
2671 g_source_remove (priv->motion_event_scroll_timeout);
2672 hildon_pannable_area_motion_event_scroll_timeout (HILDON_PANNABLE_AREA (widget));
2677 if ((ABS (dx) < 4.0) && (delta >= CURSOR_STOPPED_TIMEOUT))
2680 if ((ABS (dy) < 4.0) && (delta >= CURSOR_STOPPED_TIMEOUT))
2685 /* If overshoot has been initiated with a finger down, on release set max speed */
2686 if (priv->overshot_dist_y != 0) {
2687 priv->overshooting_y = priv->bounce_steps; /* Hack to stop a bounce in the finger down case */
2688 priv->vel_y = priv->vmax_overshooting;
2691 if (priv->overshot_dist_x != 0) {
2692 priv->overshooting_x = priv->bounce_steps; /* Hack to stop a bounce in the finger down case */
2693 priv->vel_x = priv->vmax_overshooting;
2696 priv->button_pressed = FALSE;
2698 if ((ABS (priv->vel_y) >= priv->vmin) ||
2699 (ABS (priv->vel_x) >= priv->vmin)) {
2701 /* we have to move because we are in overshooting position*/
2703 gboolean result_val;
2705 g_signal_emit (area,
2706 pannable_area_signals[PANNING_STARTED],
2710 priv->scroll_indicator_alpha = 1.0;
2712 if (ABS (priv->vel_x) > MAX_SPEED_THRESHOLD)
2713 priv->vel_x = (priv->vel_x > 0) ? priv->vmax : -priv->vmax;
2715 if (ABS (priv->vel_y) > MAX_SPEED_THRESHOLD)
2716 priv->vel_y = (priv->vel_y > 0) ? priv->vmax : -priv->vmax;
2719 priv->idle_id = gdk_threads_add_timeout ((gint) (1000.0 / (gdouble) priv->sps),
2721 hildon_pannable_area_timeout, widget);
2724 g_signal_emit (widget, pannable_area_signals[PANNING_FINISHED], 0);
2727 priv->scroll_indicator_event_interrupt = 0;
2728 priv->scroll_delay_counter = priv->scrollbar_fade_delay;
2730 hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget),
2731 priv->scroll_indicator_alpha);
2733 priv->last_time = event->time;
2734 priv->last_type = 3;
2737 priv->moved = FALSE;
2742 hildon_pannable_area_get_topmost (gtk_bin_get_child (GTK_BIN (widget))->window,
2743 event->x, event->y, &x, &y, GDK_BUTTON_RELEASE_MASK);
2745 event = (GdkEventButton *) gdk_event_copy ((GdkEvent *) event);
2749 /* Leave the widget if we've moved - This doesn't break selection,
2750 * but stops buttons from being clicked.
2752 if ((child != priv->child) || (priv->moved)) {
2753 /* Send synthetic leave event */
2754 synth_crossing (priv->child, x, y, event->x_root,
2755 event->y_root, event->time, FALSE);
2756 /* insure no click will happen for widgets that do not handle
2760 /* Send synthetic button release event */
2761 ((GdkEventAny *) event)->window = g_object_ref (priv->child);
2762 gdk_event_put ((GdkEvent *) event);
2764 /* Send synthetic button release event */
2765 ((GdkEventAny *) event)->window = g_object_ref (child);
2766 gdk_event_put ((GdkEvent *) event);
2767 /* Send synthetic leave event */
2768 synth_crossing (priv->child, x, y, event->x_root,
2769 event->y_root, event->time, FALSE);
2771 g_object_remove_weak_pointer ((GObject *) priv->child,
2772 (gpointer) & priv->child);
2774 priv->moved = FALSE;
2775 gdk_event_free ((GdkEvent *) event);
2780 /* utility event handler */
2782 hildon_pannable_area_scroll_cb (GtkWidget *widget,
2783 GdkEventScroll *event)
2785 GtkAdjustment *adj = NULL;
2786 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
2788 if ((!priv->enabled) ||
2789 (gtk_bin_get_child (GTK_BIN (widget)) == NULL))
2792 priv->scroll_indicator_event_interrupt = 0;
2793 priv->scroll_delay_counter = priv->scrollbar_fade_delay + 20;
2795 hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget), 1.0);
2797 /* Stop inertial scrolling */
2798 if (priv->idle_id) {
2801 priv->overshooting_x = 0;
2802 priv->overshooting_y = 0;
2804 if ((priv->overshot_dist_x>0)||(priv->overshot_dist_y>0)) {
2805 priv->overshot_dist_x = 0;
2806 priv->overshot_dist_y = 0;
2808 gtk_widget_queue_resize (GTK_WIDGET (widget));
2811 g_signal_emit (widget, pannable_area_signals[PANNING_FINISHED], 0);
2813 g_source_remove (priv->idle_id);
2817 if (event->direction == GDK_SCROLL_UP || event->direction == GDK_SCROLL_DOWN)
2818 adj = priv->vadjust;
2820 adj = priv->hadjust;
2824 gdouble delta, new_value;
2826 /* from gtkrange.c calculate delta*/
2827 delta = pow (adj->page_size, 2.0 / 3.0);
2829 if (event->direction == GDK_SCROLL_UP ||
2830 event->direction == GDK_SCROLL_LEFT)
2833 new_value = CLAMP (adj->value + delta, adj->lower, adj->upper - adj->page_size);
2835 gtk_adjustment_set_value (adj, new_value);
2842 hildon_pannable_area_child_mapped (GtkWidget *widget,
2846 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (user_data)->priv;
2848 if (priv->event_window != NULL && priv->enabled)
2849 gdk_window_raise (priv->event_window);
2853 hildon_pannable_area_add (GtkContainer *container, GtkWidget *child)
2855 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (container)->priv;
2857 g_return_if_fail (gtk_bin_get_child (GTK_BIN (container)) == NULL);
2859 gtk_widget_set_parent (child, GTK_WIDGET (container));
2860 GTK_BIN (container)->child = child;
2862 g_signal_connect_after (child, "map-event",
2863 G_CALLBACK (hildon_pannable_area_child_mapped),
2866 if (!gtk_widget_set_scroll_adjustments (child, priv->hadjust, priv->vadjust)) {
2867 g_warning ("%s: cannot add non scrollable widget, "
2868 "wrap it in a viewport", __FUNCTION__);
2873 hildon_pannable_area_remove (GtkContainer *container, GtkWidget *child)
2875 g_return_if_fail (HILDON_IS_PANNABLE_AREA (container));
2876 g_return_if_fail (child != NULL);
2877 g_return_if_fail (gtk_bin_get_child (GTK_BIN (container)) == child);
2879 gtk_widget_set_scroll_adjustments (child, NULL, NULL);
2881 g_signal_handlers_disconnect_by_func (child,
2882 hildon_pannable_area_child_mapped,
2885 /* chain parent class handler to remove child */
2886 GTK_CONTAINER_CLASS (hildon_pannable_area_parent_class)->remove (container, child);
2890 * This method calculates a factor necessary to determine the initial distance
2891 * to jump in hildon_pannable_area_scroll_to(). For fixed time and frames per
2892 * second, we know in how many frames 'n' we need to reach the destination
2893 * point. We know that, for a distance d,
2895 * d = d_0 + d_1 + ... + d_n
2897 * where d_i is the distance travelled in the i-th frame and decel_factor is
2898 * the deceleration factor. This can be rewritten as
2900 * d = d_0 + (d_0 * decel_factor) + ... + (d_n-1 * decel_factor),
2902 * since the distance travelled on each frame is the distance travelled in the
2903 * previous frame reduced by the deceleration factor. Reducing this and
2904 * factoring d_0 out, we get
2906 * d = d_0 (1 + decel_factor + ... + decel_factor^(n-1)).
2908 * Since the sum is independent of the distance to be travelled, we can define
2911 * vel_factor = 1 + decel_factor + ... + decel_factor^(n-1).
2913 * That's the gem we calculate in this method.
2916 hildon_pannable_calculate_vel_factor (HildonPannableArea * self)
2918 HildonPannableAreaPrivate *priv = self->priv;
2923 n = ceil (priv->sps * priv->scroll_time);
2925 for (i = 1; i < n && fct_i >= RATIO_TOLERANCE; i++) {
2926 fct_i *= priv->decel;
2930 priv->vel_factor = fct;
2934 * hildon_pannable_area_new:
2936 * Create a new pannable area widget
2938 * Returns: the newly created #HildonPannableArea
2944 hildon_pannable_area_new (void)
2946 return g_object_new (HILDON_TYPE_PANNABLE_AREA, NULL);
2950 * hildon_pannable_area_new_full:
2951 * @mode: #HildonPannableAreaMode
2952 * @enabled: Value for the enabled property
2953 * @vel_min: Value for the velocity-min property
2954 * @vel_max: Value for the velocity-max property
2955 * @decel: Value for the deceleration property
2956 * @sps: Value for the sps property
2958 * Create a new #HildonPannableArea widget and set various properties
2960 * returns: the newly create #HildonPannableArea
2966 hildon_pannable_area_new_full (gint mode, gboolean enabled,
2967 gdouble vel_min, gdouble vel_max,
2968 gdouble decel, guint sps)
2970 return g_object_new (HILDON_TYPE_PANNABLE_AREA,
2973 "velocity_min", vel_min,
2974 "velocity_max", vel_max,
2975 "deceleration", decel, "sps", sps, NULL);
2979 * hildon_pannable_area_add_with_viewport:
2980 * @area: A #HildonPannableArea
2981 * @child: Child widget to add to the viewport
2983 * Convenience function used to add a child to a #GtkViewport, and add the
2984 * viewport to the scrolled window.
2990 hildon_pannable_area_add_with_viewport (HildonPannableArea * area,
2994 GtkWidget *viewport;
2996 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
2997 g_return_if_fail (GTK_IS_WIDGET (child));
2998 g_return_if_fail (child->parent == NULL);
3000 bin = GTK_BIN (area);
3002 if (bin->child != NULL)
3004 g_return_if_fail (GTK_IS_VIEWPORT (bin->child));
3005 g_return_if_fail (GTK_BIN (bin->child)->child == NULL);
3007 viewport = bin->child;
3011 HildonPannableAreaPrivate *priv = area->priv;
3013 viewport = gtk_viewport_new (priv->hadjust,
3015 gtk_viewport_set_shadow_type (GTK_VIEWPORT (viewport), GTK_SHADOW_NONE);
3016 gtk_container_add (GTK_CONTAINER (area), viewport);
3019 gtk_widget_show (viewport);
3020 gtk_container_add (GTK_CONTAINER (viewport), child);
3024 * hildon_pannable_area_scroll_to:
3025 * @area: A #HildonPannableArea.
3026 * @x: The x coordinate of the destination point or -1 to ignore this axis.
3027 * @y: The y coordinate of the destination point or -1 to ignore this axis.
3029 * Smoothly scrolls @area to ensure that (@x, @y) is a visible point
3030 * on the widget. To move in only one coordinate, you must set the other one
3031 * to -1. Notice that, in %HILDON_PANNABLE_AREA_MODE_PUSH mode, this function
3032 * works just like hildon_pannable_area_jump_to().
3034 * This function is useful if you need to present the user with a particular
3035 * element inside a scrollable widget, like #GtkTreeView. For instance,
3036 * the following example shows how to scroll inside a #GtkTreeView to
3037 * make visible an item, indicated by the #GtkTreeIter @iter.
3041 * GtkTreePath *path;
3042 * GdkRectangle *rect;
3044 * path = gtk_tree_model_get_path (model, &iter);
3045 * gtk_tree_view_get_background_area (GTK_TREE_VIEW (treeview),
3046 * path, NULL, &rect);
3047 * gtk_tree_view_convert_bin_window_to_tree_coords (GTK_TREE_VIEW (treeview),
3048 * 0, rect.y, NULL, &y);
3049 * hildon_pannable_area_scroll_to (panarea, -1, y);
3050 * gtk_tree_path_free (path);
3054 * If you want to present a child widget in simpler scenarios,
3055 * use hildon_pannable_area_scroll_to_child() instead.
3057 * There is a precondition to this function: the widget must be
3058 * already realized. Check the hildon_pannable_area_jump_to_child() for
3059 * more tips regarding how to call this function during
3065 hildon_pannable_area_scroll_to (HildonPannableArea *area,
3066 const gint x, const gint y)
3068 HildonPannableAreaPrivate *priv;
3070 gint dist_x, dist_y;
3071 gboolean hscroll_visible, vscroll_visible;
3073 g_return_if_fail (GTK_WIDGET_REALIZED (area));
3074 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3078 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
3079 priv->vadjust->page_size);
3080 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
3081 priv->hadjust->page_size);
3083 if (((!vscroll_visible)&&(!hscroll_visible)) ||
3084 (x == -1 && y == -1)) {
3088 if (priv->mode == HILDON_PANNABLE_AREA_MODE_PUSH)
3089 hildon_pannable_area_jump_to (area, x, y);
3091 width = priv->hadjust->upper - priv->hadjust->lower;
3092 height = priv->vadjust->upper - priv->vadjust->lower;
3094 g_return_if_fail (x < width || y < height);
3096 if ((x > -1)&&(hscroll_visible)) {
3097 priv->scroll_to_x = x - priv->hadjust->page_size/2;
3098 dist_x = priv->scroll_to_x - priv->hadjust->value;
3100 priv->scroll_to_x = -1;
3102 priv->vel_x = - dist_x/priv->vel_factor;
3105 priv->scroll_to_x = -1;
3108 if ((y > -1)&&(vscroll_visible)) {
3109 priv->scroll_to_y = y - priv->vadjust->page_size/2;
3110 dist_y = priv->scroll_to_y - priv->vadjust->value;
3112 priv->scroll_to_y = -1;
3114 priv->vel_y = - dist_y/priv->vel_factor;
3117 priv->scroll_to_y = y;
3120 if ((priv->scroll_to_y == -1) && (priv->scroll_to_y == -1)) {
3124 hildon_pannable_area_launch_fade_timeout (area, 1.0);
3127 priv->idle_id = gdk_threads_add_timeout ((gint) (1000.0 / (gdouble) priv->sps),
3129 hildon_pannable_area_timeout, area);
3133 * hildon_pannable_area_jump_to:
3134 * @area: A #HildonPannableArea.
3135 * @x: The x coordinate of the destination point or -1 to ignore this axis.
3136 * @y: The y coordinate of the destination point or -1 to ignore this axis.
3138 * Jumps the position of @area to ensure that (@x, @y) is a visible
3139 * point in the widget. In order to move in only one coordinate, you
3140 * must set the other one to -1. See hildon_pannable_area_scroll_to()
3141 * function for an example of how to calculate the position of
3142 * children in scrollable widgets like #GtkTreeview.
3144 * There is a precondition to this function: the widget must be
3145 * already realized. Check the hildon_pannable_area_jump_to_child() for
3146 * more tips regarding how to call this function during
3152 hildon_pannable_area_jump_to (HildonPannableArea *area,
3153 const gint x, const gint y)
3155 HildonPannableAreaPrivate *priv;
3159 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3160 g_return_if_fail (GTK_WIDGET_REALIZED (area));
3161 g_return_if_fail (x >= -1 && y >= -1);
3163 if (x == -1 && y == -1) {
3169 width = priv->hadjust->upper - priv->hadjust->lower;
3170 height = priv->vadjust->upper - priv->vadjust->lower;
3172 g_return_if_fail (x < width || y < height);
3174 hv = priv->hadjust->value;
3175 vv = priv->vadjust->value;
3178 gdouble jump_to = x - priv->hadjust->page_size/2;
3180 priv->hadjust->value = CLAMP (jump_to,
3181 priv->hadjust->lower,
3182 priv->hadjust->upper -
3183 priv->hadjust->page_size);
3187 gdouble jump_to = y - priv->vadjust->page_size/2;
3189 priv->vadjust->value = CLAMP (jump_to,
3190 priv->vadjust->lower,
3191 priv->vadjust->upper -
3192 priv->vadjust->page_size);
3195 if (hv != priv->hadjust->value)
3196 gtk_adjustment_value_changed (priv->hadjust);
3198 if (vv != priv->vadjust->value)
3199 gtk_adjustment_value_changed (priv->vadjust);
3201 priv->scroll_indicator_alpha = 1.0;
3203 if (priv->scroll_indicator_timeout) {
3204 g_source_remove (priv->scroll_indicator_timeout);
3205 priv->scroll_indicator_timeout = 0;
3208 if (priv->idle_id) {
3211 priv->overshooting_x = 0;
3212 priv->overshooting_y = 0;
3214 if ((priv->overshot_dist_x>0)||(priv->overshot_dist_y>0)) {
3215 priv->overshot_dist_x = 0;
3216 priv->overshot_dist_y = 0;
3218 gtk_widget_queue_resize (GTK_WIDGET (area));
3221 g_signal_emit (area, pannable_area_signals[PANNING_FINISHED], 0);
3222 g_source_remove (priv->idle_id);
3228 * hildon_pannable_area_scroll_to_child:
3229 * @area: A #HildonPannableArea.
3230 * @child: A #GtkWidget, descendant of @area.
3232 * Smoothly scrolls until @child is visible inside @area. @child must
3233 * be a descendant of @area. If you need to scroll inside a scrollable
3234 * widget, e.g., #GtkTreeview, see hildon_pannable_area_scroll_to().
3236 * There is a precondition to this function: the widget must be
3237 * already realized. Check the hildon_pannable_area_jump_to_child() for
3238 * more tips regarding how to call this function during
3244 hildon_pannable_area_scroll_to_child (HildonPannableArea *area, GtkWidget *child)
3246 GtkWidget *bin_child;
3249 g_return_if_fail (GTK_WIDGET_REALIZED (area));
3250 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3251 g_return_if_fail (GTK_IS_WIDGET (child));
3252 g_return_if_fail (gtk_widget_is_ancestor (child, GTK_WIDGET (area)));
3254 if (GTK_BIN (area)->child == NULL)
3257 /* We need to get to check the child of the inside the area */
3258 bin_child = GTK_BIN (area)->child;
3260 /* we check if we added a viewport */
3261 if (GTK_IS_VIEWPORT (bin_child)) {
3262 bin_child = GTK_BIN (bin_child)->child;
3265 if (gtk_widget_translate_coordinates (child, bin_child, 0, 0, &x, &y))
3266 hildon_pannable_area_scroll_to (area, x, y);
3270 * hildon_pannable_area_jump_to_child:
3271 * @area: A #HildonPannableArea.
3272 * @child: A #GtkWidget, descendant of @area.
3274 * Jumps to make sure @child is visible inside @area. @child must
3275 * be a descendant of @area. If you want to move inside a scrollable
3276 * widget, like, #GtkTreeview, see hildon_pannable_area_scroll_to().
3278 * There is a precondition to this function: the widget must be
3279 * already realized. You can control if the widget is ready with the
3280 * GTK_WIDGET_REALIZED macro. If you want to call this function during
3281 * the initialization process of the widget do it inside a callback to
3282 * the ::realize signal, using g_signal_connect_after() function.
3287 hildon_pannable_area_jump_to_child (HildonPannableArea *area, GtkWidget *child)
3289 GtkWidget *bin_child;
3292 g_return_if_fail (GTK_WIDGET_REALIZED (area));
3293 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3294 g_return_if_fail (GTK_IS_WIDGET (child));
3295 g_return_if_fail (gtk_widget_is_ancestor (child, GTK_WIDGET (area)));
3297 if (gtk_bin_get_child (GTK_BIN (area)) == NULL)
3300 /* We need to get to check the child of the inside the area */
3301 bin_child = gtk_bin_get_child (GTK_BIN (area));
3303 /* we check if we added a viewport */
3304 if (GTK_IS_VIEWPORT (bin_child)) {
3305 bin_child = gtk_bin_get_child (GTK_BIN (bin_child));
3308 if (gtk_widget_translate_coordinates (child, bin_child, 0, 0, &x, &y))
3309 hildon_pannable_area_jump_to (area, x, y);
3313 * hildon_pannable_get_child_widget_at:
3314 * @area: A #HildonPannableArea.
3315 * @x: horizontal coordinate of the point
3316 * @y: vertical coordinate of the point
3318 * Get the widget at the point (x, y) inside the pannable area. In
3319 * case no widget found it returns NULL.
3321 * returns: the #GtkWidget if we find a widget, NULL in any other case
3326 hildon_pannable_get_child_widget_at (HildonPannableArea *area,
3327 gdouble x, gdouble y)
3329 GdkWindow *window = NULL;
3330 GtkWidget *child_widget = NULL;
3332 window = hildon_pannable_area_get_topmost
3333 (gtk_bin_get_child (GTK_BIN (area))->window,
3334 x, y, NULL, NULL, GDK_ALL_EVENTS_MASK);
3336 gdk_window_get_user_data (window, (gpointer) &child_widget);
3338 return child_widget;
3343 * hildon_pannable_area_get_hadjustment:
3344 * @area: A #HildonPannableArea.
3346 * Returns the horizontal adjustment. This adjustment is the internal
3347 * widget adjustment used to control the animations. Do not modify it
3348 * directly to change the position of the pannable, to do that use the
3349 * pannable API. If you modify the object directly it could cause
3350 * artifacts in the animations.
3352 * returns: The horizontal #GtkAdjustment
3357 hildon_pannable_area_get_hadjustment (HildonPannableArea *area)
3360 g_return_val_if_fail (HILDON_IS_PANNABLE_AREA (area), NULL);
3362 return area->priv->hadjust;
3366 * hildon_pannable_area_get_vadjustment:
3367 * @area: A #HildonPannableArea.
3369 * Returns the vertical adjustment. This adjustment is the internal
3370 * widget adjustment used to control the animations. Do not modify it
3371 * directly to change the position of the pannable, to do that use the
3372 * pannable API. If you modify the object directly it could cause
3373 * artifacts in the animations.
3375 * returns: The vertical #GtkAdjustment
3380 hildon_pannable_area_get_vadjustment (HildonPannableArea *area)
3382 g_return_val_if_fail (HILDON_IS_PANNABLE_AREA (area), NULL);
3384 return area->priv->vadjust;
3389 * hildon_pannable_area_get_size_request_policy:
3390 * @area: A #HildonPannableArea.
3392 * This function returns the current size request policy of the
3393 * widget. That policy controls the way the size_request is done in
3394 * the pannable area. Check
3395 * hildon_pannable_area_set_size_request_policy() for a more detailed
3398 * returns: the policy is currently being used in the widget
3399 * #HildonSizeRequestPolicy.
3403 HildonSizeRequestPolicy
3404 hildon_pannable_area_get_size_request_policy (HildonPannableArea *area)
3406 HildonPannableAreaPrivate *priv;
3408 g_return_val_if_fail (HILDON_IS_PANNABLE_AREA (area), FALSE);
3412 return priv->size_request_policy;
3416 * hildon_pannable_area_set_size_request_policy:
3417 * @area: A #HildonPannableArea.
3418 * @size_request_policy: One of the allowed #HildonSizeRequestPolicy
3420 * This function sets the pannable area size request policy. That
3421 * policy controls the way the size_request is done in the pannable
3422 * area. Pannable can use the size request of its children
3423 * (#HILDON_SIZE_REQUEST_CHILDREN) or the minimum size required for
3424 * the area itself (#HILDON_SIZE_REQUEST_MINIMUM), the latter is the
3425 * default. Recall this size depends on the scrolling policy you are
3426 * requesting to the pannable area, if you set #GTK_POLICY_NEVER this
3427 * parameter will not have any effect with
3428 * #HILDON_SIZE_REQUEST_MINIMUM set.
3432 * Deprecated: This method and the policy request is deprecated, DO
3433 * NOT use it in future code, the only policy properly supported in
3434 * gtk+ nowadays is the minimum size. Use #gtk_window_set_default_size
3435 * or #gtk_window_set_geometry_hints with the proper size in your case
3436 * to define the height of your dialogs.
3439 hildon_pannable_area_set_size_request_policy (HildonPannableArea *area,
3440 HildonSizeRequestPolicy size_request_policy)
3442 HildonPannableAreaPrivate *priv;
3444 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3448 if (priv->size_request_policy == size_request_policy)
3451 priv->size_request_policy = size_request_policy;
3453 gtk_widget_queue_resize (GTK_WIDGET (area));
3455 g_object_notify (G_OBJECT (area), "size-request-policy");