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 = priv->scrollbars_gc;
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 gtk_style_lookup_color (widget->style, "SecondaryTextColor", &transp_color);
1490 } else if (priv->scroll_indicator_alpha < 1.0) {
1491 tranparency_color (&transp_color, *back_color, *scroll_color,
1492 priv->scroll_indicator_alpha);
1494 gdk_gc_set_rgb_fg_color (gc, &transp_color);
1496 gdk_draw_rectangle (widget->window, gc,
1497 TRUE, priv->vscroll_rect.x, y,
1498 priv->vscroll_rect.width, height);
1502 hildon_pannable_draw_hscroll (GtkWidget *widget,
1503 GdkColor *back_color,
1504 GdkColor *scroll_color)
1506 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1508 GdkColor transp_color;
1509 GdkGC *gc = priv->scrollbars_gc;
1511 gdk_draw_rectangle (widget->window,
1512 widget->style->bg_gc[GTK_STATE_INSENSITIVE],
1514 priv->hscroll_rect.x, priv->hscroll_rect.y,
1515 priv->hscroll_rect.width,
1516 priv->hscroll_rect.height);
1518 /* calculate the scrollbar width and position */
1519 x = ((priv->hadjust->value - priv->hadjust->lower) / (priv->hadjust->upper - priv->hadjust->lower)) *
1520 (widget->allocation.width - (priv->vscroll_visible ? priv->indicator_width : 0));
1521 width =((((priv->hadjust->value - priv->hadjust->lower) +
1522 priv->hadjust->page_size) / (priv->hadjust->upper - priv->hadjust->lower)) *
1523 (widget->allocation.width -
1524 (priv->vscroll_visible ? priv->indicator_width : 0))) - x;
1526 /* Set a minimum width */
1527 width = MAX (SCROLL_BAR_MIN_SIZE, width);
1529 /* Check the max x position */
1530 x = MIN (x, widget->allocation.width -
1531 (priv->vscroll_visible ? priv->vscroll_rect.width : 0) -
1534 if (priv->scroll_indicator_alpha == 1.0) {
1535 gtk_style_lookup_color (widget->style, "SecondaryTextColor", &transp_color);
1536 } else if (priv->scroll_indicator_alpha < 1.0) {
1537 tranparency_color (&transp_color, *back_color, *scroll_color,
1538 priv->scroll_indicator_alpha);
1540 gdk_gc_set_rgb_fg_color (gc, &transp_color);
1542 gdk_draw_rectangle (widget->window, gc,
1543 TRUE, x, priv->hscroll_rect.y, width,
1544 priv->hscroll_rect.height);
1547 #endif /* USE_CAIRO_SCROLLBARS */
1550 hildon_pannable_area_initial_effect (GtkWidget * widget)
1552 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1554 if (priv->initial_hint) {
1555 if (priv->vscroll_visible || priv->hscroll_visible) {
1557 priv->scroll_indicator_event_interrupt = 0;
1558 priv->scroll_delay_counter = priv->scrollbar_fade_delay;
1560 hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget), 1.0);
1566 hildon_pannable_area_launch_fade_timeout (HildonPannableArea * area,
1569 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (area)->priv;
1571 priv->scroll_indicator_alpha = alpha;
1573 if (!priv->scroll_indicator_timeout)
1574 priv->scroll_indicator_timeout =
1575 gdk_threads_add_timeout (SCROLL_FADE_TIMEOUT,
1576 (GSourceFunc) hildon_pannable_area_scroll_indicator_fade,
1581 hildon_pannable_area_adjust_changed (HildonPannableArea * area,
1584 if (GTK_WIDGET_REALIZED (area))
1585 hildon_pannable_area_refresh (area);
1589 hildon_pannable_area_adjust_value_changed (HildonPannableArea * area,
1592 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (area)->priv;
1594 gint x = priv->x_offset;
1595 gint y = priv->y_offset;
1597 priv->x_offset = priv->hadjust->value;
1598 xdiff = x - priv->x_offset;
1599 priv->y_offset = priv->vadjust->value;
1600 ydiff = y - priv->y_offset;
1602 if ((xdiff || ydiff) && GTK_WIDGET_DRAWABLE (area)) {
1603 hildon_pannable_area_redraw (area);
1605 if ((priv->vscroll_visible) || (priv->hscroll_visible)) {
1606 priv->scroll_indicator_event_interrupt = 0;
1607 priv->scroll_delay_counter = priv->scrollbar_fade_delay;
1609 hildon_pannable_area_launch_fade_timeout (area, 1.0);
1615 hildon_pannable_area_redraw (HildonPannableArea * area)
1617 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (area)->priv;
1619 /* Redraw scroll indicators */
1620 if (GTK_WIDGET_DRAWABLE (area)) {
1621 if (priv->hscroll_visible) {
1622 gdk_window_invalidate_rect (GTK_WIDGET (area)->window,
1623 &priv->hscroll_rect, FALSE);
1626 if (priv->vscroll_visible) {
1627 gdk_window_invalidate_rect (GTK_WIDGET (area)->window,
1628 &priv->vscroll_rect, FALSE);
1634 hildon_pannable_area_scroll_indicator_fade(HildonPannableArea * area)
1636 HildonPannableAreaPrivate *priv = area->priv;
1638 /* if moving do not fade out */
1639 if (((ABS (priv->vel_y)>priv->vmin)||
1640 (ABS (priv->vel_x)>priv->vmin))&&(!priv->button_pressed)) {
1645 if (priv->scroll_indicator_event_interrupt) {
1646 /* Stop a fade out, and fade back in */
1647 if (priv->scroll_indicator_alpha > 0.9) {
1648 priv->scroll_indicator_alpha = 1.0;
1649 priv->scroll_indicator_timeout = 0;
1653 priv->scroll_indicator_alpha += 0.2;
1654 hildon_pannable_area_redraw (area);
1660 if ((priv->scroll_indicator_alpha > 0.9) &&
1661 (priv->scroll_delay_counter > 0)) {
1662 priv->scroll_delay_counter--;
1667 if (!priv->scroll_indicator_event_interrupt) {
1668 /* Continue fade out */
1669 if (priv->scroll_indicator_alpha < 0.1) {
1670 priv->scroll_indicator_timeout = 0;
1671 priv->scroll_indicator_alpha = 0.0;
1675 priv->scroll_indicator_alpha -= 0.2;
1676 hildon_pannable_area_redraw (area);
1686 hildon_pannable_area_expose_event (GtkWidget * widget,
1687 GdkEventExpose * event)
1690 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1691 #if USE_CAIRO_SCROLLBARS == 1
1692 GdkColor back_color = widget->style->bg[GTK_STATE_NORMAL];
1693 GdkColor scroll_color = widget->style->base[GTK_STATE_SELECTED];
1694 #else /* USE_CAIRO_SCROLLBARS */
1695 GdkColor back_color = widget->style->bg[GTK_STATE_NORMAL];
1696 GdkColor scroll_color;
1697 gtk_style_lookup_color (widget->style, "SecondaryTextColor", &scroll_color);
1700 if (G_UNLIKELY (priv->initial_effect)) {
1701 hildon_pannable_area_initial_effect (widget);
1703 priv->initial_effect = FALSE;
1706 if (gtk_bin_get_child (GTK_BIN (widget))) {
1708 if (priv->scroll_indicator_alpha > 0.1) {
1709 if (priv->vscroll_visible) {
1710 hildon_pannable_draw_vscroll (widget, &back_color, &scroll_color);
1712 if (priv->hscroll_visible) {
1713 hildon_pannable_draw_hscroll (widget, &back_color, &scroll_color);
1717 /* draw overshooting rectangles */
1718 if (priv->overshot_dist_y > 0) {
1719 gint overshot_height;
1721 overshot_height = MIN (priv->overshot_dist_y, widget->allocation.height -
1722 (priv->hscroll_visible ? priv->hscroll_rect.height : 0));
1724 gdk_draw_rectangle (widget->window,
1725 widget->style->bg_gc[GTK_STATE_NORMAL],
1729 widget->allocation.width -
1730 (priv->vscroll_visible ? priv->vscroll_rect.width : 0),
1732 } else if (priv->overshot_dist_y < 0) {
1733 gint overshot_height;
1737 MAX (priv->overshot_dist_y,
1738 -(widget->allocation.height -
1739 (priv->hscroll_visible ? priv->hscroll_rect.height : 0)));
1741 overshot_y = MAX (widget->allocation.height +
1743 (priv->hscroll_visible ? priv->hscroll_rect.height : 0), 0);
1745 gdk_draw_rectangle (widget->window,
1746 widget->style->bg_gc[GTK_STATE_NORMAL],
1750 widget->allocation.width -
1751 priv->vscroll_rect.width,
1755 if (priv->overshot_dist_x > 0) {
1756 gint overshot_width;
1758 overshot_width = MIN (priv->overshot_dist_x, widget->allocation.width -
1759 (priv->vscroll_visible ? priv->vscroll_rect.width : 0));
1761 gdk_draw_rectangle (widget->window,
1762 widget->style->bg_gc[GTK_STATE_NORMAL],
1767 widget->allocation.height -
1768 (priv->hscroll_visible ? priv->hscroll_rect.height : 0));
1769 } else if (priv->overshot_dist_x < 0) {
1770 gint overshot_width;
1774 MAX (priv->overshot_dist_x,
1775 -(widget->allocation.width -
1776 (priv->vscroll_visible ? priv->vscroll_rect.width : 0)));
1778 overshot_x = MAX (widget->allocation.width +
1780 (priv->vscroll_visible ? priv->vscroll_rect.width : 0), 0);
1782 gdk_draw_rectangle (widget->window,
1783 widget->style->bg_gc[GTK_STATE_NORMAL],
1788 widget->allocation.height -
1789 priv->hscroll_rect.height);
1794 return GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->expose_event (widget, event);
1798 hildon_pannable_area_get_topmost (GdkWindow * window,
1800 gint * tx, gint * ty,
1803 /* Find the GdkWindow at the given point, by recursing from a given
1804 * parent GdkWindow. Optionally return the co-ordinates transformed
1805 * relative to the child window.
1808 GList *c, *children;
1809 GdkWindow *selected_window = NULL;
1811 gdk_drawable_get_size (GDK_DRAWABLE (window), &width, &height);
1812 if ((x < 0) || (x >= width) || (y < 0) || (y >= height))
1815 children = gdk_window_peek_children (window);
1822 selected_window = window;
1825 for (c = children; c; c = c->next) {
1826 GdkWindow *child = (GdkWindow *) c->data;
1829 gdk_drawable_get_size (GDK_DRAWABLE (child), &width, &height);
1830 gdk_window_get_position (child, &wx, &wy);
1832 if ((x >= wx) && (x < (wx + width)) && (y >= wy) && (y < (wy + height)) &&
1833 (gdk_window_is_visible (child))) {
1835 if (gdk_window_peek_children (child)) {
1836 selected_window = hildon_pannable_area_get_topmost (child, x-wx, y-wy,
1838 if (!selected_window) {
1843 selected_window = child;
1846 if ((gdk_window_get_events (child)&mask)) {
1851 selected_window = child;
1857 return selected_window;
1861 synth_crossing (GdkWindow * child,
1863 gint x_root, gint y_root,
1864 guint32 time, gboolean in)
1866 GdkEventCrossing *crossing_event;
1867 GdkEventType type = in ? GDK_ENTER_NOTIFY : GDK_LEAVE_NOTIFY;
1869 /* Send synthetic enter event */
1870 crossing_event = (GdkEventCrossing *) gdk_event_new (type);
1871 ((GdkEventAny *) crossing_event)->type = type;
1872 ((GdkEventAny *) crossing_event)->window = g_object_ref (child);
1873 ((GdkEventAny *) crossing_event)->send_event = FALSE;
1874 crossing_event->subwindow = g_object_ref (child);
1875 crossing_event->time = time;
1876 crossing_event->x = x;
1877 crossing_event->y = y;
1878 crossing_event->x_root = x_root;
1879 crossing_event->y_root = y_root;
1880 crossing_event->mode = GDK_CROSSING_NORMAL;
1881 crossing_event->detail = GDK_NOTIFY_UNKNOWN;
1882 crossing_event->focus = FALSE;
1883 crossing_event->state = 0;
1884 gdk_event_put ((GdkEvent *) crossing_event);
1885 gdk_event_free ((GdkEvent *) crossing_event);
1889 hildon_pannable_area_button_press_cb (GtkWidget * widget,
1890 GdkEventButton * event)
1893 HildonPannableArea *area = HILDON_PANNABLE_AREA (widget);
1894 HildonPannableAreaPrivate *priv = area->priv;
1896 if ((!priv->enabled) || (event->button != 1) ||
1897 ((event->time == priv->last_time) &&
1898 (priv->last_type == 1)) || (gtk_bin_get_child (GTK_BIN (widget)) == NULL))
1901 priv->scroll_indicator_event_interrupt = 1;
1903 hildon_pannable_area_launch_fade_timeout (area,
1904 priv->scroll_indicator_alpha);
1906 priv->last_time = event->time;
1907 priv->last_type = 1;
1909 priv->scroll_to_x = -1;
1910 priv->scroll_to_y = -1;
1912 if (priv->button_pressed && priv->child) {
1913 /* Widget stole focus on last click, send crossing-out event */
1914 synth_crossing (priv->child, 0, 0, event->x_root, event->y_root,
1915 event->time, FALSE);
1923 /* Don't allow a click if we're still moving fast */
1924 if ((ABS (priv->vel_x) <= (priv->vmax * priv->vfast_factor)) &&
1925 (ABS (priv->vel_y) <= (priv->vmax * priv->vfast_factor)))
1927 hildon_pannable_area_get_topmost (gtk_bin_get_child (GTK_BIN (widget))->window,
1928 event->x, event->y, &x, &y, GDK_BUTTON_PRESS_MASK);
1932 priv->button_pressed = TRUE;
1934 /* Stop scrolling on mouse-down (so you can flick, then hold to stop) */
1937 if (priv->idle_id) {
1938 g_source_remove (priv->idle_id);
1940 g_signal_emit (area, pannable_area_signals[PANNING_FINISHED], 0);
1945 gdk_drawable_get_size (priv->child, &priv->child_width,
1946 &priv->child_height);
1947 priv->last_in = TRUE;
1949 g_object_add_weak_pointer ((GObject *) priv->child,
1950 (gpointer) & priv->child);
1952 event = (GdkEventButton *) gdk_event_copy ((GdkEvent *) event);
1958 synth_crossing (priv->child, x, y, event->x_root,
1959 event->y_root, event->time, TRUE);
1961 /* Send synthetic click (button press/release) event */
1962 ((GdkEventAny *) event)->window = g_object_ref (priv->child);
1964 gdk_event_put ((GdkEvent *) event);
1965 gdk_event_free ((GdkEvent *) event);
1973 hildon_pannable_area_check_scrollbars (HildonPannableArea * area)
1975 HildonPannableAreaPrivate *priv = area->priv;
1976 gboolean prev_hscroll_visible, prev_vscroll_visible;
1978 prev_hscroll_visible = priv->hscroll_visible;
1979 prev_vscroll_visible = priv->vscroll_visible;
1981 if (!gtk_bin_get_child (GTK_BIN (area))) {
1982 priv->vscroll_visible = FALSE;
1983 priv->hscroll_visible = FALSE;
1985 switch (priv->hscrollbar_policy) {
1986 case GTK_POLICY_ALWAYS:
1987 priv->hscroll_visible = TRUE;
1989 case GTK_POLICY_NEVER:
1990 priv->hscroll_visible = FALSE;
1993 priv->hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
1994 priv->hadjust->page_size);
1997 switch (priv->vscrollbar_policy) {
1998 case GTK_POLICY_ALWAYS:
1999 priv->vscroll_visible = TRUE;
2001 case GTK_POLICY_NEVER:
2002 priv->vscroll_visible = FALSE;
2005 priv->vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
2006 priv->vadjust->page_size);
2009 /* Store the vscroll/hscroll areas for redrawing */
2010 if (priv->vscroll_visible) {
2011 GtkAllocation *allocation = >K_WIDGET (area)->allocation;
2012 priv->vscroll_rect.x = allocation->width - priv->indicator_width;
2013 priv->vscroll_rect.y = 0;
2014 priv->vscroll_rect.width = priv->indicator_width;
2015 priv->vscroll_rect.height = allocation->height -
2016 (priv->hscroll_visible ? priv->indicator_width : 0);
2018 if (priv->hscroll_visible) {
2019 GtkAllocation *allocation = >K_WIDGET (area)->allocation;
2020 priv->hscroll_rect.y = allocation->height - priv->indicator_width;
2021 priv->hscroll_rect.x = 0;
2022 priv->hscroll_rect.height = priv->indicator_width;
2023 priv->hscroll_rect.width = allocation->width -
2024 (priv->vscroll_visible ? priv->indicator_width : 0);
2028 return ((priv->hscroll_visible != prev_hscroll_visible) ||
2029 (priv->vscroll_visible != prev_vscroll_visible));
2033 hildon_pannable_area_refresh (HildonPannableArea * area)
2035 if (GTK_WIDGET_DRAWABLE (area) &&
2036 hildon_pannable_area_check_scrollbars (area)) {
2037 HildonPannableAreaPrivate *priv = area->priv;
2039 gtk_widget_queue_resize (GTK_WIDGET (area));
2041 if ((priv->vscroll_visible) || (priv->hscroll_visible)) {
2042 priv->scroll_indicator_event_interrupt = 0;
2043 priv->scroll_delay_counter = area->priv->scrollbar_fade_delay;
2045 hildon_pannable_area_launch_fade_timeout (area, 1.0);
2048 hildon_pannable_area_redraw (area);
2052 /* Scroll by a particular amount (in pixels). Optionally, return if
2053 * the scroll on a particular axis was successful.
2056 hildon_pannable_axis_scroll (HildonPannableArea *area,
2057 GtkAdjustment *adjust,
2061 gint *overshot_dist,
2067 HildonPannableAreaPrivate *priv = area->priv;
2069 dist = gtk_adjustment_get_value (adjust) - inc;
2072 * We use overshot_dist to define the distance of the current overshoot,
2073 * and overshooting to define the direction/whether or not we are overshot
2075 if (!(*overshooting)) {
2077 /* Initiation of the overshoot happens when the finger is released
2078 * and the current position of the pannable contents are out of range
2080 if (dist < adjust->lower) {
2083 dist = adjust->lower;
2085 if (overshoot_max!=0) {
2088 *overshot_dist = CLAMP (*overshot_dist + *vel, 0, overshoot_max);
2089 *vel = MIN (priv->vmax_overshooting, *vel);
2090 gtk_widget_queue_resize (GTK_WIDGET (area));
2094 } else if (dist > adjust->upper - adjust->page_size) {
2097 dist = adjust->upper - adjust->page_size;
2099 if (overshoot_max!=0) {
2102 *overshot_dist = CLAMP (*overshot_dist + *vel, -overshoot_max, 0);
2103 *vel = MAX (-priv->vmax_overshooting, *vel);
2104 gtk_widget_queue_resize (GTK_WIDGET (area));
2109 if ((*scroll_to) != -1) {
2110 if (((inc < 0)&&(*scroll_to <= dist))||
2111 ((inc > 0)&&(*scroll_to >= dist))) {
2119 adjust->value = dist;
2121 if (!priv->button_pressed) {
2123 /* When the overshoot has started we continue for
2124 * PROP_BOUNCE_STEPS more steps into the overshoot before we
2125 * reverse direction. The deceleration factor is calculated
2126 * based on the percentage distance from the first item with
2127 * each iteration, therefore always returning us to the
2128 * top/bottom most element
2130 if (*overshot_dist > 0) {
2132 if ((*overshooting < priv->bounce_steps) && (*vel > 0)) {
2134 *vel = (((gdouble)*overshot_dist)/overshoot_max) * (*vel);
2135 } else if ((*overshooting >= priv->bounce_steps) && (*vel > 0)) {
2137 } else if ((*overshooting > 1) && (*vel < 0)) {
2138 /* we add the MIN in order to avoid very small speeds */
2139 *vel = MIN ((((gdouble)*overshot_dist)*0.4) * -1, -2.0);
2142 *overshot_dist = CLAMP (*overshot_dist + *vel, 0, overshoot_max);
2144 gtk_widget_queue_resize (GTK_WIDGET (area));
2146 } else if (*overshot_dist < 0) {
2148 if ((*overshooting < priv->bounce_steps) && (*vel < 0)) {
2150 *vel = (((gdouble)*overshot_dist)/overshoot_max) * (*vel) * -1;
2151 } else if ((*overshooting >= priv->bounce_steps) && (*vel < 0)) {
2153 } else if ((*overshooting > 1) && (*vel > 0)) {
2154 /* we add the MAX in order to avoid very small speeds */
2155 *vel = MAX ((((gdouble)*overshot_dist)*0.4) * -1, 2.0);
2158 *overshot_dist = CLAMP (*overshot_dist + (*vel), -overshoot_max, 0);
2160 gtk_widget_queue_resize (GTK_WIDGET (area));
2165 gtk_widget_queue_resize (GTK_WIDGET (area));
2169 gint overshot_dist_old = *overshot_dist;
2171 if (*overshot_dist > 0) {
2172 *overshot_dist = CLAMP ((*overshot_dist) + inc, 0, overshoot_max);
2173 } else if (*overshot_dist < 0) {
2174 *overshot_dist = CLAMP ((*overshot_dist) + inc, -1 * overshoot_max, 0);
2177 adjust->value = CLAMP (dist,
2183 if (*overshot_dist != overshot_dist_old)
2184 gtk_widget_queue_resize (GTK_WIDGET (area));
2190 hildon_pannable_area_scroll (HildonPannableArea *area,
2191 gdouble x, gdouble y)
2194 HildonPannableAreaPrivate *priv = area->priv;
2195 gboolean hscroll_visible, vscroll_visible;
2198 if (gtk_bin_get_child (GTK_BIN (area)) == NULL)
2201 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
2202 priv->vadjust->page_size);
2203 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
2204 priv->hadjust->page_size);
2209 hv = priv->hadjust->value;
2210 vv = priv->vadjust->value;
2212 if (vscroll_visible) {
2213 hildon_pannable_axis_scroll (area, priv->vadjust, &priv->vel_y, y,
2214 &priv->overshooting_y, &priv->overshot_dist_y,
2215 &priv->scroll_to_y, priv->vovershoot_max, &sy);
2220 if (hscroll_visible) {
2221 hildon_pannable_axis_scroll (area, priv->hadjust, &priv->vel_x, x,
2222 &priv->overshooting_x, &priv->overshot_dist_x,
2223 &priv->scroll_to_x, priv->hovershoot_max, &sx);
2228 if (hv != priv->hadjust->value)
2229 gtk_adjustment_value_changed (priv->hadjust);
2231 if (vv != priv->vadjust->value)
2232 gtk_adjustment_value_changed (priv->vadjust);
2234 /* If the scroll on a particular axis wasn't succesful, reset the
2235 * initial scroll position to the new mouse co-ordinate. This means
2236 * when you get to the top of the page, dragging down works immediately.
2238 if (priv->mode == HILDON_PANNABLE_AREA_MODE_ACCEL) {
2250 hildon_pannable_area_timeout (HildonPannableArea * area)
2252 HildonPannableAreaPrivate *priv = area->priv;
2254 if ((!priv->enabled) || (priv->mode == HILDON_PANNABLE_AREA_MODE_PUSH)) {
2256 g_signal_emit (area, pannable_area_signals[PANNING_FINISHED], 0);
2261 if (!priv->button_pressed) {
2262 /* Decelerate gradually when pointer is raised */
2263 if ((!priv->overshot_dist_y) &&
2264 (!priv->overshot_dist_x)) {
2266 /* in case we move to a specific point do not decelerate when arriving */
2267 if ((priv->scroll_to_x != -1)||(priv->scroll_to_y != -1)) {
2269 if (ABS (priv->vel_x) >= 1.5) {
2270 priv->vel_x *= priv->decel;
2273 if (ABS (priv->vel_y) >= 1.5) {
2274 priv->vel_y *= priv->decel;
2278 if ((!priv->low_friction_mode) ||
2279 ((priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ) &&
2280 (ABS (priv->vel_x) < 0.8*priv->vmax)))
2281 priv->vel_x *= priv->decel;
2283 if ((!priv->low_friction_mode) ||
2284 ((priv->mov_mode&HILDON_MOVEMENT_MODE_VERT) &&
2285 (ABS (priv->vel_y) < 0.8*priv->vmax)))
2286 priv->vel_y *= priv->decel;
2288 if ((ABS (priv->vel_x) < 1.0) && (ABS (priv->vel_y) < 1.0)) {
2293 g_signal_emit (area, pannable_area_signals[PANNING_FINISHED], 0);
2299 } else if (priv->mode == HILDON_PANNABLE_AREA_MODE_AUTO) {
2305 hildon_pannable_area_scroll (area, priv->vel_x, priv->vel_y);
2311 hildon_pannable_area_calculate_velocity (gdouble *vel,
2315 gdouble drag_inertia,
2321 if (ABS (dist) >= RATIO_TOLERANCE) {
2322 rawvel = (dist / ABS (delta)) * force;
2323 *vel = *vel * (1 - drag_inertia) +
2324 rawvel * drag_inertia;
2325 *vel = *vel > 0 ? MIN (*vel, vmax)
2326 : MAX (*vel, -1 * vmax);
2331 hildon_pannable_area_motion_event_scroll_timeout (HildonPannableArea *area)
2333 HildonPannableAreaPrivate *priv = area->priv;
2335 if ((priv->motion_x != 0)||(priv->motion_y != 0))
2336 hildon_pannable_area_scroll (area, priv->motion_x, priv->motion_y);
2338 priv->motion_event_scroll_timeout = 0;
2344 hildon_pannable_area_motion_event_scroll (HildonPannableArea *area,
2345 gdouble x, gdouble y)
2347 HildonPannableAreaPrivate *priv = area->priv;
2349 if (priv->motion_event_scroll_timeout) {
2351 priv->motion_x += x;
2352 priv->motion_y += y;
2356 /* we do not delay the first event but the next ones */
2357 hildon_pannable_area_scroll (area, x, y);
2362 priv->motion_event_scroll_timeout = gdk_threads_add_timeout
2363 ((gint) (1000.0 / (gdouble) MOTION_EVENTS_PER_SECOND),
2364 (GSourceFunc) hildon_pannable_area_motion_event_scroll_timeout, area);
2369 hildon_pannable_area_check_move (HildonPannableArea *area,
2370 GdkEventMotion * event,
2374 HildonPannableAreaPrivate *priv = area->priv;
2376 if (priv->first_drag && (!priv->moved) &&
2377 ((ABS (*x) > (priv->panning_threshold))
2378 || (ABS (*y) > (priv->panning_threshold)))) {
2383 if (priv->first_drag) {
2384 gboolean vscroll_visible;
2385 gboolean hscroll_visible;
2387 if (ABS (priv->iy - event->y) >=
2388 ABS (priv->ix - event->x)) {
2390 g_signal_emit (area,
2391 pannable_area_signals[VERTICAL_MOVEMENT],
2392 0, (priv->iy > event->y) ?
2393 HILDON_MOVEMENT_UP :
2394 HILDON_MOVEMENT_DOWN,
2395 (gdouble)priv->ix, (gdouble)priv->iy);
2397 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
2398 priv->vadjust->page_size);
2400 if (!((vscroll_visible)&&
2401 (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT))) {
2403 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
2404 priv->hadjust->page_size);
2406 /* even in case we do not have to move we check if this
2407 could be a fake horizontal movement */
2408 if (!((hscroll_visible)&&
2409 (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ)) ||
2410 (ABS (priv->iy - event->y) -
2411 ABS (priv->ix - event->x) >= priv->direction_error_margin))
2412 priv->moved = FALSE;
2416 g_signal_emit (area,
2417 pannable_area_signals[HORIZONTAL_MOVEMENT],
2418 0, (priv->ix > event->x) ?
2419 HILDON_MOVEMENT_LEFT :
2420 HILDON_MOVEMENT_RIGHT,
2421 (gdouble)priv->ix, (gdouble)priv->iy);
2423 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
2424 priv->hadjust->page_size);
2426 if (!((hscroll_visible)&&
2427 (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ))) {
2429 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
2430 priv->vadjust->page_size);
2432 /* even in case we do not have to move we check if this
2433 could be a fake vertical movement */
2434 if (!((vscroll_visible) &&
2435 (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT)) ||
2436 (ABS (priv->ix - event->x) -
2437 ABS (priv->iy - event->y) >= priv->direction_error_margin))
2438 priv->moved = FALSE;
2442 if ((priv->moved)&&(priv->child)) {
2445 pos_x = priv->cx + (event->x - priv->ix);
2446 pos_y = priv->cy + (event->y - priv->iy);
2448 synth_crossing (priv->child, pos_x, pos_y, event->x_root,
2449 event->y_root, event->time, FALSE);
2453 gboolean result_val;
2455 g_signal_emit (area,
2456 pannable_area_signals[PANNING_STARTED],
2459 priv->moved = !result_val;
2463 priv->first_drag = FALSE;
2465 if ((priv->mode != HILDON_PANNABLE_AREA_MODE_PUSH) &&
2466 (priv->mode != HILDON_PANNABLE_AREA_MODE_AUTO)) {
2469 priv->idle_id = gdk_threads_add_timeout ((gint)
2470 (1000.0 / (gdouble) priv->sps),
2472 hildon_pannable_area_timeout, area);
2478 hildon_pannable_area_handle_move (HildonPannableArea *area,
2479 GdkEventMotion * event,
2483 HildonPannableAreaPrivate *priv = area->priv;
2486 switch (priv->mode) {
2487 case HILDON_PANNABLE_AREA_MODE_PUSH:
2488 /* Scroll by the amount of pixels the cursor has moved
2489 * since the last motion event.
2491 hildon_pannable_area_motion_event_scroll (area, *x, *y);
2495 case HILDON_PANNABLE_AREA_MODE_ACCEL:
2496 /* Set acceleration relative to the initial click */
2497 priv->ex = event->x;
2498 priv->ey = event->y;
2499 priv->vel_x = ((*x > 0) ? 1 : -1) *
2501 (gdouble) GTK_WIDGET (area)->allocation.width) *
2502 (priv->vmax - priv->vmin)) + priv->vmin);
2503 priv->vel_y = ((*y > 0) ? 1 : -1) *
2505 (gdouble) GTK_WIDGET (area)->allocation.height) *
2506 (priv->vmax - priv->vmin)) + priv->vmin);
2508 case HILDON_PANNABLE_AREA_MODE_AUTO:
2510 delta = event->time - priv->last_time;
2512 if (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT) {
2513 gdouble dist = event->y - priv->y;
2515 hildon_pannable_area_calculate_velocity (&priv->vel_y,
2527 if (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ) {
2528 gdouble dist = event->x - priv->x;
2530 hildon_pannable_area_calculate_velocity (&priv->vel_x,
2542 hildon_pannable_area_motion_event_scroll (area, *x, *y);
2544 if (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ)
2546 if (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT)
2556 hildon_pannable_area_motion_notify_cb (GtkWidget * widget,
2557 GdkEventMotion * event)
2559 HildonPannableArea *area = HILDON_PANNABLE_AREA (widget);
2560 HildonPannableAreaPrivate *priv = area->priv;
2563 if (gtk_bin_get_child (GTK_BIN (widget)) == NULL)
2566 if ((!priv->enabled) || (!priv->button_pressed) ||
2567 ((event->time == priv->last_time) && (priv->last_type == 2))) {
2568 gdk_window_get_pointer (widget->window, NULL, NULL, 0);
2572 if (priv->last_type == 1) {
2573 priv->first_drag = TRUE;
2576 x = event->x - priv->x;
2577 y = event->y - priv->y;
2580 hildon_pannable_area_check_move (area, event, &x, &y);
2584 hildon_pannable_area_handle_move (area, event, &x, &y);
2585 } else if (priv->child) {
2589 pos_x = priv->cx + (event->x - priv->ix);
2590 pos_y = priv->cy + (event->y - priv->iy);
2592 in = (((0 <= pos_x)&&(priv->child_width >= pos_x)) &&
2593 ((0 <= pos_y)&&(priv->child_height >= pos_y)));
2595 if (((!priv->last_in)&&in)||((priv->last_in)&&(!in))) {
2597 synth_crossing (priv->child, pos_x, pos_y, event->x_root,
2598 event->y_root, event->time, in);
2604 priv->last_time = event->time;
2605 priv->last_type = 2;
2608 /* Send motion notify to child */
2609 event = (GdkEventMotion *) gdk_event_copy ((GdkEvent *) event);
2610 event->x = priv->cx + (event->x - priv->ix);
2611 event->y = priv->cy + (event->y - priv->iy);
2612 event->window = g_object_ref (priv->child);
2613 gdk_event_put ((GdkEvent *) event);
2614 gdk_event_free ((GdkEvent *) event);
2617 gdk_window_get_pointer (widget->window, NULL, NULL, 0);
2623 hildon_pannable_leave_notify_event (GtkWidget *widget,
2624 GdkEventCrossing *event)
2626 HildonPannableArea *area = HILDON_PANNABLE_AREA (widget);
2627 HildonPannableAreaPrivate *priv = area->priv;
2629 if ((priv->child)&&(priv->last_in)) {
2630 priv->last_in = FALSE;
2632 synth_crossing (priv->child, 0, 0, event->x_root,
2633 event->y_root, event->time, FALSE);
2640 hildon_pannable_area_button_release_cb (GtkWidget * widget,
2641 GdkEventButton * event)
2643 HildonPannableArea *area = HILDON_PANNABLE_AREA (widget);
2644 HildonPannableAreaPrivate *priv = area->priv;
2649 if (((event->time == priv->last_time) && (priv->last_type == 3))
2650 || (gtk_bin_get_child (GTK_BIN (widget)) == NULL)
2651 || (!priv->button_pressed) || (!priv->enabled) || (event->button != 1))
2654 /* if last event was a motion-notify we have to check the movement
2655 and launch the animation */
2656 if (priv->last_type == 2) {
2658 dx = event->x - priv->x;
2659 dy = event->y - priv->y;
2661 hildon_pannable_area_check_move (area, (GdkEventMotion *) event, &dx, &dy);
2664 gdouble delta = event->time - priv->last_time;
2666 hildon_pannable_area_handle_move (area, (GdkEventMotion *) event, &dx, &dy);
2668 /* move all the way to the last position now */
2669 if (priv->motion_event_scroll_timeout) {
2670 g_source_remove (priv->motion_event_scroll_timeout);
2671 hildon_pannable_area_motion_event_scroll_timeout (HILDON_PANNABLE_AREA (widget));
2676 if ((ABS (dx) < 4.0) && (delta >= CURSOR_STOPPED_TIMEOUT))
2679 if ((ABS (dy) < 4.0) && (delta >= CURSOR_STOPPED_TIMEOUT))
2684 /* If overshoot has been initiated with a finger down, on release set max speed */
2685 if (priv->overshot_dist_y != 0) {
2686 priv->overshooting_y = priv->bounce_steps; /* Hack to stop a bounce in the finger down case */
2687 priv->vel_y = priv->vmax_overshooting;
2690 if (priv->overshot_dist_x != 0) {
2691 priv->overshooting_x = priv->bounce_steps; /* Hack to stop a bounce in the finger down case */
2692 priv->vel_x = priv->vmax_overshooting;
2695 priv->button_pressed = FALSE;
2697 if ((ABS (priv->vel_y) >= priv->vmin) ||
2698 (ABS (priv->vel_x) >= priv->vmin)) {
2700 /* we have to move because we are in overshooting position*/
2702 gboolean result_val;
2704 g_signal_emit (area,
2705 pannable_area_signals[PANNING_STARTED],
2709 priv->scroll_indicator_alpha = 1.0;
2711 if (ABS (priv->vel_x) > MAX_SPEED_THRESHOLD)
2712 priv->vel_x = (priv->vel_x > 0) ? priv->vmax : -priv->vmax;
2714 if (ABS (priv->vel_y) > MAX_SPEED_THRESHOLD)
2715 priv->vel_y = (priv->vel_y > 0) ? priv->vmax : -priv->vmax;
2718 priv->idle_id = gdk_threads_add_timeout ((gint) (1000.0 / (gdouble) priv->sps),
2720 hildon_pannable_area_timeout, widget);
2723 g_signal_emit (widget, pannable_area_signals[PANNING_FINISHED], 0);
2726 priv->scroll_indicator_event_interrupt = 0;
2727 priv->scroll_delay_counter = priv->scrollbar_fade_delay;
2729 hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget),
2730 priv->scroll_indicator_alpha);
2732 priv->last_time = event->time;
2733 priv->last_type = 3;
2736 priv->moved = FALSE;
2741 hildon_pannable_area_get_topmost (gtk_bin_get_child (GTK_BIN (widget))->window,
2742 event->x, event->y, &x, &y, GDK_BUTTON_RELEASE_MASK);
2744 event = (GdkEventButton *) gdk_event_copy ((GdkEvent *) event);
2748 /* Leave the widget if we've moved - This doesn't break selection,
2749 * but stops buttons from being clicked.
2751 if ((child != priv->child) || (priv->moved)) {
2752 /* Send synthetic leave event */
2753 synth_crossing (priv->child, x, y, event->x_root,
2754 event->y_root, event->time, FALSE);
2755 /* insure no click will happen for widgets that do not handle
2759 /* Send synthetic button release event */
2760 ((GdkEventAny *) event)->window = g_object_ref (priv->child);
2761 gdk_event_put ((GdkEvent *) event);
2763 /* Send synthetic button release event */
2764 ((GdkEventAny *) event)->window = g_object_ref (child);
2765 gdk_event_put ((GdkEvent *) event);
2766 /* Send synthetic leave event */
2767 synth_crossing (priv->child, x, y, event->x_root,
2768 event->y_root, event->time, FALSE);
2770 g_object_remove_weak_pointer ((GObject *) priv->child,
2771 (gpointer) & priv->child);
2773 priv->moved = FALSE;
2774 gdk_event_free ((GdkEvent *) event);
2779 /* utility event handler */
2781 hildon_pannable_area_scroll_cb (GtkWidget *widget,
2782 GdkEventScroll *event)
2784 GtkAdjustment *adj = NULL;
2785 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
2787 if ((!priv->enabled) ||
2788 (gtk_bin_get_child (GTK_BIN (widget)) == NULL))
2791 priv->scroll_indicator_event_interrupt = 0;
2792 priv->scroll_delay_counter = priv->scrollbar_fade_delay + 20;
2794 hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget), 1.0);
2796 /* Stop inertial scrolling */
2797 if (priv->idle_id) {
2800 priv->overshooting_x = 0;
2801 priv->overshooting_y = 0;
2803 if ((priv->overshot_dist_x>0)||(priv->overshot_dist_y>0)) {
2804 priv->overshot_dist_x = 0;
2805 priv->overshot_dist_y = 0;
2807 gtk_widget_queue_resize (GTK_WIDGET (widget));
2810 g_signal_emit (widget, pannable_area_signals[PANNING_FINISHED], 0);
2812 g_source_remove (priv->idle_id);
2816 if (event->direction == GDK_SCROLL_UP || event->direction == GDK_SCROLL_DOWN)
2817 adj = priv->vadjust;
2819 adj = priv->hadjust;
2823 gdouble delta, new_value;
2825 /* from gtkrange.c calculate delta*/
2826 delta = pow (adj->page_size, 2.0 / 3.0);
2828 if (event->direction == GDK_SCROLL_UP ||
2829 event->direction == GDK_SCROLL_LEFT)
2832 new_value = CLAMP (adj->value + delta, adj->lower, adj->upper - adj->page_size);
2834 gtk_adjustment_set_value (adj, new_value);
2841 hildon_pannable_area_child_mapped (GtkWidget *widget,
2845 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (user_data)->priv;
2847 if (priv->event_window != NULL && priv->enabled)
2848 gdk_window_raise (priv->event_window);
2852 hildon_pannable_area_add (GtkContainer *container, GtkWidget *child)
2854 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (container)->priv;
2856 g_return_if_fail (gtk_bin_get_child (GTK_BIN (container)) == NULL);
2858 gtk_widget_set_parent (child, GTK_WIDGET (container));
2859 GTK_BIN (container)->child = child;
2861 g_signal_connect_after (child, "map-event",
2862 G_CALLBACK (hildon_pannable_area_child_mapped),
2865 if (!gtk_widget_set_scroll_adjustments (child, priv->hadjust, priv->vadjust)) {
2866 g_warning ("%s: cannot add non scrollable widget, "
2867 "wrap it in a viewport", __FUNCTION__);
2872 hildon_pannable_area_remove (GtkContainer *container, GtkWidget *child)
2874 g_return_if_fail (HILDON_IS_PANNABLE_AREA (container));
2875 g_return_if_fail (child != NULL);
2876 g_return_if_fail (gtk_bin_get_child (GTK_BIN (container)) == child);
2878 gtk_widget_set_scroll_adjustments (child, NULL, NULL);
2880 g_signal_handlers_disconnect_by_func (child,
2881 hildon_pannable_area_child_mapped,
2884 /* chain parent class handler to remove child */
2885 GTK_CONTAINER_CLASS (hildon_pannable_area_parent_class)->remove (container, child);
2889 * This method calculates a factor necessary to determine the initial distance
2890 * to jump in hildon_pannable_area_scroll_to(). For fixed time and frames per
2891 * second, we know in how many frames 'n' we need to reach the destination
2892 * point. We know that, for a distance d,
2894 * d = d_0 + d_1 + ... + d_n
2896 * where d_i is the distance travelled in the i-th frame and decel_factor is
2897 * the deceleration factor. This can be rewritten as
2899 * d = d_0 + (d_0 * decel_factor) + ... + (d_n-1 * decel_factor),
2901 * since the distance travelled on each frame is the distance travelled in the
2902 * previous frame reduced by the deceleration factor. Reducing this and
2903 * factoring d_0 out, we get
2905 * d = d_0 (1 + decel_factor + ... + decel_factor^(n-1)).
2907 * Since the sum is independent of the distance to be travelled, we can define
2910 * vel_factor = 1 + decel_factor + ... + decel_factor^(n-1).
2912 * That's the gem we calculate in this method.
2915 hildon_pannable_calculate_vel_factor (HildonPannableArea * self)
2917 HildonPannableAreaPrivate *priv = self->priv;
2922 n = ceil (priv->sps * priv->scroll_time);
2924 for (i = 1; i < n && fct_i >= RATIO_TOLERANCE; i++) {
2925 fct_i *= priv->decel;
2929 priv->vel_factor = fct;
2933 * hildon_pannable_area_new:
2935 * Create a new pannable area widget
2937 * Returns: the newly created #HildonPannableArea
2943 hildon_pannable_area_new (void)
2945 return g_object_new (HILDON_TYPE_PANNABLE_AREA, NULL);
2949 * hildon_pannable_area_new_full:
2950 * @mode: #HildonPannableAreaMode
2951 * @enabled: Value for the enabled property
2952 * @vel_min: Value for the velocity-min property
2953 * @vel_max: Value for the velocity-max property
2954 * @decel: Value for the deceleration property
2955 * @sps: Value for the sps property
2957 * Create a new #HildonPannableArea widget and set various properties
2959 * returns: the newly create #HildonPannableArea
2965 hildon_pannable_area_new_full (gint mode, gboolean enabled,
2966 gdouble vel_min, gdouble vel_max,
2967 gdouble decel, guint sps)
2969 return g_object_new (HILDON_TYPE_PANNABLE_AREA,
2972 "velocity_min", vel_min,
2973 "velocity_max", vel_max,
2974 "deceleration", decel, "sps", sps, NULL);
2978 * hildon_pannable_area_add_with_viewport:
2979 * @area: A #HildonPannableArea
2980 * @child: Child widget to add to the viewport
2982 * Convenience function used to add a child to a #GtkViewport, and add the
2983 * viewport to the scrolled window.
2989 hildon_pannable_area_add_with_viewport (HildonPannableArea * area,
2993 GtkWidget *viewport;
2995 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
2996 g_return_if_fail (GTK_IS_WIDGET (child));
2997 g_return_if_fail (child->parent == NULL);
2999 bin = GTK_BIN (area);
3001 if (bin->child != NULL)
3003 g_return_if_fail (GTK_IS_VIEWPORT (bin->child));
3004 g_return_if_fail (GTK_BIN (bin->child)->child == NULL);
3006 viewport = bin->child;
3010 HildonPannableAreaPrivate *priv = area->priv;
3012 viewport = gtk_viewport_new (priv->hadjust,
3014 gtk_viewport_set_shadow_type (GTK_VIEWPORT (viewport), GTK_SHADOW_NONE);
3015 gtk_container_add (GTK_CONTAINER (area), viewport);
3018 gtk_widget_show (viewport);
3019 gtk_container_add (GTK_CONTAINER (viewport), child);
3023 * hildon_pannable_area_scroll_to:
3024 * @area: A #HildonPannableArea.
3025 * @x: The x coordinate of the destination point or -1 to ignore this axis.
3026 * @y: The y coordinate of the destination point or -1 to ignore this axis.
3028 * Smoothly scrolls @area to ensure that (@x, @y) is a visible point
3029 * on the widget. To move in only one coordinate, you must set the other one
3030 * to -1. Notice that, in %HILDON_PANNABLE_AREA_MODE_PUSH mode, this function
3031 * works just like hildon_pannable_area_jump_to().
3033 * This function is useful if you need to present the user with a particular
3034 * element inside a scrollable widget, like #GtkTreeView. For instance,
3035 * the following example shows how to scroll inside a #GtkTreeView to
3036 * make visible an item, indicated by the #GtkTreeIter @iter.
3040 * GtkTreePath *path;
3041 * GdkRectangle *rect;
3043 * path = gtk_tree_model_get_path (model, &iter);
3044 * gtk_tree_view_get_background_area (GTK_TREE_VIEW (treeview),
3045 * path, NULL, &rect);
3046 * gtk_tree_view_convert_bin_window_to_tree_coords (GTK_TREE_VIEW (treeview),
3047 * 0, rect.y, NULL, &y);
3048 * hildon_pannable_area_scroll_to (panarea, -1, y);
3049 * gtk_tree_path_free (path);
3053 * If you want to present a child widget in simpler scenarios,
3054 * use hildon_pannable_area_scroll_to_child() instead.
3056 * There is a precondition to this function: the widget must be
3057 * already realized. Check the hildon_pannable_area_jump_to_child() for
3058 * more tips regarding how to call this function during
3064 hildon_pannable_area_scroll_to (HildonPannableArea *area,
3065 const gint x, const gint y)
3067 HildonPannableAreaPrivate *priv;
3069 gint dist_x, dist_y;
3070 gboolean hscroll_visible, vscroll_visible;
3072 g_return_if_fail (GTK_WIDGET_REALIZED (area));
3073 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3077 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
3078 priv->vadjust->page_size);
3079 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
3080 priv->hadjust->page_size);
3082 if (((!vscroll_visible)&&(!hscroll_visible)) ||
3083 (x == -1 && y == -1)) {
3087 if (priv->mode == HILDON_PANNABLE_AREA_MODE_PUSH)
3088 hildon_pannable_area_jump_to (area, x, y);
3090 width = priv->hadjust->upper - priv->hadjust->lower;
3091 height = priv->vadjust->upper - priv->vadjust->lower;
3093 g_return_if_fail (x < width || y < height);
3095 if ((x > -1)&&(hscroll_visible)) {
3096 priv->scroll_to_x = x - priv->hadjust->page_size/2;
3097 dist_x = priv->scroll_to_x - priv->hadjust->value;
3099 priv->scroll_to_x = -1;
3101 priv->vel_x = - dist_x/priv->vel_factor;
3104 priv->scroll_to_x = -1;
3107 if ((y > -1)&&(vscroll_visible)) {
3108 priv->scroll_to_y = y - priv->vadjust->page_size/2;
3109 dist_y = priv->scroll_to_y - priv->vadjust->value;
3111 priv->scroll_to_y = -1;
3113 priv->vel_y = - dist_y/priv->vel_factor;
3116 priv->scroll_to_y = y;
3119 if ((priv->scroll_to_y == -1) && (priv->scroll_to_y == -1)) {
3123 hildon_pannable_area_launch_fade_timeout (area, 1.0);
3126 priv->idle_id = gdk_threads_add_timeout ((gint) (1000.0 / (gdouble) priv->sps),
3128 hildon_pannable_area_timeout, area);
3132 * hildon_pannable_area_jump_to:
3133 * @area: A #HildonPannableArea.
3134 * @x: The x coordinate of the destination point or -1 to ignore this axis.
3135 * @y: The y coordinate of the destination point or -1 to ignore this axis.
3137 * Jumps the position of @area to ensure that (@x, @y) is a visible
3138 * point in the widget. In order to move in only one coordinate, you
3139 * must set the other one to -1. See hildon_pannable_area_scroll_to()
3140 * function for an example of how to calculate the position of
3141 * children in scrollable widgets like #GtkTreeview.
3143 * There is a precondition to this function: the widget must be
3144 * already realized. Check the hildon_pannable_area_jump_to_child() for
3145 * more tips regarding how to call this function during
3151 hildon_pannable_area_jump_to (HildonPannableArea *area,
3152 const gint x, const gint y)
3154 HildonPannableAreaPrivate *priv;
3158 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3159 g_return_if_fail (GTK_WIDGET_REALIZED (area));
3160 g_return_if_fail (x >= -1 && y >= -1);
3162 if (x == -1 && y == -1) {
3168 width = priv->hadjust->upper - priv->hadjust->lower;
3169 height = priv->vadjust->upper - priv->vadjust->lower;
3171 g_return_if_fail (x < width || y < height);
3173 hv = priv->hadjust->value;
3174 vv = priv->vadjust->value;
3177 gdouble jump_to = x - priv->hadjust->page_size/2;
3179 priv->hadjust->value = CLAMP (jump_to,
3180 priv->hadjust->lower,
3181 priv->hadjust->upper -
3182 priv->hadjust->page_size);
3186 gdouble jump_to = y - priv->vadjust->page_size/2;
3188 priv->vadjust->value = CLAMP (jump_to,
3189 priv->vadjust->lower,
3190 priv->vadjust->upper -
3191 priv->vadjust->page_size);
3194 if (hv != priv->hadjust->value)
3195 gtk_adjustment_value_changed (priv->hadjust);
3197 if (vv != priv->vadjust->value)
3198 gtk_adjustment_value_changed (priv->vadjust);
3200 priv->scroll_indicator_alpha = 1.0;
3202 if (priv->scroll_indicator_timeout) {
3203 g_source_remove (priv->scroll_indicator_timeout);
3204 priv->scroll_indicator_timeout = 0;
3207 if (priv->idle_id) {
3210 priv->overshooting_x = 0;
3211 priv->overshooting_y = 0;
3213 if ((priv->overshot_dist_x>0)||(priv->overshot_dist_y>0)) {
3214 priv->overshot_dist_x = 0;
3215 priv->overshot_dist_y = 0;
3217 gtk_widget_queue_resize (GTK_WIDGET (area));
3220 g_signal_emit (area, pannable_area_signals[PANNING_FINISHED], 0);
3221 g_source_remove (priv->idle_id);
3227 * hildon_pannable_area_scroll_to_child:
3228 * @area: A #HildonPannableArea.
3229 * @child: A #GtkWidget, descendant of @area.
3231 * Smoothly scrolls until @child is visible inside @area. @child must
3232 * be a descendant of @area. If you need to scroll inside a scrollable
3233 * widget, e.g., #GtkTreeview, see hildon_pannable_area_scroll_to().
3235 * There is a precondition to this function: the widget must be
3236 * already realized. Check the hildon_pannable_area_jump_to_child() for
3237 * more tips regarding how to call this function during
3243 hildon_pannable_area_scroll_to_child (HildonPannableArea *area, GtkWidget *child)
3245 GtkWidget *bin_child;
3248 g_return_if_fail (GTK_WIDGET_REALIZED (area));
3249 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3250 g_return_if_fail (GTK_IS_WIDGET (child));
3251 g_return_if_fail (gtk_widget_is_ancestor (child, GTK_WIDGET (area)));
3253 if (GTK_BIN (area)->child == NULL)
3256 /* We need to get to check the child of the inside the area */
3257 bin_child = GTK_BIN (area)->child;
3259 /* we check if we added a viewport */
3260 if (GTK_IS_VIEWPORT (bin_child)) {
3261 bin_child = GTK_BIN (bin_child)->child;
3264 if (gtk_widget_translate_coordinates (child, bin_child, 0, 0, &x, &y))
3265 hildon_pannable_area_scroll_to (area, x, y);
3269 * hildon_pannable_area_jump_to_child:
3270 * @area: A #HildonPannableArea.
3271 * @child: A #GtkWidget, descendant of @area.
3273 * Jumps to make sure @child is visible inside @area. @child must
3274 * be a descendant of @area. If you want to move inside a scrollable
3275 * widget, like, #GtkTreeview, see hildon_pannable_area_scroll_to().
3277 * There is a precondition to this function: the widget must be
3278 * already realized. You can control if the widget is ready with the
3279 * GTK_WIDGET_REALIZED macro. If you want to call this function during
3280 * the initialization process of the widget do it inside a callback to
3281 * the ::realize signal, using g_signal_connect_after() function.
3286 hildon_pannable_area_jump_to_child (HildonPannableArea *area, GtkWidget *child)
3288 GtkWidget *bin_child;
3291 g_return_if_fail (GTK_WIDGET_REALIZED (area));
3292 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3293 g_return_if_fail (GTK_IS_WIDGET (child));
3294 g_return_if_fail (gtk_widget_is_ancestor (child, GTK_WIDGET (area)));
3296 if (gtk_bin_get_child (GTK_BIN (area)) == NULL)
3299 /* We need to get to check the child of the inside the area */
3300 bin_child = gtk_bin_get_child (GTK_BIN (area));
3302 /* we check if we added a viewport */
3303 if (GTK_IS_VIEWPORT (bin_child)) {
3304 bin_child = gtk_bin_get_child (GTK_BIN (bin_child));
3307 if (gtk_widget_translate_coordinates (child, bin_child, 0, 0, &x, &y))
3308 hildon_pannable_area_jump_to (area, x, y);
3312 * hildon_pannable_get_child_widget_at:
3313 * @area: A #HildonPannableArea.
3314 * @x: horizontal coordinate of the point
3315 * @y: vertical coordinate of the point
3317 * Get the widget at the point (x, y) inside the pannable area. In
3318 * case no widget found it returns NULL.
3320 * returns: the #GtkWidget if we find a widget, NULL in any other case
3325 hildon_pannable_get_child_widget_at (HildonPannableArea *area,
3326 gdouble x, gdouble y)
3328 GdkWindow *window = NULL;
3329 GtkWidget *child_widget = NULL;
3331 window = hildon_pannable_area_get_topmost
3332 (gtk_bin_get_child (GTK_BIN (area))->window,
3333 x, y, NULL, NULL, GDK_ALL_EVENTS_MASK);
3335 gdk_window_get_user_data (window, (gpointer) &child_widget);
3337 return child_widget;
3342 * hildon_pannable_area_get_hadjustment:
3343 * @area: A #HildonPannableArea.
3345 * Returns the horizontal adjustment. This adjustment is the internal
3346 * widget adjustment used to control the animations. Do not modify it
3347 * directly to change the position of the pannable, to do that use the
3348 * pannable API. If you modify the object directly it could cause
3349 * artifacts in the animations.
3351 * returns: The horizontal #GtkAdjustment
3356 hildon_pannable_area_get_hadjustment (HildonPannableArea *area)
3359 g_return_val_if_fail (HILDON_IS_PANNABLE_AREA (area), NULL);
3361 return area->priv->hadjust;
3365 * hildon_pannable_area_get_vadjustment:
3366 * @area: A #HildonPannableArea.
3368 * Returns the vertical adjustment. This adjustment is the internal
3369 * widget adjustment used to control the animations. Do not modify it
3370 * directly to change the position of the pannable, to do that use the
3371 * pannable API. If you modify the object directly it could cause
3372 * artifacts in the animations.
3374 * returns: The vertical #GtkAdjustment
3379 hildon_pannable_area_get_vadjustment (HildonPannableArea *area)
3381 g_return_val_if_fail (HILDON_IS_PANNABLE_AREA (area), NULL);
3383 return area->priv->vadjust;
3388 * hildon_pannable_area_get_size_request_policy:
3389 * @area: A #HildonPannableArea.
3391 * This function returns the current size request policy of the
3392 * widget. That policy controls the way the size_request is done in
3393 * the pannable area. Check
3394 * hildon_pannable_area_set_size_request_policy() for a more detailed
3397 * returns: the policy is currently being used in the widget
3398 * #HildonSizeRequestPolicy.
3402 HildonSizeRequestPolicy
3403 hildon_pannable_area_get_size_request_policy (HildonPannableArea *area)
3405 HildonPannableAreaPrivate *priv;
3407 g_return_val_if_fail (HILDON_IS_PANNABLE_AREA (area), FALSE);
3411 return priv->size_request_policy;
3415 * hildon_pannable_area_set_size_request_policy:
3416 * @area: A #HildonPannableArea.
3417 * @size_request_policy: One of the allowed #HildonSizeRequestPolicy
3419 * This function sets the pannable area size request policy. That
3420 * policy controls the way the size_request is done in the pannable
3421 * area. Pannable can use the size request of its children
3422 * (#HILDON_SIZE_REQUEST_CHILDREN) or the minimum size required for
3423 * the area itself (#HILDON_SIZE_REQUEST_MINIMUM), the latter is the
3424 * default. Recall this size depends on the scrolling policy you are
3425 * requesting to the pannable area, if you set #GTK_POLICY_NEVER this
3426 * parameter will not have any effect with
3427 * #HILDON_SIZE_REQUEST_MINIMUM set.
3431 * Deprecated: This method and the policy request is deprecated, DO
3432 * NOT use it in future code, the only policy properly supported in
3433 * gtk+ nowadays is the minimum size. Use #gtk_window_set_default_size
3434 * or #gtk_window_set_geometry_hints with the proper size in your case
3435 * to define the height of your dialogs.
3438 hildon_pannable_area_set_size_request_policy (HildonPannableArea *area,
3439 HildonSizeRequestPolicy size_request_policy)
3441 HildonPannableAreaPrivate *priv;
3443 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3447 if (priv->size_request_policy == size_request_policy)
3450 priv->size_request_policy = size_request_policy;
3452 gtk_widget_queue_resize (GTK_WIDGET (area));
3454 g_object_notify (G_OBJECT (area), "size-request-policy");