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;
153 static guint pannable_area_signals [LAST_SIGNAL] = { 0 };
161 PROP_VEL_MAX_OVERSHOOTING,
162 PROP_VELOCITY_FAST_FACTOR,
166 PROP_PANNING_THRESHOLD,
167 PROP_SCROLLBAR_FADE_DELAY,
170 PROP_DIRECTION_ERROR_MARGIN,
171 PROP_VSCROLLBAR_POLICY,
172 PROP_HSCROLLBAR_POLICY,
177 PROP_LOW_FRICTION_MODE,
178 PROP_SIZE_REQUEST_POLICY,
184 static void hildon_pannable_area_class_init (HildonPannableAreaClass * klass);
185 static void hildon_pannable_area_init (HildonPannableArea * area);
186 static void hildon_pannable_area_get_property (GObject * object,
190 static void hildon_pannable_area_set_property (GObject * object,
192 const GValue * value,
194 static void hildon_pannable_area_dispose (GObject * object);
195 static void hildon_pannable_area_realize (GtkWidget * widget);
196 static void hildon_pannable_area_unrealize (GtkWidget * widget);
197 static void hildon_pannable_area_size_request (GtkWidget * widget,
198 GtkRequisition * requisition);
199 static void hildon_pannable_area_size_allocate (GtkWidget * widget,
200 GtkAllocation * allocation);
201 static void hildon_pannable_area_child_allocate_calculate (GtkWidget * widget,
202 GtkAllocation * allocation,
203 GtkAllocation * child_allocation);
204 static void hildon_pannable_area_style_set (GtkWidget * widget,
205 GtkStyle * previous_style);
206 static void hildon_pannable_area_map (GtkWidget * widget);
207 static void hildon_pannable_area_unmap (GtkWidget * widget);
208 static void hildon_pannable_area_grab_notify (GtkWidget *widget,
209 gboolean was_grabbed,
211 #if USE_CAIRO_SCROLLBARS == 1
212 static void rgb_from_gdkcolor (GdkColor *color, gdouble *r, gdouble *g, gdouble *b);
213 #else /* USE_CAIRO_SCROLLBARS */
214 static void tranparency_color (GdkColor *color,
217 gdouble transparency);
218 #endif /* USE_CAIRO_SCROLLBARS */
219 static void hildon_pannable_draw_vscroll (GtkWidget * widget,
220 GdkColor *back_color,
221 GdkColor *scroll_color);
222 static void hildon_pannable_draw_hscroll (GtkWidget * widget,
223 GdkColor *back_color,
224 GdkColor *scroll_color);
225 static void hildon_pannable_area_initial_effect (GtkWidget * widget);
226 static void hildon_pannable_area_redraw (HildonPannableArea * area);
227 static void hildon_pannable_area_launch_fade_timeout (HildonPannableArea * area,
229 static void hildon_pannable_area_adjust_value_changed (HildonPannableArea * area,
231 static void hildon_pannable_area_adjust_changed (HildonPannableArea * area,
233 static gboolean hildon_pannable_area_scroll_indicator_fade(HildonPannableArea * area);
234 static gboolean hildon_pannable_area_expose_event (GtkWidget * widget,
235 GdkEventExpose * event);
236 static GdkWindow * hildon_pannable_area_get_topmost (GdkWindow * window,
238 gint * tx, gint * ty,
240 static void synth_crossing (GdkWindow * child,
242 gint x_root, gint y_root,
243 guint32 time, gboolean in);
244 static gboolean hildon_pannable_area_button_press_cb (GtkWidget * widget,
245 GdkEventButton * event);
246 static void hildon_pannable_area_refresh (HildonPannableArea * area);
247 static gboolean hildon_pannable_area_check_scrollbars (HildonPannableArea * area);
248 static void hildon_pannable_axis_scroll (HildonPannableArea *area,
249 GtkAdjustment *adjust,
257 static void hildon_pannable_area_scroll (HildonPannableArea *area,
258 gdouble x, gdouble y);
259 static gboolean hildon_pannable_area_timeout (HildonPannableArea * area);
260 static void hildon_pannable_area_calculate_velocity (gdouble *vel,
264 gdouble drag_inertia,
267 static gboolean hildon_pannable_area_motion_event_scroll_timeout (HildonPannableArea *area);
268 static void hildon_pannable_area_motion_event_scroll (HildonPannableArea *area,
269 gdouble x, gdouble y);
270 static void hildon_pannable_area_check_move (HildonPannableArea *area,
271 GdkEventMotion * event,
274 static void hildon_pannable_area_handle_move (HildonPannableArea *area,
275 GdkEventMotion * event,
278 static gboolean hildon_pannable_area_motion_notify_cb (GtkWidget * widget,
279 GdkEventMotion * event);
280 static gboolean hildon_pannable_leave_notify_event (GtkWidget *widget,
281 GdkEventCrossing *event);
282 static gboolean hildon_pannable_area_button_release_cb (GtkWidget * widget,
283 GdkEventButton * event);
284 static gboolean hildon_pannable_area_scroll_cb (GtkWidget *widget,
285 GdkEventScroll *event);
286 static void hildon_pannable_area_child_mapped (GtkWidget *widget,
289 static void hildon_pannable_area_add (GtkContainer *container, GtkWidget *child);
290 static void hildon_pannable_area_remove (GtkContainer *container, GtkWidget *child);
291 static void hildon_pannable_calculate_vel_factor (HildonPannableArea * self);
295 hildon_pannable_area_class_init (HildonPannableAreaClass * klass)
297 GObjectClass *object_class = G_OBJECT_CLASS (klass);
298 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
299 GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
302 g_type_class_add_private (klass, sizeof (HildonPannableAreaPrivate));
304 object_class->get_property = hildon_pannable_area_get_property;
305 object_class->set_property = hildon_pannable_area_set_property;
306 object_class->dispose = hildon_pannable_area_dispose;
308 widget_class->realize = hildon_pannable_area_realize;
309 widget_class->unrealize = hildon_pannable_area_unrealize;
310 widget_class->map = hildon_pannable_area_map;
311 widget_class->unmap = hildon_pannable_area_unmap;
312 widget_class->size_request = hildon_pannable_area_size_request;
313 widget_class->size_allocate = hildon_pannable_area_size_allocate;
314 widget_class->expose_event = hildon_pannable_area_expose_event;
315 widget_class->style_set = hildon_pannable_area_style_set;
316 widget_class->button_press_event = hildon_pannable_area_button_press_cb;
317 widget_class->button_release_event = hildon_pannable_area_button_release_cb;
318 widget_class->motion_notify_event = hildon_pannable_area_motion_notify_cb;
319 widget_class->leave_notify_event = hildon_pannable_leave_notify_event;
320 widget_class->scroll_event = hildon_pannable_area_scroll_cb;
322 container_class->add = hildon_pannable_area_add;
323 container_class->remove = hildon_pannable_area_remove;
325 klass->horizontal_movement = NULL;
326 klass->vertical_movement = NULL;
328 g_object_class_install_property (object_class,
330 g_param_spec_boolean ("enabled",
332 "Enable or disable finger-scroll.",
337 g_object_class_install_property (object_class,
338 PROP_VSCROLLBAR_POLICY,
339 g_param_spec_enum ("vscrollbar_policy",
341 "Visual policy of the vertical scrollbar",
342 GTK_TYPE_POLICY_TYPE,
343 GTK_POLICY_AUTOMATIC,
347 g_object_class_install_property (object_class,
348 PROP_HSCROLLBAR_POLICY,
349 g_param_spec_enum ("hscrollbar_policy",
351 "Visual policy of the horizontal scrollbar",
352 GTK_TYPE_POLICY_TYPE,
353 GTK_POLICY_AUTOMATIC,
357 g_object_class_install_property (object_class,
359 g_param_spec_enum ("mode",
361 "Change the finger-scrolling mode.",
362 HILDON_TYPE_PANNABLE_AREA_MODE,
363 HILDON_PANNABLE_AREA_MODE_AUTO,
367 g_object_class_install_property (object_class,
369 g_param_spec_flags ("mov_mode",
370 "Scroll movement mode",
371 "Controls if the widget can scroll vertically, horizontally or both",
372 HILDON_TYPE_MOVEMENT_MODE,
373 HILDON_MOVEMENT_MODE_VERT,
377 g_object_class_install_property (object_class,
379 g_param_spec_double ("velocity_min",
380 "Minimum scroll velocity",
381 "Minimum distance the child widget should scroll "
382 "per 'frame', in pixels per frame.",
387 g_object_class_install_property (object_class,
389 g_param_spec_double ("velocity_max",
390 "Maximum scroll velocity",
391 "Maximum distance the child widget should scroll "
392 "per 'frame', in pixels per frame.",
397 g_object_class_install_property (object_class,
398 PROP_VEL_MAX_OVERSHOOTING,
399 g_param_spec_double ("velocity_overshooting_max",
400 "Maximum scroll velocity when overshooting",
401 "Maximum distance the child widget should scroll "
402 "per 'frame', in pixels per frame when it overshoots after hitting the edge.",
407 g_object_class_install_property (object_class,
408 PROP_VELOCITY_FAST_FACTOR,
409 g_param_spec_double ("velocity_fast_factor",
410 "Fast velocity factor",
411 "Minimum velocity that is considered 'fast': "
412 "children widgets won't receive button presses. "
413 "Expressed as a fraction of the maximum velocity.",
418 g_object_class_install_property (object_class,
420 g_param_spec_double ("deceleration",
421 "Deceleration multiplier",
422 "The multiplier used when decelerating when in "
423 "acceleration scrolling mode.",
428 g_object_class_install_property (object_class,
430 g_param_spec_double ("drag_inertia",
431 "Inertia of the cursor dragging",
432 "Percentage of the calculated speed in each moment we are are going to use"
433 "to calculate the launch speed, the other part would be the speed"
434 "calculated previously",
439 g_object_class_install_property (object_class,
441 g_param_spec_uint ("sps",
442 "Scrolls per second",
443 "Amount of scroll events to generate per second.",
448 g_object_class_install_property (object_class,
449 PROP_PANNING_THRESHOLD,
450 g_param_spec_uint ("panning_threshold",
451 "Threshold to consider a motion event an scroll",
452 "Amount of pixels to consider a motion event an scroll, if it is less"
453 "it is a click detected incorrectly by the touch screen.",
458 g_object_class_install_property (object_class,
459 PROP_SCROLLBAR_FADE_DELAY,
460 g_param_spec_uint ("scrollbar_fade_delay",
461 "Time before starting to fade the scrollbar",
462 "Time the scrollbar is going to be visible if the widget is not in"
463 "action in miliseconds",
468 g_object_class_install_property (object_class,
470 g_param_spec_uint ("bounce_steps",
472 "Number of steps that is going to be used to bounce when hitting the"
473 "edge, the rubberband effect depends on it",
478 g_object_class_install_property (object_class,
480 g_param_spec_uint ("force",
481 "Multiplier of the calculated speed",
482 "Force applied to the movement, multiplies the calculated speed of the"
483 "user movement the cursor in the screen",
488 g_object_class_install_property (object_class,
489 PROP_DIRECTION_ERROR_MARGIN,
490 g_param_spec_uint ("direction_error_margin",
491 "Margin in the direction detection",
492 "After detecting the direction of the movement (horizontal or"
493 "vertical), we can add this margin of error to allow the movement in"
494 "the other direction even apparently it is not",
499 g_object_class_install_property (object_class,
501 g_param_spec_int ("vovershoot_max",
502 "Vertical overshoot distance",
503 "Space we allow the widget to pass over its vertical limits when"
504 "hitting the edges, set 0 in order to deactivate overshooting.",
509 g_object_class_install_property (object_class,
511 g_param_spec_int ("hovershoot_max",
512 "Horizontal overshoot distance",
513 "Space we allow the widget to pass over its horizontal limits when"
514 "hitting the edges, set 0 in order to deactivate overshooting.",
519 g_object_class_install_property (object_class,
521 g_param_spec_double ("scroll_time",
522 "Time to scroll to a position",
523 "The time to scroll to a position when calling the hildon_pannable_scroll_to function",
528 g_object_class_install_property (object_class,
530 g_param_spec_boolean ("initial-hint",
532 "Whether to hint the user about the pannability of the container.",
537 g_object_class_install_property (object_class,
538 PROP_LOW_FRICTION_MODE,
539 g_param_spec_boolean ("low-friction-mode",
540 "Do not decelerate the initial velocity",
541 "Avoid decelerating the panning movement, like no friction, the widget"
542 "will stop in the edges or if the user clicks.",
547 g_object_class_install_property (object_class,
548 PROP_SIZE_REQUEST_POLICY,
549 g_param_spec_enum ("size-request-policy",
550 "Size Requisition policy",
551 "Controls the size request policy of the widget",
552 HILDON_TYPE_SIZE_REQUEST_POLICY,
553 HILDON_SIZE_REQUEST_MINIMUM,
557 g_object_class_install_property (object_class,
559 g_param_spec_object ("hadjustment",
560 "Horizontal Adjustment",
561 "The GtkAdjustment for the horizontal position",
564 g_object_class_install_property (object_class,
566 g_param_spec_object ("vadjustment",
567 "Vertical Adjustment",
568 "The GtkAdjustment for the vertical position",
572 gtk_widget_class_install_style_property (widget_class,
575 "Width of the scroll indicators",
576 "Pixel width used to draw the scroll indicators.",
581 * HildonPannableArea::horizontal-movement:
582 * @hildonpannable: the object which received the signal
583 * @direction: the direction of the movement #HILDON_MOVEMENT_LEFT or #HILDON_MOVEMENT_RIGHT
584 * @initial_x: the x coordinate of the point where the user clicked to start the movement
585 * @initial_y: the y coordinate of the point where the user clicked to start the movement
587 * The horizontal-movement signal is emitted when the pannable area
588 * detects a horizontal movement. The detection does not mean the
589 * widget is going to move (i.e. maybe the children are smaller
590 * horizontally than the screen).
594 pannable_area_signals[HORIZONTAL_MOVEMENT] =
595 g_signal_new ("horizontal_movement",
596 G_TYPE_FROM_CLASS (object_class),
597 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
598 G_STRUCT_OFFSET (HildonPannableAreaClass, horizontal_movement),
600 _hildon_marshal_VOID__INT_DOUBLE_DOUBLE,
607 * HildonPannableArea::vertical-movement:
608 * @hildonpannable: the object which received the signal
609 * @direction: the direction of the movement #HILDON_MOVEMENT_UP or #HILDON_MOVEMENT_DOWN
610 * @initial_x: the x coordinate of the point where the user clicked to start the movement
611 * @initial_y: the y coordinate of the point where the user clicked to start the movement
613 * The vertical-movement signal is emitted when the pannable area
614 * detects a vertical movement. The detection does not mean the
615 * widget is going to move (i.e. maybe the children are smaller
616 * vertically than the screen).
620 pannable_area_signals[VERTICAL_MOVEMENT] =
621 g_signal_new ("vertical_movement",
622 G_TYPE_FROM_CLASS (object_class),
623 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
624 G_STRUCT_OFFSET (HildonPannableAreaClass, vertical_movement),
626 _hildon_marshal_VOID__INT_DOUBLE_DOUBLE,
635 hildon_pannable_area_init (HildonPannableArea * area)
637 HildonPannableAreaPrivate *priv = PANNABLE_AREA_PRIVATE (area);
639 GTK_WIDGET_UNSET_FLAGS (area, GTK_NO_WINDOW);
644 priv->button_pressed = FALSE;
647 priv->vscroll_visible = TRUE;
648 priv->hscroll_visible = TRUE;
649 priv->indicator_width = 6;
650 priv->overshot_dist_x = 0;
651 priv->overshot_dist_y = 0;
652 priv->overshooting_y = 0;
653 priv->overshooting_x = 0;
657 priv->scroll_indicator_alpha = 0.0;
658 priv->scroll_indicator_timeout = 0;
659 priv->motion_event_scroll_timeout = 0;
660 priv->scroll_indicator_event_interrupt = 0;
661 priv->scroll_delay_counter = 0;
662 priv->scrollbar_fade_delay = 0;
663 priv->scroll_to_x = -1;
664 priv->scroll_to_y = -1;
665 priv->first_drag = TRUE;
666 priv->initial_effect = TRUE;
667 priv->child_width = 0;
668 priv->child_height = 0;
669 priv->last_in = TRUE;
673 gtk_widget_add_events (GTK_WIDGET (area), GDK_POINTER_MOTION_HINT_MASK);
676 GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
678 GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
680 g_object_ref_sink (G_OBJECT (priv->hadjust));
681 g_object_ref_sink (G_OBJECT (priv->vadjust));
683 g_signal_connect_swapped (priv->hadjust, "value-changed",
684 G_CALLBACK (hildon_pannable_area_adjust_value_changed), area);
685 g_signal_connect_swapped (priv->vadjust, "value-changed",
686 G_CALLBACK (hildon_pannable_area_adjust_value_changed), area);
687 g_signal_connect_swapped (priv->hadjust, "changed",
688 G_CALLBACK (hildon_pannable_area_adjust_changed), area);
689 g_signal_connect_swapped (priv->vadjust, "changed",
690 G_CALLBACK (hildon_pannable_area_adjust_changed), area);
691 g_signal_connect (area, "grab-notify",
692 G_CALLBACK (hildon_pannable_area_grab_notify), NULL);
696 hildon_pannable_area_get_property (GObject * object,
701 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (object)->priv;
703 switch (property_id) {
705 g_value_set_boolean (value, priv->enabled);
708 g_value_set_enum (value, priv->mode);
710 case PROP_MOVEMENT_MODE:
711 g_value_set_flags (value, priv->mov_mode);
713 case PROP_VELOCITY_MIN:
714 g_value_set_double (value, priv->vmin);
716 case PROP_VELOCITY_MAX:
717 g_value_set_double (value, priv->vmax);
719 case PROP_VEL_MAX_OVERSHOOTING:
720 g_value_set_double (value, priv->vmax_overshooting);
722 case PROP_VELOCITY_FAST_FACTOR:
723 g_value_set_double (value, priv->vfast_factor);
725 case PROP_DECELERATION:
726 g_value_set_double (value, priv->decel);
728 case PROP_DRAG_INERTIA:
729 g_value_set_double (value, priv->drag_inertia);
732 g_value_set_uint (value, priv->sps);
734 case PROP_PANNING_THRESHOLD:
735 g_value_set_uint (value, priv->panning_threshold);
737 case PROP_SCROLLBAR_FADE_DELAY:
738 /* convert to miliseconds */
739 g_value_set_uint (value, priv->scrollbar_fade_delay * SCROLL_FADE_TIMEOUT);
741 case PROP_BOUNCE_STEPS:
742 g_value_set_uint (value, priv->bounce_steps);
745 g_value_set_uint (value, priv->force);
747 case PROP_DIRECTION_ERROR_MARGIN:
748 g_value_set_uint (value, priv->direction_error_margin);
750 case PROP_VSCROLLBAR_POLICY:
751 g_value_set_enum (value, priv->vscrollbar_policy);
753 case PROP_HSCROLLBAR_POLICY:
754 g_value_set_enum (value, priv->hscrollbar_policy);
756 case PROP_VOVERSHOOT_MAX:
757 g_value_set_int (value, priv->vovershoot_max);
759 case PROP_HOVERSHOOT_MAX:
760 g_value_set_int (value, priv->hovershoot_max);
762 case PROP_SCROLL_TIME:
763 g_value_set_double (value, priv->scroll_time);
765 case PROP_INITIAL_HINT:
766 g_value_set_boolean (value, priv->initial_hint);
768 case PROP_LOW_FRICTION_MODE:
769 g_value_set_boolean (value, priv->low_friction_mode);
771 case PROP_SIZE_REQUEST_POLICY:
772 g_value_set_enum (value, priv->size_request_policy);
774 case PROP_HADJUSTMENT:
775 g_value_set_object (value,
776 hildon_pannable_area_get_hadjustment
777 (HILDON_PANNABLE_AREA (object)));
779 case PROP_VADJUSTMENT:
780 g_value_set_object (value,
781 hildon_pannable_area_get_vadjustment
782 (HILDON_PANNABLE_AREA (object)));
785 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
790 hildon_pannable_area_set_property (GObject * object,
792 const GValue * value,
795 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (object)->priv;
798 switch (property_id) {
800 enabled = g_value_get_boolean (value);
802 if ((priv->enabled != enabled) && (GTK_WIDGET_REALIZED (object))) {
804 gdk_window_raise (priv->event_window);
806 gdk_window_lower (priv->event_window);
809 priv->enabled = enabled;
812 priv->mode = g_value_get_enum (value);
814 case PROP_MOVEMENT_MODE:
815 priv->mov_mode = g_value_get_flags (value);
817 case PROP_VELOCITY_MIN:
818 priv->vmin = g_value_get_double (value);
820 case PROP_VELOCITY_MAX:
821 priv->vmax = g_value_get_double (value);
823 case PROP_VEL_MAX_OVERSHOOTING:
824 priv->vmax_overshooting = g_value_get_double (value);
826 case PROP_VELOCITY_FAST_FACTOR:
827 priv->vfast_factor = g_value_get_double (value);
829 case PROP_DECELERATION:
830 priv->decel = g_value_get_double (value);
831 hildon_pannable_calculate_vel_factor (HILDON_PANNABLE_AREA (object));
833 case PROP_DRAG_INERTIA:
834 priv->drag_inertia = g_value_get_double (value);
837 priv->sps = g_value_get_uint (value);
839 case PROP_PANNING_THRESHOLD:
841 GtkSettings *settings = gtk_settings_get_default ();
842 GtkSettingsValue svalue = { NULL, { 0, }, };
844 priv->panning_threshold = g_value_get_uint (value);
846 /* insure gtk dnd is the same we are using, not allowed
847 different thresholds in the same application */
848 svalue.origin = "panning_threshold";
849 g_value_init (&svalue.value, G_TYPE_LONG);
850 g_value_set_long (&svalue.value, priv->panning_threshold);
851 gtk_settings_set_property_value (settings, "gtk-dnd-drag-threshold", &svalue);
852 g_value_unset (&svalue.value);
855 case PROP_SCROLLBAR_FADE_DELAY:
856 /* convert to miliseconds */
857 priv->scrollbar_fade_delay = g_value_get_uint (value)/(SCROLL_FADE_TIMEOUT);
859 case PROP_BOUNCE_STEPS:
860 priv->bounce_steps = g_value_get_uint (value);
863 priv->force = g_value_get_uint (value);
865 case PROP_DIRECTION_ERROR_MARGIN:
866 priv->direction_error_margin = g_value_get_uint (value);
868 case PROP_VSCROLLBAR_POLICY:
869 priv->vscrollbar_policy = g_value_get_enum (value);
871 gtk_widget_queue_resize (GTK_WIDGET (object));
873 case PROP_HSCROLLBAR_POLICY:
874 priv->hscrollbar_policy = g_value_get_enum (value);
876 gtk_widget_queue_resize (GTK_WIDGET (object));
878 case PROP_VOVERSHOOT_MAX:
879 priv->vovershoot_max = g_value_get_int (value);
881 case PROP_HOVERSHOOT_MAX:
882 priv->hovershoot_max = g_value_get_int (value);
884 case PROP_SCROLL_TIME:
885 priv->scroll_time = g_value_get_double (value);
887 hildon_pannable_calculate_vel_factor (HILDON_PANNABLE_AREA (object));
889 case PROP_INITIAL_HINT:
890 priv->initial_hint = g_value_get_boolean (value);
892 case PROP_LOW_FRICTION_MODE:
893 priv->low_friction_mode = g_value_get_boolean (value);
895 case PROP_SIZE_REQUEST_POLICY:
896 hildon_pannable_area_set_size_request_policy (HILDON_PANNABLE_AREA (object),
897 g_value_get_enum (value));
901 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
906 hildon_pannable_area_dispose (GObject * object)
908 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (object)->priv;
909 GtkWidget *child = gtk_bin_get_child (GTK_BIN (object));
912 g_source_remove (priv->idle_id);
916 if (priv->scroll_indicator_timeout){
917 g_source_remove (priv->scroll_indicator_timeout);
918 priv->scroll_indicator_timeout = 0;
921 if (priv->motion_event_scroll_timeout){
922 g_source_remove (priv->motion_event_scroll_timeout);
923 priv->motion_event_scroll_timeout = 0;
927 g_signal_handlers_disconnect_by_func (child,
928 hildon_pannable_area_child_mapped,
932 g_signal_handlers_disconnect_by_func (object,
933 hildon_pannable_area_grab_notify,
937 g_signal_handlers_disconnect_by_func (priv->hadjust,
938 hildon_pannable_area_adjust_value_changed,
940 g_signal_handlers_disconnect_by_func (priv->hadjust,
941 hildon_pannable_area_adjust_changed,
943 g_object_unref (priv->hadjust);
944 priv->hadjust = NULL;
948 g_signal_handlers_disconnect_by_func (priv->vadjust,
949 hildon_pannable_area_adjust_value_changed,
951 g_signal_handlers_disconnect_by_func (priv->vadjust,
952 hildon_pannable_area_adjust_changed,
954 g_object_unref (priv->vadjust);
955 priv->vadjust = NULL;
958 if (G_OBJECT_CLASS (hildon_pannable_area_parent_class)->dispose)
959 G_OBJECT_CLASS (hildon_pannable_area_parent_class)->dispose (object);
963 hildon_pannable_area_realize (GtkWidget * widget)
965 GdkWindowAttr attributes;
966 gint attributes_mask;
968 HildonPannableAreaPrivate *priv;
970 priv = HILDON_PANNABLE_AREA (widget)->priv;
972 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
974 border_width = GTK_CONTAINER (widget)->border_width;
976 attributes.x = widget->allocation.x + border_width;
977 attributes.y = widget->allocation.y + border_width;
978 attributes.width = MAX (widget->allocation.width - 2 * border_width, 0);
979 attributes.height = MAX (widget->allocation.height - 2 * border_width, 0);
980 attributes.window_type = GDK_WINDOW_CHILD;
982 /* avoid using the hildon_window */
983 attributes.visual = gtk_widget_get_visual (widget);
984 attributes.colormap = gtk_widget_get_colormap (widget);
985 attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK;
986 attributes.wclass = GDK_INPUT_OUTPUT;
988 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
990 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
991 &attributes, attributes_mask);
992 gdk_window_set_user_data (widget->window, widget);
994 /* create the events window */
997 attributes.event_mask = gtk_widget_get_events (widget)
998 | GDK_BUTTON_MOTION_MASK
999 | GDK_BUTTON_PRESS_MASK
1000 | GDK_BUTTON_RELEASE_MASK
1002 | GDK_EXPOSURE_MASK | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK;
1003 attributes.wclass = GDK_INPUT_ONLY;
1005 attributes_mask = GDK_WA_X | GDK_WA_Y;
1007 priv->event_window = gdk_window_new (widget->window,
1008 &attributes, attributes_mask);
1009 gdk_window_set_user_data (priv->event_window, widget);
1011 widget->style = gtk_style_attach (widget->style, widget->window);
1012 gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
1014 priv->scrollbars_gc = gdk_gc_new (GDK_DRAWABLE (widget->window));
1015 gdk_gc_copy (priv->scrollbars_gc, widget->style->fg_gc[GTK_STATE_INSENSITIVE]);
1019 hildon_pannable_area_unrealize (GtkWidget * widget)
1021 HildonPannableAreaPrivate *priv;
1023 priv = HILDON_PANNABLE_AREA (widget)->priv;
1025 if (priv->event_window != NULL) {
1026 gdk_window_set_user_data (priv->event_window, NULL);
1027 gdk_window_destroy (priv->event_window);
1028 priv->event_window = NULL;
1031 gdk_gc_unref (priv->scrollbars_gc);
1033 if (GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->unrealize)
1034 (*GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->unrealize)(widget);
1038 hildon_pannable_area_size_request (GtkWidget * widget,
1039 GtkRequisition * requisition)
1041 GtkRequisition child_requisition = {0};
1042 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1043 GtkWidget *child = gtk_bin_get_child (GTK_BIN (widget));
1045 if (child && GTK_WIDGET_VISIBLE (child))
1047 gtk_widget_size_request (child, &child_requisition);
1050 if (priv->hscrollbar_policy == GTK_POLICY_NEVER) {
1051 requisition->width = child_requisition.width;
1053 switch (priv->size_request_policy) {
1054 case HILDON_SIZE_REQUEST_CHILDREN:
1055 requisition->width = MIN (PANNABLE_MAX_WIDTH,
1056 child_requisition.width);
1058 case HILDON_SIZE_REQUEST_MINIMUM:
1060 requisition->width = priv->indicator_width;
1064 if (priv->vscrollbar_policy == GTK_POLICY_NEVER) {
1065 requisition->height = child_requisition.height;
1067 switch (priv->size_request_policy) {
1068 case HILDON_SIZE_REQUEST_CHILDREN:
1069 requisition->height = MIN (PANNABLE_MAX_HEIGHT,
1070 child_requisition.height);
1072 case HILDON_SIZE_REQUEST_MINIMUM:
1074 requisition->height = priv->indicator_width;
1078 requisition->width += 2 * GTK_CONTAINER (widget)->border_width;
1079 requisition->height += 2 * GTK_CONTAINER (widget)->border_width;
1083 hildon_pannable_area_child_allocate_calculate (GtkWidget * widget,
1084 GtkAllocation * allocation,
1085 GtkAllocation * child_allocation)
1088 HildonPannableAreaPrivate *priv;
1090 border_width = GTK_CONTAINER (widget)->border_width;
1092 priv = HILDON_PANNABLE_AREA (widget)->priv;
1094 child_allocation->x = 0;
1095 child_allocation->y = 0;
1096 child_allocation->width = MAX (allocation->width - 2 * border_width -
1097 (priv->vscroll_visible ? priv->vscroll_rect.width : 0), 0);
1098 child_allocation->height = MAX (allocation->height - 2 * border_width -
1099 (priv->hscroll_visible ? priv->hscroll_rect.height : 0), 0);
1101 if (priv->overshot_dist_y > 0) {
1102 child_allocation->y = MIN (child_allocation->y + priv->overshot_dist_y,
1103 child_allocation->height);
1104 child_allocation->height = MAX (child_allocation->height - priv->overshot_dist_y, 0);
1105 } else if (priv->overshot_dist_y < 0) {
1106 child_allocation->height = MAX (child_allocation->height + priv->overshot_dist_y, 0);
1109 if (priv->overshot_dist_x > 0) {
1110 child_allocation->x = MIN (child_allocation->x + priv->overshot_dist_x,
1111 child_allocation->width);
1112 child_allocation->width = MAX (child_allocation->width - priv->overshot_dist_x, 0);
1113 } else if (priv->overshot_dist_x < 0) {
1114 child_allocation->width = MAX (child_allocation->width + priv->overshot_dist_x, 0);
1119 hildon_pannable_area_size_allocate (GtkWidget * widget,
1120 GtkAllocation * allocation)
1122 GtkAllocation child_allocation;
1123 HildonPannableAreaPrivate *priv;
1124 GtkWidget *child = gtk_bin_get_child (GTK_BIN (widget));
1128 border_width = GTK_CONTAINER (widget)->border_width;
1130 widget->allocation = *allocation;
1132 priv = HILDON_PANNABLE_AREA (widget)->priv;
1134 if (GTK_WIDGET_REALIZED (widget)) {
1135 gdk_window_move_resize (widget->window,
1136 allocation->x + border_width,
1137 allocation->y + border_width,
1138 allocation->width - border_width * 2,
1139 allocation->height - border_width * 2);
1140 gdk_window_move_resize (priv->event_window,
1143 allocation->width - border_width * 2,
1144 allocation->height - border_width * 2);
1147 if (child && GTK_WIDGET_VISIBLE (child)) {
1149 hildon_pannable_area_child_allocate_calculate (widget,
1153 gtk_widget_size_allocate (child, &child_allocation);
1155 if (hildon_pannable_area_check_scrollbars (HILDON_PANNABLE_AREA (widget))) {
1156 hildon_pannable_area_child_allocate_calculate (widget,
1160 gtk_widget_size_allocate (child, &child_allocation);
1163 hv = priv->hadjust->value;
1164 vv = priv->vadjust->value;
1166 /* we have to do this after child size_allocate because page_size is
1167 * changed when we allocate the size of the children */
1168 if (priv->overshot_dist_y < 0) {
1169 priv->vadjust->value = priv->vadjust->upper - priv->vadjust->page_size;
1172 if (priv->overshot_dist_x < 0) {
1173 priv->hadjust->value = priv->hadjust->upper - priv->hadjust->page_size;
1176 if (hv != priv->hadjust->value)
1177 gtk_adjustment_value_changed (priv->hadjust);
1179 if (vv != priv->vadjust->value)
1180 gtk_adjustment_value_changed (priv->vadjust);
1183 hildon_pannable_area_check_scrollbars (HILDON_PANNABLE_AREA (widget));
1188 hildon_pannable_area_style_set (GtkWidget * widget,
1189 GtkStyle * previous_style)
1191 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1193 GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->
1194 style_set (widget, previous_style);
1196 gtk_widget_style_get (widget, "indicator-width", &priv->indicator_width, NULL);
1200 hildon_pannable_area_map (GtkWidget * widget)
1202 HildonPannableAreaPrivate *priv;
1204 priv = HILDON_PANNABLE_AREA (widget)->priv;
1206 gdk_window_show (widget->window);
1208 if (priv->event_window != NULL && !priv->enabled)
1209 gdk_window_show (priv->event_window);
1211 (*GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->map) (widget);
1213 if (priv->event_window != NULL && priv->enabled)
1214 gdk_window_show (priv->event_window);
1218 hildon_pannable_area_unmap (GtkWidget * widget)
1220 HildonPannableAreaPrivate *priv;
1222 priv = HILDON_PANNABLE_AREA (widget)->priv;
1224 if (priv->event_window != NULL)
1225 gdk_window_hide (priv->event_window);
1227 gdk_window_hide (widget->window);
1229 (*GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->unmap) (widget);
1233 hildon_pannable_area_grab_notify (GtkWidget *widget,
1234 gboolean was_grabbed,
1237 /* an internal widget has grabbed the focus and now has returned it,
1238 we have to do some release actions */
1240 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1242 priv->scroll_indicator_event_interrupt = 0;
1244 if ((!priv->scroll_indicator_timeout)&&(priv->scroll_indicator_alpha)>0.1) {
1245 priv->scroll_delay_counter = priv->scrollbar_fade_delay;
1247 hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget),
1248 priv->scroll_indicator_alpha);
1251 priv->last_type = 3;
1252 priv->moved = FALSE;
1256 #if USE_CAIRO_SCROLLBARS == 1
1259 rgb_from_gdkcolor (GdkColor *color, gdouble *r, gdouble *g, gdouble *b)
1261 *r = (color->red >> 8) / 255.0;
1262 *g = (color->green >> 8) / 255.0;
1263 *b = (color->blue >> 8) / 255.0;
1267 hildon_pannable_draw_vscroll (GtkWidget * widget,
1268 GdkColor *back_color,
1269 GdkColor *scroll_color)
1271 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1274 cairo_pattern_t *pattern;
1276 gint radius = (priv->vscroll_rect.width/2) - 1;
1278 cr = gdk_cairo_create(widget->window);
1280 /* Draw the background */
1281 rgb_from_gdkcolor (back_color, &r, &g, &b);
1282 cairo_set_source_rgb (cr, r, g, b);
1283 cairo_rectangle(cr, priv->vscroll_rect.x, priv->vscroll_rect.y,
1284 priv->vscroll_rect.width,
1285 priv->vscroll_rect.height);
1286 cairo_fill_preserve (cr);
1289 /* Calculate the scroll bar height and position */
1290 y = ((priv->vadjust->value - priv->vadjust->lower) / (priv->vadjust->upper - priv->vadjust->lower)) *
1291 (widget->allocation.height -
1292 (priv->hscroll_visible ? priv->indicator_width : 0));
1293 height = ((((priv->vadjust->value - priv->vadjust->lower) +
1294 priv->vadjust->page_size) /
1295 (priv->vadjust->upper - priv->vadjust->lower)) *
1296 (widget->allocation.height -
1297 (priv->hscroll_visible ? priv->indicator_width : 0))) - y;
1299 /* Set a minimum height */
1300 height = MAX (SCROLL_BAR_MIN_SIZE, height);
1302 /* Check the max y position */
1303 y = MIN (y, widget->allocation.height -
1304 (priv->hscroll_visible ? priv->hscroll_rect.height : 0) -
1307 /* Draw the scrollbar */
1308 rgb_from_gdkcolor (scroll_color, &r, &g, &b);
1310 pattern = cairo_pattern_create_linear(radius+1, y, radius+1,y + height);
1311 cairo_pattern_add_color_stop_rgb(pattern, 0, r, g, b);
1312 cairo_pattern_add_color_stop_rgb(pattern, 1, r/2, g/2, b/2);
1313 cairo_set_source(cr, pattern);
1315 cairo_pattern_destroy(pattern);
1317 cairo_arc(cr, priv->vscroll_rect.x + radius + 1, y + radius + 1, radius, G_PI, 0);
1318 cairo_line_to(cr, priv->vscroll_rect.x + (radius * 2) + 1, y + height - radius);
1319 cairo_arc(cr, priv->vscroll_rect.x + radius + 1, y + height - radius, radius, 0, G_PI);
1320 cairo_line_to(cr, priv->vscroll_rect.x + 1, y + height - radius);
1323 cairo_paint_with_alpha(cr, priv->scroll_indicator_alpha);
1329 hildon_pannable_draw_hscroll (GtkWidget * widget,
1330 GdkColor *back_color,
1331 GdkColor *scroll_color)
1333 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1336 cairo_pattern_t *pattern;
1338 gint radius = (priv->hscroll_rect.height/2) - 1;
1340 cr = gdk_cairo_create(widget->window);
1342 /* Draw the background */
1343 rgb_from_gdkcolor (back_color, &r, &g, &b);
1344 cairo_set_source_rgb (cr, r, g, b);
1345 cairo_rectangle(cr, priv->hscroll_rect.x, priv->hscroll_rect.y,
1346 priv->hscroll_rect.width,
1347 priv->hscroll_rect.height);
1348 cairo_fill_preserve (cr);
1351 /* calculate the scrollbar width and position */
1352 x = ((priv->hadjust->value - priv->hadjust->lower) / (priv->hadjust->upper - priv->hadjust->lower)) *
1353 (widget->allocation.width - (priv->vscroll_visible ? priv->indicator_width : 0));
1354 width =((((priv->hadjust->value - priv->hadjust->lower) +
1355 priv->hadjust->page_size) / (priv->hadjust->upper - priv->hadjust->lower)) *
1356 (widget->allocation.width -
1357 (priv->vscroll_visible ? priv->indicator_width : 0))) - x;
1359 /* Set a minimum width */
1360 width = MAX (SCROLL_BAR_MIN_SIZE, width);
1362 /* Check the max x position */
1363 x = MIN (x, widget->allocation.width -
1364 (priv->vscroll_visible ? priv->vscroll_rect.width : 0) -
1367 /* Draw the scrollbar */
1368 rgb_from_gdkcolor (scroll_color, &r, &g, &b);
1370 pattern = cairo_pattern_create_linear(x, radius+1, x+width, radius+1);
1371 cairo_pattern_add_color_stop_rgb(pattern, 0, r, g, b);
1372 cairo_pattern_add_color_stop_rgb(pattern, 1, r/2, g/2, b/2);
1373 cairo_set_source(cr, pattern);
1375 cairo_pattern_destroy(pattern);
1377 cairo_arc_negative(cr, x + radius + 1, priv->hscroll_rect.y + radius + 1, radius, 3*G_PI_2, G_PI_2);
1378 cairo_line_to(cr, x + width - radius, priv->hscroll_rect.y + (radius * 2) + 1);
1379 cairo_arc_negative(cr, x + width - radius, priv->hscroll_rect.y + radius + 1, radius, G_PI_2, 3*G_PI_2);
1380 cairo_line_to(cr, x + width - radius, priv->hscroll_rect.y + 1);
1383 cairo_paint_with_alpha(cr, priv->scroll_indicator_alpha);
1388 #else /* USE_CAIRO_SCROLLBARS */
1391 tranparency_color (GdkColor *color,
1394 gdouble transparency)
1398 diff = colora.red - colorb.red;
1399 color->red = colora.red-diff*transparency;
1401 diff = colora.green - colorb.green;
1402 color->green = colora.green-diff*transparency;
1404 diff = colora.blue - colorb.blue;
1405 color->blue = colora.blue-diff*transparency;
1409 hildon_pannable_draw_vscroll (GtkWidget *widget,
1410 GdkColor *back_color,
1411 GdkColor *scroll_color)
1413 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1415 GdkColor transp_color;
1416 GdkGC *gc = widget->style->fg_gc[GTK_STATE_INSENSITIVE];
1418 gdk_draw_rectangle (widget->window,
1419 widget->style->bg_gc[GTK_STATE_NORMAL],
1421 priv->vscroll_rect.x, priv->vscroll_rect.y,
1422 priv->vscroll_rect.width,
1423 priv->vscroll_rect.height);
1425 y = ((priv->vadjust->value - priv->vadjust->lower) / (priv->vadjust->upper - priv->vadjust->lower)) *
1426 (widget->allocation.height - (priv->hscroll_visible ? priv->indicator_width : 0));
1427 height = ((((priv->vadjust->value - priv->vadjust->lower) + priv->vadjust->page_size) /
1428 (priv->vadjust->upper - priv->vadjust->lower)) *
1429 (widget->allocation.height -
1430 (priv->hscroll_visible ? priv->indicator_width : 0))) - y;
1432 /* Set a minimum height */
1433 height = MAX (SCROLL_BAR_MIN_SIZE, height);
1435 /* Check the max y position */
1436 y = MIN (y, widget->allocation.height -
1437 (priv->hscroll_visible ? priv->hscroll_rect.height : 0) -
1440 if (priv->scroll_indicator_alpha < 1.0) {
1441 tranparency_color (&transp_color, *back_color, *scroll_color,
1442 priv->scroll_indicator_alpha);
1444 gdk_gc_set_rgb_fg_color (priv->scrollbars_gc, &transp_color);
1446 gc = priv->scrollbars_gc;
1449 gdk_draw_rectangle (widget->window, gc,
1450 TRUE, priv->vscroll_rect.x, y,
1451 priv->vscroll_rect.width, height);
1455 hildon_pannable_draw_hscroll (GtkWidget *widget,
1456 GdkColor *back_color,
1457 GdkColor *scroll_color)
1459 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1461 GdkColor transp_color;
1462 GdkGC *gc = widget->style->fg_gc[GTK_STATE_INSENSITIVE];
1464 gdk_draw_rectangle (widget->window,
1465 widget->style->bg_gc[GTK_STATE_INSENSITIVE],
1467 priv->hscroll_rect.x, priv->hscroll_rect.y,
1468 priv->hscroll_rect.width,
1469 priv->hscroll_rect.height);
1471 /* calculate the scrollbar width and position */
1472 x = ((priv->hadjust->value - priv->hadjust->lower) / (priv->hadjust->upper - priv->hadjust->lower)) *
1473 (widget->allocation.width - (priv->vscroll_visible ? priv->indicator_width : 0));
1474 width =((((priv->hadjust->value - priv->hadjust->lower) +
1475 priv->hadjust->page_size) / (priv->hadjust->upper - priv->hadjust->lower)) *
1476 (widget->allocation.width -
1477 (priv->vscroll_visible ? priv->indicator_width : 0))) - x;
1479 /* Set a minimum width */
1480 width = MAX (SCROLL_BAR_MIN_SIZE, width);
1482 /* Check the max x position */
1483 x = MIN (x, widget->allocation.width -
1484 (priv->vscroll_visible ? priv->vscroll_rect.width : 0) -
1487 if (priv->scroll_indicator_alpha < 1.0) {
1488 tranparency_color (&transp_color, *back_color, *scroll_color,
1489 priv->scroll_indicator_alpha);
1491 gdk_gc_set_rgb_fg_color (priv->scrollbars_gc, &transp_color);
1493 gc = priv->scrollbars_gc;
1496 gdk_draw_rectangle (widget->window, gc,
1497 TRUE, x, priv->hscroll_rect.y, width,
1498 priv->hscroll_rect.height);
1501 #endif /* USE_CAIRO_SCROLLBARS */
1504 hildon_pannable_area_initial_effect (GtkWidget * widget)
1506 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1508 if (priv->initial_hint) {
1509 if (priv->vscroll_visible || priv->hscroll_visible) {
1511 priv->scroll_indicator_event_interrupt = 0;
1512 priv->scroll_delay_counter = priv->scrollbar_fade_delay;
1514 hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget), 1.0);
1520 hildon_pannable_area_launch_fade_timeout (HildonPannableArea * area,
1523 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (area)->priv;
1525 priv->scroll_indicator_alpha = alpha;
1527 if (!priv->scroll_indicator_timeout)
1528 priv->scroll_indicator_timeout =
1529 gdk_threads_add_timeout (SCROLL_FADE_TIMEOUT,
1530 (GSourceFunc) hildon_pannable_area_scroll_indicator_fade,
1535 hildon_pannable_area_adjust_changed (HildonPannableArea * area,
1538 if (GTK_WIDGET_REALIZED (area))
1539 hildon_pannable_area_refresh (area);
1543 hildon_pannable_area_adjust_value_changed (HildonPannableArea * area,
1546 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (area)->priv;
1548 gint x = priv->x_offset;
1549 gint y = priv->y_offset;
1551 priv->x_offset = priv->hadjust->value;
1552 xdiff = x - priv->x_offset;
1553 priv->y_offset = priv->vadjust->value;
1554 ydiff = y - priv->y_offset;
1556 if ((xdiff || ydiff) && GTK_WIDGET_DRAWABLE (area)) {
1557 hildon_pannable_area_redraw (area);
1559 if ((priv->vscroll_visible) || (priv->hscroll_visible)) {
1560 priv->scroll_indicator_event_interrupt = 0;
1561 priv->scroll_delay_counter = priv->scrollbar_fade_delay;
1563 hildon_pannable_area_launch_fade_timeout (area, 1.0);
1569 hildon_pannable_area_redraw (HildonPannableArea * area)
1571 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (area)->priv;
1573 /* Redraw scroll indicators */
1574 if (GTK_WIDGET_DRAWABLE (area)) {
1575 if (priv->hscroll_visible) {
1576 gdk_window_invalidate_rect (GTK_WIDGET (area)->window,
1577 &priv->hscroll_rect, FALSE);
1580 if (priv->vscroll_visible) {
1581 gdk_window_invalidate_rect (GTK_WIDGET (area)->window,
1582 &priv->vscroll_rect, FALSE);
1588 hildon_pannable_area_scroll_indicator_fade(HildonPannableArea * area)
1590 HildonPannableAreaPrivate *priv = area->priv;
1592 /* if moving do not fade out */
1593 if (((ABS (priv->vel_y)>priv->vmin)||
1594 (ABS (priv->vel_x)>priv->vmin))&&(!priv->button_pressed)) {
1599 if (priv->scroll_indicator_event_interrupt) {
1600 /* Stop a fade out, and fade back in */
1601 if (priv->scroll_indicator_alpha > 0.9) {
1602 priv->scroll_indicator_alpha = 1.0;
1603 priv->scroll_indicator_timeout = 0;
1607 priv->scroll_indicator_alpha += 0.2;
1608 hildon_pannable_area_redraw (area);
1614 if ((priv->scroll_indicator_alpha > 0.9) &&
1615 (priv->scroll_delay_counter > 0)) {
1616 priv->scroll_delay_counter--;
1621 if (!priv->scroll_indicator_event_interrupt) {
1622 /* Continue fade out */
1623 if (priv->scroll_indicator_alpha < 0.1) {
1624 priv->scroll_indicator_timeout = 0;
1625 priv->scroll_indicator_alpha = 0.0;
1629 priv->scroll_indicator_alpha -= 0.2;
1630 hildon_pannable_area_redraw (area);
1640 hildon_pannable_area_expose_event (GtkWidget * widget,
1641 GdkEventExpose * event)
1644 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1645 #if USE_CAIRO_SCROLLBARS == 1
1646 GdkColor back_color = widget->style->bg[GTK_STATE_NORMAL];
1647 GdkColor scroll_color = widget->style->base[GTK_STATE_SELECTED];
1648 #else /* USE_CAIRO_SCROLLBARS */
1649 GdkColor back_color = widget->style->bg[GTK_STATE_NORMAL];
1650 GdkColor scroll_color = widget->style->fg[GTK_STATE_INSENSITIVE];
1653 if (G_UNLIKELY (priv->initial_effect)) {
1654 hildon_pannable_area_initial_effect (widget);
1656 priv->initial_effect = FALSE;
1659 if (gtk_bin_get_child (GTK_BIN (widget))) {
1661 if (priv->scroll_indicator_alpha > 0.1) {
1662 if (priv->vscroll_visible) {
1663 hildon_pannable_draw_vscroll (widget, &back_color, &scroll_color);
1665 if (priv->hscroll_visible) {
1666 hildon_pannable_draw_hscroll (widget, &back_color, &scroll_color);
1670 /* draw overshooting rectangles */
1671 if (priv->overshot_dist_y > 0) {
1672 gint overshot_height;
1674 overshot_height = MIN (priv->overshot_dist_y, widget->allocation.height -
1675 (priv->hscroll_visible ? priv->hscroll_rect.height : 0));
1677 gdk_draw_rectangle (widget->window,
1678 widget->style->bg_gc[GTK_STATE_NORMAL],
1682 widget->allocation.width -
1683 (priv->vscroll_visible ? priv->vscroll_rect.width : 0),
1685 } else if (priv->overshot_dist_y < 0) {
1686 gint overshot_height;
1690 MAX (priv->overshot_dist_y,
1691 -(widget->allocation.height -
1692 (priv->hscroll_visible ? priv->hscroll_rect.height : 0)));
1694 overshot_y = MAX (widget->allocation.height +
1696 (priv->hscroll_visible ? priv->hscroll_rect.height : 0), 0);
1698 gdk_draw_rectangle (widget->window,
1699 widget->style->bg_gc[GTK_STATE_NORMAL],
1703 widget->allocation.width -
1704 priv->vscroll_rect.width,
1708 if (priv->overshot_dist_x > 0) {
1709 gint overshot_width;
1711 overshot_width = MIN (priv->overshot_dist_x, widget->allocation.width -
1712 (priv->vscroll_visible ? priv->vscroll_rect.width : 0));
1714 gdk_draw_rectangle (widget->window,
1715 widget->style->bg_gc[GTK_STATE_NORMAL],
1720 widget->allocation.height -
1721 (priv->hscroll_visible ? priv->hscroll_rect.height : 0));
1722 } else if (priv->overshot_dist_x < 0) {
1723 gint overshot_width;
1727 MAX (priv->overshot_dist_x,
1728 -(widget->allocation.width -
1729 (priv->vscroll_visible ? priv->vscroll_rect.width : 0)));
1731 overshot_x = MAX (widget->allocation.width +
1733 (priv->vscroll_visible ? priv->vscroll_rect.width : 0), 0);
1735 gdk_draw_rectangle (widget->window,
1736 widget->style->bg_gc[GTK_STATE_NORMAL],
1741 widget->allocation.height -
1742 priv->hscroll_rect.height);
1747 return GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->expose_event (widget, event);
1751 hildon_pannable_area_get_topmost (GdkWindow * window,
1753 gint * tx, gint * ty,
1756 /* Find the GdkWindow at the given point, by recursing from a given
1757 * parent GdkWindow. Optionally return the co-ordinates transformed
1758 * relative to the child window.
1761 GList *c, *children;
1762 GdkWindow *selected_window = NULL;
1764 gdk_drawable_get_size (GDK_DRAWABLE (window), &width, &height);
1765 if ((x < 0) || (x >= width) || (y < 0) || (y >= height))
1768 children = gdk_window_peek_children (window);
1775 selected_window = window;
1778 for (c = children; c; c = c->next) {
1779 GdkWindow *child = (GdkWindow *) c->data;
1782 gdk_drawable_get_size (GDK_DRAWABLE (child), &width, &height);
1783 gdk_window_get_position (child, &wx, &wy);
1785 if ((x >= wx) && (x < (wx + width)) && (y >= wy) && (y < (wy + height)) &&
1786 (gdk_window_is_visible (child))) {
1788 if (gdk_window_peek_children (child)) {
1789 selected_window = hildon_pannable_area_get_topmost (child, x-wx, y-wy,
1791 if (!selected_window) {
1796 selected_window = child;
1799 if ((gdk_window_get_events (child)&mask)) {
1804 selected_window = child;
1810 return selected_window;
1814 synth_crossing (GdkWindow * child,
1816 gint x_root, gint y_root,
1817 guint32 time, gboolean in)
1819 GdkEventCrossing *crossing_event;
1820 GdkEventType type = in ? GDK_ENTER_NOTIFY : GDK_LEAVE_NOTIFY;
1822 /* Send synthetic enter event */
1823 crossing_event = (GdkEventCrossing *) gdk_event_new (type);
1824 ((GdkEventAny *) crossing_event)->type = type;
1825 ((GdkEventAny *) crossing_event)->window = g_object_ref (child);
1826 ((GdkEventAny *) crossing_event)->send_event = FALSE;
1827 crossing_event->subwindow = g_object_ref (child);
1828 crossing_event->time = time;
1829 crossing_event->x = x;
1830 crossing_event->y = y;
1831 crossing_event->x_root = x_root;
1832 crossing_event->y_root = y_root;
1833 crossing_event->mode = GDK_CROSSING_NORMAL;
1834 crossing_event->detail = GDK_NOTIFY_UNKNOWN;
1835 crossing_event->focus = FALSE;
1836 crossing_event->state = 0;
1837 gdk_event_put ((GdkEvent *) crossing_event);
1838 gdk_event_free ((GdkEvent *) crossing_event);
1842 hildon_pannable_area_button_press_cb (GtkWidget * widget,
1843 GdkEventButton * event)
1846 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1848 if ((!priv->enabled) || (event->button != 1) ||
1849 ((event->time == priv->last_time) &&
1850 (priv->last_type == 1)) || (gtk_bin_get_child (GTK_BIN (widget)) == NULL))
1853 priv->scroll_indicator_event_interrupt = 1;
1855 hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget),
1856 priv->scroll_indicator_alpha);
1858 priv->last_time = event->time;
1859 priv->last_type = 1;
1861 priv->scroll_to_x = -1;
1862 priv->scroll_to_y = -1;
1864 if (priv->button_pressed && priv->child) {
1865 /* Widget stole focus on last click, send crossing-out event */
1866 synth_crossing (priv->child, 0, 0, event->x_root, event->y_root,
1867 event->time, FALSE);
1875 /* Don't allow a click if we're still moving fast */
1876 if ((ABS (priv->vel_x) <= (priv->vmax * priv->vfast_factor)) &&
1877 (ABS (priv->vel_y) <= (priv->vmax * priv->vfast_factor)))
1879 hildon_pannable_area_get_topmost (gtk_bin_get_child (GTK_BIN (widget))->window,
1880 event->x, event->y, &x, &y, GDK_BUTTON_PRESS_MASK);
1884 priv->button_pressed = TRUE;
1886 /* Stop scrolling on mouse-down (so you can flick, then hold to stop) */
1892 gdk_drawable_get_size (priv->child, &priv->child_width,
1893 &priv->child_height);
1894 priv->last_in = TRUE;
1896 g_object_add_weak_pointer ((GObject *) priv->child,
1897 (gpointer) & priv->child);
1899 event = (GdkEventButton *) gdk_event_copy ((GdkEvent *) event);
1905 synth_crossing (priv->child, x, y, event->x_root,
1906 event->y_root, event->time, TRUE);
1908 /* Send synthetic click (button press/release) event */
1909 ((GdkEventAny *) event)->window = g_object_ref (priv->child);
1911 gdk_event_put ((GdkEvent *) event);
1912 gdk_event_free ((GdkEvent *) event);
1920 hildon_pannable_area_check_scrollbars (HildonPannableArea * area)
1922 HildonPannableAreaPrivate *priv = area->priv;
1923 gboolean prev_hscroll_visible, prev_vscroll_visible;
1925 prev_hscroll_visible = priv->hscroll_visible;
1926 prev_vscroll_visible = priv->vscroll_visible;
1928 if (!gtk_bin_get_child (GTK_BIN (area))) {
1929 priv->vscroll_visible = FALSE;
1930 priv->hscroll_visible = FALSE;
1932 switch (priv->hscrollbar_policy) {
1933 case GTK_POLICY_ALWAYS:
1934 priv->hscroll_visible = TRUE;
1936 case GTK_POLICY_NEVER:
1937 priv->hscroll_visible = FALSE;
1940 priv->hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
1941 priv->hadjust->page_size);
1944 switch (priv->vscrollbar_policy) {
1945 case GTK_POLICY_ALWAYS:
1946 priv->vscroll_visible = TRUE;
1948 case GTK_POLICY_NEVER:
1949 priv->vscroll_visible = FALSE;
1952 priv->vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
1953 priv->vadjust->page_size);
1956 /* Store the vscroll/hscroll areas for redrawing */
1957 if (priv->vscroll_visible) {
1958 GtkAllocation *allocation = >K_WIDGET (area)->allocation;
1959 priv->vscroll_rect.x = allocation->width - priv->indicator_width;
1960 priv->vscroll_rect.y = 0;
1961 priv->vscroll_rect.width = priv->indicator_width;
1962 priv->vscroll_rect.height = allocation->height -
1963 (priv->hscroll_visible ? priv->indicator_width : 0);
1965 if (priv->hscroll_visible) {
1966 GtkAllocation *allocation = >K_WIDGET (area)->allocation;
1967 priv->hscroll_rect.y = allocation->height - priv->indicator_width;
1968 priv->hscroll_rect.x = 0;
1969 priv->hscroll_rect.height = priv->indicator_width;
1970 priv->hscroll_rect.width = allocation->width -
1971 (priv->vscroll_visible ? priv->indicator_width : 0);
1975 return ((priv->hscroll_visible != prev_hscroll_visible) ||
1976 (priv->vscroll_visible != prev_vscroll_visible));
1980 hildon_pannable_area_refresh (HildonPannableArea * area)
1982 if (GTK_WIDGET_DRAWABLE (area) &&
1983 hildon_pannable_area_check_scrollbars (area)) {
1984 HildonPannableAreaPrivate *priv = area->priv;
1986 gtk_widget_queue_resize (GTK_WIDGET (area));
1988 if ((priv->vscroll_visible) || (priv->hscroll_visible)) {
1989 priv->scroll_indicator_event_interrupt = 0;
1990 priv->scroll_delay_counter = area->priv->scrollbar_fade_delay;
1992 hildon_pannable_area_launch_fade_timeout (area, 1.0);
1995 hildon_pannable_area_redraw (area);
1999 /* Scroll by a particular amount (in pixels). Optionally, return if
2000 * the scroll on a particular axis was successful.
2003 hildon_pannable_axis_scroll (HildonPannableArea *area,
2004 GtkAdjustment *adjust,
2008 gint *overshot_dist,
2014 HildonPannableAreaPrivate *priv = area->priv;
2016 dist = gtk_adjustment_get_value (adjust) - inc;
2019 * We use overshot_dist to define the distance of the current overshoot,
2020 * and overshooting to define the direction/whether or not we are overshot
2022 if (!(*overshooting)) {
2024 /* Initiation of the overshoot happens when the finger is released
2025 * and the current position of the pannable contents are out of range
2027 if (dist < adjust->lower) {
2030 dist = adjust->lower;
2032 if (overshoot_max!=0) {
2035 *overshot_dist = CLAMP (*overshot_dist + *vel, 0, overshoot_max);
2036 *vel = MIN (priv->vmax_overshooting, *vel);
2037 gtk_widget_queue_resize (GTK_WIDGET (area));
2041 } else if (dist > adjust->upper - adjust->page_size) {
2044 dist = adjust->upper - adjust->page_size;
2046 if (overshoot_max!=0) {
2049 *overshot_dist = CLAMP (*overshot_dist + *vel, -overshoot_max, 0);
2050 *vel = MAX (-priv->vmax_overshooting, *vel);
2051 gtk_widget_queue_resize (GTK_WIDGET (area));
2056 if ((*scroll_to) != -1) {
2057 if (((inc < 0)&&(*scroll_to <= dist))||
2058 ((inc > 0)&&(*scroll_to >= dist))) {
2066 adjust->value = dist;
2068 if (!priv->button_pressed) {
2070 /* When the overshoot has started we continue for
2071 * PROP_BOUNCE_STEPS more steps into the overshoot before we
2072 * reverse direction. The deceleration factor is calculated
2073 * based on the percentage distance from the first item with
2074 * each iteration, therefore always returning us to the
2075 * top/bottom most element
2077 if (*overshot_dist > 0) {
2079 if ((*overshooting < priv->bounce_steps) && (*vel > 0)) {
2081 *vel = (((gdouble)*overshot_dist)/overshoot_max) * (*vel);
2082 } else if ((*overshooting >= priv->bounce_steps) && (*vel > 0)) {
2084 } else if ((*overshooting > 1) && (*vel < 0)) {
2085 /* we add the MIN in order to avoid very small speeds */
2086 *vel = MIN ((((gdouble)*overshot_dist)*0.4) * -1, -2.0);
2089 *overshot_dist = CLAMP (*overshot_dist + *vel, 0, overshoot_max);
2091 gtk_widget_queue_resize (GTK_WIDGET (area));
2093 } else if (*overshot_dist < 0) {
2095 if ((*overshooting < priv->bounce_steps) && (*vel < 0)) {
2097 *vel = (((gdouble)*overshot_dist)/overshoot_max) * (*vel) * -1;
2098 } else if ((*overshooting >= priv->bounce_steps) && (*vel < 0)) {
2100 } else if ((*overshooting > 1) && (*vel > 0)) {
2101 /* we add the MAX in order to avoid very small speeds */
2102 *vel = MAX ((((gdouble)*overshot_dist)*0.4) * -1, 2.0);
2105 *overshot_dist = CLAMP (*overshot_dist + (*vel), -overshoot_max, 0);
2107 gtk_widget_queue_resize (GTK_WIDGET (area));
2112 gtk_widget_queue_resize (GTK_WIDGET (area));
2116 gint overshot_dist_old = *overshot_dist;
2118 if (*overshot_dist > 0) {
2119 *overshot_dist = CLAMP ((*overshot_dist) + inc, 0, overshoot_max);
2120 } else if (*overshot_dist < 0) {
2121 *overshot_dist = CLAMP ((*overshot_dist) + inc, -1 * overshoot_max, 0);
2124 adjust->value = CLAMP (dist,
2130 if (*overshot_dist != overshot_dist_old)
2131 gtk_widget_queue_resize (GTK_WIDGET (area));
2137 hildon_pannable_area_scroll (HildonPannableArea *area,
2138 gdouble x, gdouble y)
2141 HildonPannableAreaPrivate *priv = area->priv;
2142 gboolean hscroll_visible, vscroll_visible;
2145 if (gtk_bin_get_child (GTK_BIN (area)) == NULL)
2148 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
2149 priv->vadjust->page_size);
2150 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
2151 priv->hadjust->page_size);
2156 hv = priv->hadjust->value;
2157 vv = priv->vadjust->value;
2159 if (vscroll_visible) {
2160 hildon_pannable_axis_scroll (area, priv->vadjust, &priv->vel_y, y,
2161 &priv->overshooting_y, &priv->overshot_dist_y,
2162 &priv->scroll_to_y, priv->vovershoot_max, &sy);
2167 if (hscroll_visible) {
2168 hildon_pannable_axis_scroll (area, priv->hadjust, &priv->vel_x, x,
2169 &priv->overshooting_x, &priv->overshot_dist_x,
2170 &priv->scroll_to_x, priv->hovershoot_max, &sx);
2175 if (hv != priv->hadjust->value)
2176 gtk_adjustment_value_changed (priv->hadjust);
2178 if (vv != priv->vadjust->value)
2179 gtk_adjustment_value_changed (priv->vadjust);
2181 /* If the scroll on a particular axis wasn't succesful, reset the
2182 * initial scroll position to the new mouse co-ordinate. This means
2183 * when you get to the top of the page, dragging down works immediately.
2185 if (priv->mode == HILDON_PANNABLE_AREA_MODE_ACCEL) {
2197 hildon_pannable_area_timeout (HildonPannableArea * area)
2199 HildonPannableAreaPrivate *priv = area->priv;
2201 if ((!priv->enabled) || (priv->mode == HILDON_PANNABLE_AREA_MODE_PUSH)) {
2207 if (!priv->button_pressed) {
2208 /* Decelerate gradually when pointer is raised */
2209 if ((!priv->overshot_dist_y) &&
2210 (!priv->overshot_dist_x)) {
2212 /* in case we move to a specific point do not decelerate when arriving */
2213 if ((priv->scroll_to_x != -1)||(priv->scroll_to_y != -1)) {
2215 if (ABS (priv->vel_x) >= 1.5) {
2216 priv->vel_x *= priv->decel;
2219 if (ABS (priv->vel_y) >= 1.5) {
2220 priv->vel_y *= priv->decel;
2224 if ((!priv->low_friction_mode) ||
2225 ((priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ) &&
2226 (ABS (priv->vel_x) < 0.8*priv->vmax)))
2227 priv->vel_x *= priv->decel;
2229 if ((!priv->low_friction_mode) ||
2230 ((priv->mov_mode&HILDON_MOVEMENT_MODE_VERT) &&
2231 (ABS (priv->vel_y) < 0.8*priv->vmax)))
2232 priv->vel_y *= priv->decel;
2234 if ((ABS (priv->vel_x) < 1.0) && (ABS (priv->vel_y) < 1.0)) {
2243 } else if (priv->mode == HILDON_PANNABLE_AREA_MODE_AUTO) {
2249 hildon_pannable_area_scroll (area, priv->vel_x, priv->vel_y);
2255 hildon_pannable_area_calculate_velocity (gdouble *vel,
2259 gdouble drag_inertia,
2265 if (ABS (dist) >= RATIO_TOLERANCE) {
2266 rawvel = (dist / ABS (delta)) * force;
2267 *vel = *vel * (1 - drag_inertia) +
2268 rawvel * drag_inertia;
2269 *vel = *vel > 0 ? MIN (*vel, vmax)
2270 : MAX (*vel, -1 * vmax);
2275 hildon_pannable_area_motion_event_scroll_timeout (HildonPannableArea *area)
2277 HildonPannableAreaPrivate *priv = area->priv;
2279 if ((priv->motion_x != 0)||(priv->motion_y != 0))
2280 hildon_pannable_area_scroll (area, priv->motion_x, priv->motion_y);
2282 priv->motion_event_scroll_timeout = 0;
2288 hildon_pannable_area_motion_event_scroll (HildonPannableArea *area,
2289 gdouble x, gdouble y)
2291 HildonPannableAreaPrivate *priv = area->priv;
2293 if (priv->motion_event_scroll_timeout) {
2295 priv->motion_x += x;
2296 priv->motion_y += y;
2300 /* we do not delay the first event but the next ones */
2301 hildon_pannable_area_scroll (area, x, y);
2306 priv->motion_event_scroll_timeout = gdk_threads_add_timeout
2307 ((gint) (1000.0 / (gdouble) MOTION_EVENTS_PER_SECOND),
2308 (GSourceFunc) hildon_pannable_area_motion_event_scroll_timeout, area);
2313 hildon_pannable_area_check_move (HildonPannableArea *area,
2314 GdkEventMotion * event,
2318 HildonPannableAreaPrivate *priv = area->priv;
2320 if (priv->first_drag && (!priv->moved) &&
2321 ((ABS (*x) > (priv->panning_threshold))
2322 || (ABS (*y) > (priv->panning_threshold)))) {
2327 if (priv->first_drag) {
2328 gboolean vscroll_visible;
2329 gboolean hscroll_visible;
2331 if (ABS (priv->iy - event->y) >=
2332 ABS (priv->ix - event->x)) {
2334 g_signal_emit (area,
2335 pannable_area_signals[VERTICAL_MOVEMENT],
2336 0, (priv->iy > event->y) ?
2337 HILDON_MOVEMENT_UP :
2338 HILDON_MOVEMENT_DOWN,
2339 (gdouble)priv->ix, (gdouble)priv->iy);
2341 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
2342 priv->vadjust->page_size);
2344 if (!((vscroll_visible)&&
2345 (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT))) {
2347 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
2348 priv->hadjust->page_size);
2350 /* even in case we do not have to move we check if this
2351 could be a fake horizontal movement */
2352 if (!((hscroll_visible)&&
2353 (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ)) ||
2354 (ABS (priv->iy - event->y) -
2355 ABS (priv->ix - event->x) >= priv->direction_error_margin))
2356 priv->moved = FALSE;
2360 g_signal_emit (area,
2361 pannable_area_signals[HORIZONTAL_MOVEMENT],
2362 0, (priv->ix > event->x) ?
2363 HILDON_MOVEMENT_LEFT :
2364 HILDON_MOVEMENT_RIGHT,
2365 (gdouble)priv->ix, (gdouble)priv->iy);
2367 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
2368 priv->hadjust->page_size);
2370 if (!((hscroll_visible)&&
2371 (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ))) {
2373 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
2374 priv->vadjust->page_size);
2376 /* even in case we do not have to move we check if this
2377 could be a fake vertical movement */
2378 if (!((vscroll_visible) &&
2379 (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT)) ||
2380 (ABS (priv->ix - event->x) -
2381 ABS (priv->iy - event->y) >= priv->direction_error_margin))
2382 priv->moved = FALSE;
2386 if ((priv->moved)&&(priv->child)) {
2389 pos_x = priv->cx + (event->x - priv->ix);
2390 pos_y = priv->cy + (event->y - priv->iy);
2392 synth_crossing (priv->child, pos_x, pos_y, event->x_root,
2393 event->y_root, event->time, FALSE);
2397 priv->first_drag = FALSE;
2399 if ((priv->mode != HILDON_PANNABLE_AREA_MODE_PUSH) &&
2400 (priv->mode != HILDON_PANNABLE_AREA_MODE_AUTO)) {
2403 priv->idle_id = gdk_threads_add_timeout ((gint)
2404 (1000.0 / (gdouble) priv->sps),
2406 hildon_pannable_area_timeout, area);
2412 hildon_pannable_area_handle_move (HildonPannableArea *area,
2413 GdkEventMotion * event,
2417 HildonPannableAreaPrivate *priv = area->priv;
2420 switch (priv->mode) {
2421 case HILDON_PANNABLE_AREA_MODE_PUSH:
2422 /* Scroll by the amount of pixels the cursor has moved
2423 * since the last motion event.
2425 hildon_pannable_area_motion_event_scroll (area, *x, *y);
2429 case HILDON_PANNABLE_AREA_MODE_ACCEL:
2430 /* Set acceleration relative to the initial click */
2431 priv->ex = event->x;
2432 priv->ey = event->y;
2433 priv->vel_x = ((*x > 0) ? 1 : -1) *
2435 (gdouble) GTK_WIDGET (area)->allocation.width) *
2436 (priv->vmax - priv->vmin)) + priv->vmin);
2437 priv->vel_y = ((*y > 0) ? 1 : -1) *
2439 (gdouble) GTK_WIDGET (area)->allocation.height) *
2440 (priv->vmax - priv->vmin)) + priv->vmin);
2442 case HILDON_PANNABLE_AREA_MODE_AUTO:
2444 delta = event->time - priv->last_time;
2446 if (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT) {
2447 gdouble dist = event->y - priv->y;
2449 hildon_pannable_area_calculate_velocity (&priv->vel_y,
2461 if (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ) {
2462 gdouble dist = event->x - priv->x;
2464 hildon_pannable_area_calculate_velocity (&priv->vel_x,
2476 hildon_pannable_area_motion_event_scroll (area, *x, *y);
2478 if (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ)
2480 if (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT)
2490 hildon_pannable_area_motion_notify_cb (GtkWidget * widget,
2491 GdkEventMotion * event)
2493 HildonPannableArea *area = HILDON_PANNABLE_AREA (widget);
2494 HildonPannableAreaPrivate *priv = area->priv;
2497 if (gtk_bin_get_child (GTK_BIN (widget)) == NULL)
2500 if ((!priv->enabled) || (!priv->button_pressed) ||
2501 ((event->time == priv->last_time) && (priv->last_type == 2))) {
2502 gdk_window_get_pointer (widget->window, NULL, NULL, 0);
2506 if (priv->last_type == 1) {
2507 priv->first_drag = TRUE;
2510 x = event->x - priv->x;
2511 y = event->y - priv->y;
2514 hildon_pannable_area_check_move (area, event, &x, &y);
2518 hildon_pannable_area_handle_move (area, event, &x, &y);
2519 } else if (priv->child) {
2523 pos_x = priv->cx + (event->x - priv->ix);
2524 pos_y = priv->cy + (event->y - priv->iy);
2526 in = (((0 <= pos_x)&&(priv->child_width >= pos_x)) &&
2527 ((0 <= pos_y)&&(priv->child_height >= pos_y)));
2529 if (((!priv->last_in)&&in)||((priv->last_in)&&(!in))) {
2531 synth_crossing (priv->child, pos_x, pos_y, event->x_root,
2532 event->y_root, event->time, in);
2538 priv->last_time = event->time;
2539 priv->last_type = 2;
2542 /* Send motion notify to child */
2543 event = (GdkEventMotion *) gdk_event_copy ((GdkEvent *) event);
2544 event->x = priv->cx + (event->x - priv->ix);
2545 event->y = priv->cy + (event->y - priv->iy);
2546 event->window = g_object_ref (priv->child);
2547 gdk_event_put ((GdkEvent *) event);
2548 gdk_event_free ((GdkEvent *) event);
2551 gdk_window_get_pointer (widget->window, NULL, NULL, 0);
2557 hildon_pannable_leave_notify_event (GtkWidget *widget,
2558 GdkEventCrossing *event)
2560 HildonPannableArea *area = HILDON_PANNABLE_AREA (widget);
2561 HildonPannableAreaPrivate *priv = area->priv;
2563 if ((priv->child)&&(priv->last_in)) {
2564 priv->last_in = FALSE;
2566 synth_crossing (priv->child, 0, 0, event->x_root,
2567 event->y_root, event->time, FALSE);
2574 hildon_pannable_area_button_release_cb (GtkWidget * widget,
2575 GdkEventButton * event)
2577 HildonPannableArea *area = HILDON_PANNABLE_AREA (widget);
2578 HildonPannableAreaPrivate *priv = area->priv;
2583 if (((event->time == priv->last_time) && (priv->last_type == 3))
2584 || (gtk_bin_get_child (GTK_BIN (widget)) == NULL)
2585 || (!priv->button_pressed) || (!priv->enabled) || (event->button != 1))
2588 /* if last event was a motion-notify we have to check the movement
2589 and launch the animation */
2590 if (priv->last_type == 2) {
2592 dx = event->x - priv->x;
2593 dy = event->y - priv->y;
2595 hildon_pannable_area_check_move (area, (GdkEventMotion *) event, &dx, &dy);
2598 gdouble delta = event->time - priv->last_time;
2600 hildon_pannable_area_handle_move (area, (GdkEventMotion *) event, &dx, &dy);
2602 /* move all the way to the last position now */
2603 if (priv->motion_event_scroll_timeout) {
2604 g_source_remove (priv->motion_event_scroll_timeout);
2605 hildon_pannable_area_motion_event_scroll_timeout (HILDON_PANNABLE_AREA (widget));
2610 if ((ABS (dx) < 4.0) && (delta >= CURSOR_STOPPED_TIMEOUT))
2613 if ((ABS (dy) < 4.0) && (delta >= CURSOR_STOPPED_TIMEOUT))
2618 /* If overshoot has been initiated with a finger down, on release set max speed */
2619 if (priv->overshot_dist_y != 0) {
2620 priv->overshooting_y = priv->bounce_steps; /* Hack to stop a bounce in the finger down case */
2621 priv->vel_y = priv->vmax_overshooting;
2624 if (priv->overshot_dist_x != 0) {
2625 priv->overshooting_x = priv->bounce_steps; /* Hack to stop a bounce in the finger down case */
2626 priv->vel_x = priv->vmax_overshooting;
2629 priv->button_pressed = FALSE;
2631 if ((ABS (priv->vel_y) >= priv->vmin) ||
2632 (ABS (priv->vel_x) >= priv->vmin)) {
2634 priv->scroll_indicator_alpha = 1.0;
2636 if (ABS (priv->vel_x) > MAX_SPEED_THRESHOLD)
2637 priv->vel_x = (priv->vel_x > 0) ? priv->vmax : -priv->vmax;
2639 if (ABS (priv->vel_y) > MAX_SPEED_THRESHOLD)
2640 priv->vel_y = (priv->vel_y > 0) ? priv->vmax : -priv->vmax;
2643 priv->idle_id = gdk_threads_add_timeout ((gint) (1000.0 / (gdouble) priv->sps),
2645 hildon_pannable_area_timeout, widget);
2648 priv->scroll_indicator_event_interrupt = 0;
2649 priv->scroll_delay_counter = priv->scrollbar_fade_delay;
2651 hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget),
2652 priv->scroll_indicator_alpha);
2654 priv->last_time = event->time;
2655 priv->last_type = 3;
2658 priv->moved = FALSE;
2663 hildon_pannable_area_get_topmost (gtk_bin_get_child (GTK_BIN (widget))->window,
2664 event->x, event->y, &x, &y, GDK_BUTTON_RELEASE_MASK);
2666 event = (GdkEventButton *) gdk_event_copy ((GdkEvent *) event);
2670 /* Leave the widget if we've moved - This doesn't break selection,
2671 * but stops buttons from being clicked.
2673 if ((child != priv->child) || (priv->moved)) {
2674 /* Send synthetic leave event */
2675 synth_crossing (priv->child, x, y, event->x_root,
2676 event->y_root, event->time, FALSE);
2677 /* Send synthetic button release event */
2678 ((GdkEventAny *) event)->window = g_object_ref (priv->child);
2679 gdk_event_put ((GdkEvent *) event);
2681 /* Send synthetic button release event */
2682 ((GdkEventAny *) event)->window = g_object_ref (child);
2683 gdk_event_put ((GdkEvent *) event);
2684 /* Send synthetic leave event */
2685 synth_crossing (priv->child, x, y, event->x_root,
2686 event->y_root, event->time, FALSE);
2688 g_object_remove_weak_pointer ((GObject *) priv->child,
2689 (gpointer) & priv->child);
2691 priv->moved = FALSE;
2692 gdk_event_free ((GdkEvent *) event);
2697 /* utility event handler */
2699 hildon_pannable_area_scroll_cb (GtkWidget *widget,
2700 GdkEventScroll *event)
2702 GtkAdjustment *adj = NULL;
2703 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
2705 if ((!priv->enabled) ||
2706 (gtk_bin_get_child (GTK_BIN (widget)) == NULL))
2709 priv->scroll_indicator_event_interrupt = 0;
2710 priv->scroll_delay_counter = priv->scrollbar_fade_delay + 20;
2712 hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget), 1.0);
2714 /* Stop inertial scrolling */
2715 if (priv->idle_id) {
2718 priv->overshooting_x = 0;
2719 priv->overshooting_y = 0;
2721 if ((priv->overshot_dist_x>0)||(priv->overshot_dist_y>0)) {
2722 priv->overshot_dist_x = 0;
2723 priv->overshot_dist_y = 0;
2725 gtk_widget_queue_resize (GTK_WIDGET (widget));
2728 g_source_remove (priv->idle_id);
2732 if (event->direction == GDK_SCROLL_UP || event->direction == GDK_SCROLL_DOWN)
2733 adj = priv->vadjust;
2735 adj = priv->hadjust;
2739 gdouble delta, new_value;
2741 /* from gtkrange.c calculate delta*/
2742 delta = pow (adj->page_size, 2.0 / 3.0);
2744 if (event->direction == GDK_SCROLL_UP ||
2745 event->direction == GDK_SCROLL_LEFT)
2748 new_value = CLAMP (adj->value + delta, adj->lower, adj->upper - adj->page_size);
2750 gtk_adjustment_set_value (adj, new_value);
2757 hildon_pannable_area_child_mapped (GtkWidget *widget,
2761 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (user_data)->priv;
2763 if (priv->event_window != NULL && priv->enabled)
2764 gdk_window_raise (priv->event_window);
2768 hildon_pannable_area_add (GtkContainer *container, GtkWidget *child)
2770 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (container)->priv;
2772 g_return_if_fail (gtk_bin_get_child (GTK_BIN (container)) == NULL);
2774 gtk_widget_set_parent (child, GTK_WIDGET (container));
2775 GTK_BIN (container)->child = child;
2777 g_signal_connect_after (child, "map-event",
2778 G_CALLBACK (hildon_pannable_area_child_mapped),
2781 if (!gtk_widget_set_scroll_adjustments (child, priv->hadjust, priv->vadjust)) {
2782 g_warning ("%s: cannot add non scrollable widget, "
2783 "wrap it in a viewport", __FUNCTION__);
2788 hildon_pannable_area_remove (GtkContainer *container, GtkWidget *child)
2790 g_return_if_fail (HILDON_IS_PANNABLE_AREA (container));
2791 g_return_if_fail (child != NULL);
2792 g_return_if_fail (gtk_bin_get_child (GTK_BIN (container)) == child);
2794 gtk_widget_set_scroll_adjustments (child, NULL, NULL);
2796 g_signal_handlers_disconnect_by_func (child,
2797 hildon_pannable_area_child_mapped,
2800 /* chain parent class handler to remove child */
2801 GTK_CONTAINER_CLASS (hildon_pannable_area_parent_class)->remove (container, child);
2805 * This method calculates a factor necessary to determine the initial distance
2806 * to jump in hildon_pannable_area_scroll_to(). For fixed time and frames per
2807 * second, we know in how many frames 'n' we need to reach the destination
2808 * point. We know that, for a distance d,
2810 * d = d_0 + d_1 + ... + d_n
2812 * where d_i is the distance travelled in the i-th frame and decel_factor is
2813 * the deceleration factor. This can be rewritten as
2815 * d = d_0 + (d_0 * decel_factor) + ... + (d_n-1 * decel_factor),
2817 * since the distance travelled on each frame is the distance travelled in the
2818 * previous frame reduced by the deceleration factor. Reducing this and
2819 * factoring d_0 out, we get
2821 * d = d_0 (1 + decel_factor + ... + decel_factor^(n-1)).
2823 * Since the sum is independent of the distance to be travelled, we can define
2826 * vel_factor = 1 + decel_factor + ... + decel_factor^(n-1).
2828 * That's the gem we calculate in this method.
2831 hildon_pannable_calculate_vel_factor (HildonPannableArea * self)
2833 HildonPannableAreaPrivate *priv = self->priv;
2838 n = ceil (priv->sps * priv->scroll_time);
2840 for (i = 1; i < n && fct_i >= RATIO_TOLERANCE; i++) {
2841 fct_i *= priv->decel;
2845 priv->vel_factor = fct;
2849 * hildon_pannable_area_new:
2851 * Create a new pannable area widget
2853 * Returns: the newly created #HildonPannableArea
2859 hildon_pannable_area_new (void)
2861 return g_object_new (HILDON_TYPE_PANNABLE_AREA, NULL);
2865 * hildon_pannable_area_new_full:
2866 * @mode: #HildonPannableAreaMode
2867 * @enabled: Value for the enabled property
2868 * @vel_min: Value for the velocity-min property
2869 * @vel_max: Value for the velocity-max property
2870 * @decel: Value for the deceleration property
2871 * @sps: Value for the sps property
2873 * Create a new #HildonPannableArea widget and set various properties
2875 * returns: the newly create #HildonPannableArea
2881 hildon_pannable_area_new_full (gint mode, gboolean enabled,
2882 gdouble vel_min, gdouble vel_max,
2883 gdouble decel, guint sps)
2885 return g_object_new (HILDON_TYPE_PANNABLE_AREA,
2888 "velocity_min", vel_min,
2889 "velocity_max", vel_max,
2890 "deceleration", decel, "sps", sps, NULL);
2894 * hildon_pannable_area_add_with_viewport:
2895 * @area: A #HildonPannableArea
2896 * @child: Child widget to add to the viewport
2898 * Convenience function used to add a child to a #GtkViewport, and add the
2899 * viewport to the scrolled window.
2905 hildon_pannable_area_add_with_viewport (HildonPannableArea * area,
2909 GtkWidget *viewport;
2911 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
2912 g_return_if_fail (GTK_IS_WIDGET (child));
2913 g_return_if_fail (child->parent == NULL);
2915 bin = GTK_BIN (area);
2917 if (bin->child != NULL)
2919 g_return_if_fail (GTK_IS_VIEWPORT (bin->child));
2920 g_return_if_fail (GTK_BIN (bin->child)->child == NULL);
2922 viewport = bin->child;
2926 HildonPannableAreaPrivate *priv = area->priv;
2928 viewport = gtk_viewport_new (priv->hadjust,
2930 gtk_viewport_set_shadow_type (GTK_VIEWPORT (viewport), GTK_SHADOW_NONE);
2931 gtk_container_add (GTK_CONTAINER (area), viewport);
2934 gtk_widget_show (viewport);
2935 gtk_container_add (GTK_CONTAINER (viewport), child);
2939 * hildon_pannable_area_scroll_to:
2940 * @area: A #HildonPannableArea.
2941 * @x: The x coordinate of the destination point or -1 to ignore this axis.
2942 * @y: The y coordinate of the destination point or -1 to ignore this axis.
2944 * Smoothly scrolls @area to ensure that (@x, @y) is a visible point
2945 * on the widget. To move in only one coordinate, you must set the other one
2946 * to -1. Notice that, in %HILDON_PANNABLE_AREA_MODE_PUSH mode, this function
2947 * works just like hildon_pannable_area_jump_to().
2949 * This function is useful if you need to present the user with a particular
2950 * element inside a scrollable widget, like #GtkTreeView. For instance,
2951 * the following example shows how to scroll inside a #GtkTreeView to
2952 * make visible an item, indicated by the #GtkTreeIter @iter.
2956 * GtkTreePath *path;
2957 * GdkRectangle *rect;
2959 * path = gtk_tree_model_get_path (model, &iter);
2960 * gtk_tree_view_get_background_area (GTK_TREE_VIEW (treeview),
2961 * path, NULL, &rect);
2962 * gtk_tree_view_convert_bin_window_to_tree_coords (GTK_TREE_VIEW (treeview),
2963 * 0, rect.y, NULL, &y);
2964 * hildon_pannable_area_scroll_to (panarea, -1, y);
2965 * gtk_tree_path_free (path);
2969 * If you want to present a child widget in simpler scenarios,
2970 * use hildon_pannable_area_scroll_to_child() instead.
2972 * There is a precondition to this function: the widget must be
2973 * already realized. Check the hildon_pannable_area_jump_to_child() for
2974 * more tips regarding how to call this function during
2980 hildon_pannable_area_scroll_to (HildonPannableArea *area,
2981 const gint x, const gint y)
2983 HildonPannableAreaPrivate *priv;
2985 gint dist_x, dist_y;
2986 gboolean hscroll_visible, vscroll_visible;
2988 g_return_if_fail (GTK_WIDGET_REALIZED (area));
2989 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
2993 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
2994 priv->vadjust->page_size);
2995 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
2996 priv->hadjust->page_size);
2998 if (((!vscroll_visible)&&(!hscroll_visible)) ||
2999 (x == -1 && y == -1)) {
3003 if (priv->mode == HILDON_PANNABLE_AREA_MODE_PUSH)
3004 hildon_pannable_area_jump_to (area, x, y);
3006 width = priv->hadjust->upper - priv->hadjust->lower;
3007 height = priv->vadjust->upper - priv->vadjust->lower;
3009 g_return_if_fail (x < width || y < height);
3011 if ((x > -1)&&(hscroll_visible)) {
3012 priv->scroll_to_x = x - priv->hadjust->page_size/2;
3013 dist_x = priv->scroll_to_x - priv->hadjust->value;
3015 priv->scroll_to_x = -1;
3017 priv->vel_x = - dist_x/priv->vel_factor;
3020 priv->scroll_to_x = -1;
3023 if ((y > -1)&&(vscroll_visible)) {
3024 priv->scroll_to_y = y - priv->vadjust->page_size/2;
3025 dist_y = priv->scroll_to_y - priv->vadjust->value;
3027 priv->scroll_to_y = -1;
3029 priv->vel_y = - dist_y/priv->vel_factor;
3032 priv->scroll_to_y = y;
3035 if ((priv->scroll_to_y == -1) && (priv->scroll_to_y == -1)) {
3039 hildon_pannable_area_launch_fade_timeout (area, 1.0);
3042 priv->idle_id = gdk_threads_add_timeout ((gint) (1000.0 / (gdouble) priv->sps),
3044 hildon_pannable_area_timeout, area);
3048 * hildon_pannable_area_jump_to:
3049 * @area: A #HildonPannableArea.
3050 * @x: The x coordinate of the destination point or -1 to ignore this axis.
3051 * @y: The y coordinate of the destination point or -1 to ignore this axis.
3053 * Jumps the position of @area to ensure that (@x, @y) is a visible
3054 * point in the widget. In order to move in only one coordinate, you
3055 * must set the other one to -1. See hildon_pannable_area_scroll_to()
3056 * function for an example of how to calculate the position of
3057 * children in scrollable widgets like #GtkTreeview.
3059 * There is a precondition to this function: the widget must be
3060 * already realized. Check the hildon_pannable_area_jump_to_child() for
3061 * more tips regarding how to call this function during
3067 hildon_pannable_area_jump_to (HildonPannableArea *area,
3068 const gint x, const gint y)
3070 HildonPannableAreaPrivate *priv;
3074 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3075 g_return_if_fail (GTK_WIDGET_REALIZED (area));
3076 g_return_if_fail (x >= -1 && y >= -1);
3078 if (x == -1 && y == -1) {
3084 width = priv->hadjust->upper - priv->hadjust->lower;
3085 height = priv->vadjust->upper - priv->vadjust->lower;
3087 g_return_if_fail (x < width || y < height);
3089 hv = priv->hadjust->value;
3090 vv = priv->vadjust->value;
3093 gdouble jump_to = x - priv->hadjust->page_size/2;
3095 priv->hadjust->value = CLAMP (jump_to,
3096 priv->hadjust->lower,
3097 priv->hadjust->upper -
3098 priv->hadjust->page_size);
3102 gdouble jump_to = y - priv->vadjust->page_size/2;
3104 priv->vadjust->value = CLAMP (jump_to,
3105 priv->vadjust->lower,
3106 priv->vadjust->upper -
3107 priv->vadjust->page_size);
3110 if (hv != priv->hadjust->value)
3111 gtk_adjustment_value_changed (priv->hadjust);
3113 if (vv != priv->vadjust->value)
3114 gtk_adjustment_value_changed (priv->vadjust);
3116 priv->scroll_indicator_alpha = 1.0;
3118 if (priv->scroll_indicator_timeout) {
3119 g_source_remove (priv->scroll_indicator_timeout);
3120 priv->scroll_indicator_timeout = 0;
3123 if (priv->idle_id) {
3126 priv->overshooting_x = 0;
3127 priv->overshooting_y = 0;
3129 if ((priv->overshot_dist_x>0)||(priv->overshot_dist_y>0)) {
3130 priv->overshot_dist_x = 0;
3131 priv->overshot_dist_y = 0;
3133 gtk_widget_queue_resize (GTK_WIDGET (area));
3136 g_source_remove (priv->idle_id);
3142 * hildon_pannable_area_scroll_to_child:
3143 * @area: A #HildonPannableArea.
3144 * @child: A #GtkWidget, descendant of @area.
3146 * Smoothly scrolls until @child is visible inside @area. @child must
3147 * be a descendant of @area. If you need to scroll inside a scrollable
3148 * widget, e.g., #GtkTreeview, see hildon_pannable_area_scroll_to().
3150 * There is a precondition to this function: the widget must be
3151 * already realized. Check the hildon_pannable_area_jump_to_child() for
3152 * more tips regarding how to call this function during
3158 hildon_pannable_area_scroll_to_child (HildonPannableArea *area, GtkWidget *child)
3160 GtkWidget *bin_child;
3163 g_return_if_fail (GTK_WIDGET_REALIZED (area));
3164 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3165 g_return_if_fail (GTK_IS_WIDGET (child));
3166 g_return_if_fail (gtk_widget_is_ancestor (child, GTK_WIDGET (area)));
3168 if (GTK_BIN (area)->child == NULL)
3171 /* We need to get to check the child of the inside the area */
3172 bin_child = GTK_BIN (area)->child;
3174 /* we check if we added a viewport */
3175 if (GTK_IS_VIEWPORT (bin_child)) {
3176 bin_child = GTK_BIN (bin_child)->child;
3179 if (gtk_widget_translate_coordinates (child, bin_child, 0, 0, &x, &y))
3180 hildon_pannable_area_scroll_to (area, x, y);
3184 * hildon_pannable_area_jump_to_child:
3185 * @area: A #HildonPannableArea.
3186 * @child: A #GtkWidget, descendant of @area.
3188 * Jumps to make sure @child is visible inside @area. @child must
3189 * be a descendant of @area. If you want to move inside a scrollable
3190 * widget, like, #GtkTreeview, see hildon_pannable_area_scroll_to().
3192 * There is a precondition to this function: the widget must be
3193 * already realized. You can control if the widget is ready with the
3194 * GTK_WIDGET_REALIZED macro. If you want to call this function during
3195 * the initialization process of the widget do it inside a callback to
3196 * the ::realize signal, using g_signal_connect_after() function.
3201 hildon_pannable_area_jump_to_child (HildonPannableArea *area, GtkWidget *child)
3203 GtkWidget *bin_child;
3206 g_return_if_fail (GTK_WIDGET_REALIZED (area));
3207 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3208 g_return_if_fail (GTK_IS_WIDGET (child));
3209 g_return_if_fail (gtk_widget_is_ancestor (child, GTK_WIDGET (area)));
3211 if (gtk_bin_get_child (GTK_BIN (area)) == NULL)
3214 /* We need to get to check the child of the inside the area */
3215 bin_child = gtk_bin_get_child (GTK_BIN (area));
3217 /* we check if we added a viewport */
3218 if (GTK_IS_VIEWPORT (bin_child)) {
3219 bin_child = gtk_bin_get_child (GTK_BIN (bin_child));
3222 if (gtk_widget_translate_coordinates (child, bin_child, 0, 0, &x, &y))
3223 hildon_pannable_area_jump_to (area, x, y);
3227 * hildon_pannable_get_child_widget_at:
3228 * @area: A #HildonPannableArea.
3229 * @x: horizontal coordinate of the point
3230 * @y: vertical coordinate of the point
3232 * Get the widget at the point (x, y) inside the pannable area. In
3233 * case no widget found it returns NULL.
3235 * returns: the #GtkWidget if we find a widget, NULL in any other case
3240 hildon_pannable_get_child_widget_at (HildonPannableArea *area,
3241 gdouble x, gdouble y)
3243 GdkWindow *window = NULL;
3244 GtkWidget *child_widget = NULL;
3246 window = hildon_pannable_area_get_topmost
3247 (gtk_bin_get_child (GTK_BIN (area))->window,
3248 x, y, NULL, NULL, GDK_ALL_EVENTS_MASK);
3250 gdk_window_get_user_data (window, (gpointer) &child_widget);
3252 return child_widget;
3257 * hildon_pannable_area_get_hadjustment:
3258 * @area: A #HildonPannableArea.
3260 * Returns the horizontal adjustment. This adjustment is the internal
3261 * widget adjustment used to control the animations. Do not modify it
3262 * directly to change the position of the pannable, to do that use the
3263 * pannable API. If you modify the object directly it could cause
3264 * artifacts in the animations.
3266 * returns: The horizontal #GtkAdjustment
3271 hildon_pannable_area_get_hadjustment (HildonPannableArea *area)
3274 g_return_val_if_fail (HILDON_IS_PANNABLE_AREA (area), NULL);
3276 return area->priv->hadjust;
3280 * hildon_pannable_area_get_vadjustment:
3281 * @area: A #HildonPannableArea.
3283 * Returns the vertical adjustment. This adjustment is the internal
3284 * widget adjustment used to control the animations. Do not modify it
3285 * directly to change the position of the pannable, to do that use the
3286 * pannable API. If you modify the object directly it could cause
3287 * artifacts in the animations.
3289 * returns: The vertical #GtkAdjustment
3294 hildon_pannable_area_get_vadjustment (HildonPannableArea *area)
3296 g_return_val_if_fail (HILDON_IS_PANNABLE_AREA (area), NULL);
3298 return area->priv->vadjust;
3303 * hildon_pannable_area_get_size_request_policy:
3304 * @area: A #HildonPannableArea.
3306 * This function returns the current size request policy of the
3307 * widget. That policy controls the way the size_request is done in
3308 * the pannable area. Check
3309 * hildon_pannable_area_set_size_request_policy() for a more detailed
3312 * returns: the policy is currently being used in the widget
3313 * #HildonSizeRequestPolicy.
3317 HildonSizeRequestPolicy
3318 hildon_pannable_area_get_size_request_policy (HildonPannableArea *area)
3320 HildonPannableAreaPrivate *priv;
3322 g_return_val_if_fail (HILDON_IS_PANNABLE_AREA (area), FALSE);
3326 return priv->size_request_policy;
3330 * hildon_pannable_area_set_size_request_policy:
3331 * @area: A #HildonPannableArea.
3332 * @size_request_policy: One of the allowed #HildonSizeRequestPolicy
3334 * This function sets the pannable area size request policy. That
3335 * policy controls the way the size_request is done in the pannable
3336 * area. Pannable can use the size request of its children
3337 * (#HILDON_SIZE_REQUEST_CHILDREN) or the minimum size required for
3338 * the area itself (#HILDON_SIZE_REQUEST_MINIMUM), the latter is the
3339 * default. Recall this size depends on the scrolling policy you are
3340 * requesting to the pannable area, if you set #GTK_POLICY_NEVER this
3341 * parameter will not have any effect with
3342 * #HILDON_SIZE_REQUEST_MINIMUM set.
3346 * Deprecated: This method and the policy request is deprecated, DO
3347 * NOT use it in future code, the only policy properly supported in
3348 * gtk+ nowadays is the minimum size. Use #gtk_window_set_default_size
3349 * or #gtk_window_set_geometry_hints with the proper size in your case
3350 * to define the height of your dialogs.
3353 hildon_pannable_area_set_size_request_policy (HildonPannableArea *area,
3354 HildonSizeRequestPolicy size_request_policy)
3356 HildonPannableAreaPrivate *priv;
3358 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3362 if (priv->size_request_policy == size_request_policy)
3365 priv->size_request_policy = size_request_policy;
3367 gtk_widget_queue_resize (GTK_WIDGET (area));
3369 g_object_notify (G_OBJECT (area), "size-request-policy");