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 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
2581 if (((event->time == priv->last_time) && (priv->last_type == 3))
2582 || (gtk_bin_get_child (GTK_BIN (widget)) == NULL)
2583 || (!priv->button_pressed) || (!priv->enabled) || (event->button != 1))
2586 priv->scroll_indicator_event_interrupt = 0;
2587 priv->scroll_delay_counter = priv->scrollbar_fade_delay;
2589 /* move all the way to the last position */
2590 if (priv->motion_event_scroll_timeout) {
2591 g_source_remove (priv->motion_event_scroll_timeout);
2592 hildon_pannable_area_motion_event_scroll_timeout (HILDON_PANNABLE_AREA (widget));
2597 if (priv->last_type == 2) {
2598 gdouble delta = event->time - priv->last_time;
2600 if (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT) {
2601 gdouble dist = event->y - priv->y;
2603 if (ABS (dist) >= 1.0) {
2604 hildon_pannable_area_calculate_velocity (&priv->vel_y,
2612 priv->motion_y = dist;
2613 hildon_pannable_area_motion_event_scroll_timeout (HILDON_PANNABLE_AREA (widget));
2615 if (delta >= CURSOR_STOPPED_TIMEOUT) {
2621 if (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ) {
2622 gdouble dist = event->x - priv->x;
2624 if (ABS (dist) >= 1.0) {
2625 hildon_pannable_area_calculate_velocity (&priv->vel_x,
2632 priv->motion_x = dist;
2633 hildon_pannable_area_motion_event_scroll_timeout (HILDON_PANNABLE_AREA (widget));
2635 if (delta >= CURSOR_STOPPED_TIMEOUT) {
2642 if ((ABS (priv->vel_y) > priv->vmin)||
2643 (ABS (priv->vel_x) > priv->vmin)) {
2644 priv->scroll_indicator_alpha = 1.0;
2647 hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget),
2648 priv->scroll_indicator_alpha);
2650 priv->button_pressed = FALSE;
2652 if (priv->mode == HILDON_PANNABLE_AREA_MODE_AUTO ||
2653 priv->mode == HILDON_PANNABLE_AREA_MODE_ACCEL) {
2655 /* If overshoot has been initiated with a finger down, on release set max speed */
2656 if (priv->overshot_dist_y != 0) {
2657 priv->overshooting_y = priv->bounce_steps; /* Hack to stop a bounce in the finger down case */
2658 priv->vel_y = priv->vmax_overshooting;
2661 if (priv->overshot_dist_x != 0) {
2662 priv->overshooting_x = priv->bounce_steps; /* Hack to stop a bounce in the finger down case */
2663 priv->vel_x = priv->vmax_overshooting;
2666 if ((ABS (priv->vel_y) >= priv->vmin) ||
2667 (ABS (priv->vel_x) >= priv->vmin)) {
2669 if (ABS (priv->vel_x) > MAX_SPEED_THRESHOLD)
2670 priv->vel_x = (priv->vel_x > 0) ? priv->vmax : -priv->vmax;
2672 if (ABS (priv->vel_y) > MAX_SPEED_THRESHOLD)
2673 priv->vel_y = (priv->vel_y > 0) ? priv->vmax : -priv->vmax;
2676 priv->idle_id = gdk_threads_add_timeout ((gint) (1000.0 / (gdouble) priv->sps),
2678 hildon_pannable_area_timeout, widget);
2682 priv->last_time = event->time;
2683 priv->last_type = 3;
2686 priv->moved = FALSE;
2691 hildon_pannable_area_get_topmost (gtk_bin_get_child (GTK_BIN (widget))->window,
2692 event->x, event->y, &x, &y, GDK_BUTTON_RELEASE_MASK);
2694 event = (GdkEventButton *) gdk_event_copy ((GdkEvent *) event);
2698 /* Leave the widget if we've moved - This doesn't break selection,
2699 * but stops buttons from being clicked.
2701 if ((child != priv->child) || (priv->moved)) {
2702 /* Send synthetic leave event */
2703 synth_crossing (priv->child, x, y, event->x_root,
2704 event->y_root, event->time, FALSE);
2705 /* Send synthetic button release event */
2706 ((GdkEventAny *) event)->window = g_object_ref (priv->child);
2707 gdk_event_put ((GdkEvent *) event);
2709 /* Send synthetic button release event */
2710 ((GdkEventAny *) event)->window = g_object_ref (child);
2711 gdk_event_put ((GdkEvent *) event);
2712 /* Send synthetic leave event */
2713 synth_crossing (priv->child, x, y, event->x_root,
2714 event->y_root, event->time, FALSE);
2716 g_object_remove_weak_pointer ((GObject *) priv->child,
2717 (gpointer) & priv->child);
2719 priv->moved = FALSE;
2720 gdk_event_free ((GdkEvent *) event);
2725 /* utility event handler */
2727 hildon_pannable_area_scroll_cb (GtkWidget *widget,
2728 GdkEventScroll *event)
2730 GtkAdjustment *adj = NULL;
2731 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
2733 if ((!priv->enabled) ||
2734 (gtk_bin_get_child (GTK_BIN (widget)) == NULL))
2737 priv->scroll_indicator_event_interrupt = 0;
2738 priv->scroll_delay_counter = priv->scrollbar_fade_delay + 20;
2740 hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget), 1.0);
2742 /* Stop inertial scrolling */
2743 if (priv->idle_id) {
2746 priv->overshooting_x = 0;
2747 priv->overshooting_y = 0;
2749 if ((priv->overshot_dist_x>0)||(priv->overshot_dist_y>0)) {
2750 priv->overshot_dist_x = 0;
2751 priv->overshot_dist_y = 0;
2753 gtk_widget_queue_resize (GTK_WIDGET (widget));
2756 g_source_remove (priv->idle_id);
2760 if (event->direction == GDK_SCROLL_UP || event->direction == GDK_SCROLL_DOWN)
2761 adj = priv->vadjust;
2763 adj = priv->hadjust;
2767 gdouble delta, new_value;
2769 /* from gtkrange.c calculate delta*/
2770 delta = pow (adj->page_size, 2.0 / 3.0);
2772 if (event->direction == GDK_SCROLL_UP ||
2773 event->direction == GDK_SCROLL_LEFT)
2776 new_value = CLAMP (adj->value + delta, adj->lower, adj->upper - adj->page_size);
2778 gtk_adjustment_set_value (adj, new_value);
2785 hildon_pannable_area_child_mapped (GtkWidget *widget,
2789 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (user_data)->priv;
2791 if (priv->event_window != NULL && priv->enabled)
2792 gdk_window_raise (priv->event_window);
2796 hildon_pannable_area_add (GtkContainer *container, GtkWidget *child)
2798 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (container)->priv;
2800 g_return_if_fail (gtk_bin_get_child (GTK_BIN (container)) == NULL);
2802 gtk_widget_set_parent (child, GTK_WIDGET (container));
2803 GTK_BIN (container)->child = child;
2805 g_signal_connect_after (child, "map-event",
2806 G_CALLBACK (hildon_pannable_area_child_mapped),
2809 if (!gtk_widget_set_scroll_adjustments (child, priv->hadjust, priv->vadjust)) {
2810 g_warning ("%s: cannot add non scrollable widget, "
2811 "wrap it in a viewport", __FUNCTION__);
2816 hildon_pannable_area_remove (GtkContainer *container, GtkWidget *child)
2818 g_return_if_fail (HILDON_IS_PANNABLE_AREA (container));
2819 g_return_if_fail (child != NULL);
2820 g_return_if_fail (gtk_bin_get_child (GTK_BIN (container)) == child);
2822 gtk_widget_set_scroll_adjustments (child, NULL, NULL);
2824 g_signal_handlers_disconnect_by_func (child,
2825 hildon_pannable_area_child_mapped,
2828 /* chain parent class handler to remove child */
2829 GTK_CONTAINER_CLASS (hildon_pannable_area_parent_class)->remove (container, child);
2833 * This method calculates a factor necessary to determine the initial distance
2834 * to jump in hildon_pannable_area_scroll_to(). For fixed time and frames per
2835 * second, we know in how many frames 'n' we need to reach the destination
2836 * point. We know that, for a distance d,
2838 * d = d_0 + d_1 + ... + d_n
2840 * where d_i is the distance travelled in the i-th frame and decel_factor is
2841 * the deceleration factor. This can be rewritten as
2843 * d = d_0 + (d_0 * decel_factor) + ... + (d_n-1 * decel_factor),
2845 * since the distance travelled on each frame is the distance travelled in the
2846 * previous frame reduced by the deceleration factor. Reducing this and
2847 * factoring d_0 out, we get
2849 * d = d_0 (1 + decel_factor + ... + decel_factor^(n-1)).
2851 * Since the sum is independent of the distance to be travelled, we can define
2854 * vel_factor = 1 + decel_factor + ... + decel_factor^(n-1).
2856 * That's the gem we calculate in this method.
2859 hildon_pannable_calculate_vel_factor (HildonPannableArea * self)
2861 HildonPannableAreaPrivate *priv = self->priv;
2866 n = ceil (priv->sps * priv->scroll_time);
2868 for (i = 1; i < n && fct_i >= RATIO_TOLERANCE; i++) {
2869 fct_i *= priv->decel;
2873 priv->vel_factor = fct;
2877 * hildon_pannable_area_new:
2879 * Create a new pannable area widget
2881 * Returns: the newly created #HildonPannableArea
2887 hildon_pannable_area_new (void)
2889 return g_object_new (HILDON_TYPE_PANNABLE_AREA, NULL);
2893 * hildon_pannable_area_new_full:
2894 * @mode: #HildonPannableAreaMode
2895 * @enabled: Value for the enabled property
2896 * @vel_min: Value for the velocity-min property
2897 * @vel_max: Value for the velocity-max property
2898 * @decel: Value for the deceleration property
2899 * @sps: Value for the sps property
2901 * Create a new #HildonPannableArea widget and set various properties
2903 * returns: the newly create #HildonPannableArea
2909 hildon_pannable_area_new_full (gint mode, gboolean enabled,
2910 gdouble vel_min, gdouble vel_max,
2911 gdouble decel, guint sps)
2913 return g_object_new (HILDON_TYPE_PANNABLE_AREA,
2916 "velocity_min", vel_min,
2917 "velocity_max", vel_max,
2918 "deceleration", decel, "sps", sps, NULL);
2922 * hildon_pannable_area_add_with_viewport:
2923 * @area: A #HildonPannableArea
2924 * @child: Child widget to add to the viewport
2926 * Convenience function used to add a child to a #GtkViewport, and add the
2927 * viewport to the scrolled window.
2933 hildon_pannable_area_add_with_viewport (HildonPannableArea * area,
2937 GtkWidget *viewport;
2939 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
2940 g_return_if_fail (GTK_IS_WIDGET (child));
2941 g_return_if_fail (child->parent == NULL);
2943 bin = GTK_BIN (area);
2945 if (bin->child != NULL)
2947 g_return_if_fail (GTK_IS_VIEWPORT (bin->child));
2948 g_return_if_fail (GTK_BIN (bin->child)->child == NULL);
2950 viewport = bin->child;
2954 HildonPannableAreaPrivate *priv = area->priv;
2956 viewport = gtk_viewport_new (priv->hadjust,
2958 gtk_viewport_set_shadow_type (GTK_VIEWPORT (viewport), GTK_SHADOW_NONE);
2959 gtk_container_add (GTK_CONTAINER (area), viewport);
2962 gtk_widget_show (viewport);
2963 gtk_container_add (GTK_CONTAINER (viewport), child);
2967 * hildon_pannable_area_scroll_to:
2968 * @area: A #HildonPannableArea.
2969 * @x: The x coordinate of the destination point or -1 to ignore this axis.
2970 * @y: The y coordinate of the destination point or -1 to ignore this axis.
2972 * Smoothly scrolls @area to ensure that (@x, @y) is a visible point
2973 * on the widget. To move in only one coordinate, you must set the other one
2974 * to -1. Notice that, in %HILDON_PANNABLE_AREA_MODE_PUSH mode, this function
2975 * works just like hildon_pannable_area_jump_to().
2977 * This function is useful if you need to present the user with a particular
2978 * element inside a scrollable widget, like #GtkTreeView. For instance,
2979 * the following example shows how to scroll inside a #GtkTreeView to
2980 * make visible an item, indicated by the #GtkTreeIter @iter.
2984 * GtkTreePath *path;
2985 * GdkRectangle *rect;
2987 * path = gtk_tree_model_get_path (model, &iter);
2988 * gtk_tree_view_get_background_area (GTK_TREE_VIEW (treeview),
2989 * path, NULL, &rect);
2990 * gtk_tree_view_convert_bin_window_to_tree_coords (GTK_TREE_VIEW (treeview),
2991 * 0, rect.y, NULL, &y);
2992 * hildon_pannable_area_scroll_to (panarea, -1, y);
2993 * gtk_tree_path_free (path);
2997 * If you want to present a child widget in simpler scenarios,
2998 * use hildon_pannable_area_scroll_to_child() instead.
3000 * There is a precondition to this function: the widget must be
3001 * already realized. Check the hildon_pannable_area_jump_to_child() for
3002 * more tips regarding how to call this function during
3008 hildon_pannable_area_scroll_to (HildonPannableArea *area,
3009 const gint x, const gint y)
3011 HildonPannableAreaPrivate *priv;
3013 gint dist_x, dist_y;
3014 gboolean hscroll_visible, vscroll_visible;
3016 g_return_if_fail (GTK_WIDGET_REALIZED (area));
3017 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3021 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
3022 priv->vadjust->page_size);
3023 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
3024 priv->hadjust->page_size);
3026 if (((!vscroll_visible)&&(!hscroll_visible)) ||
3027 (x == -1 && y == -1)) {
3031 if (priv->mode == HILDON_PANNABLE_AREA_MODE_PUSH)
3032 hildon_pannable_area_jump_to (area, x, y);
3034 width = priv->hadjust->upper - priv->hadjust->lower;
3035 height = priv->vadjust->upper - priv->vadjust->lower;
3037 g_return_if_fail (x < width || y < height);
3039 if ((x > -1)&&(hscroll_visible)) {
3040 priv->scroll_to_x = x - priv->hadjust->page_size/2;
3041 dist_x = priv->scroll_to_x - priv->hadjust->value;
3043 priv->scroll_to_x = -1;
3045 priv->vel_x = - dist_x/priv->vel_factor;
3048 priv->scroll_to_x = -1;
3051 if ((y > -1)&&(vscroll_visible)) {
3052 priv->scroll_to_y = y - priv->vadjust->page_size/2;
3053 dist_y = priv->scroll_to_y - priv->vadjust->value;
3055 priv->scroll_to_y = -1;
3057 priv->vel_y = - dist_y/priv->vel_factor;
3060 priv->scroll_to_y = y;
3063 if ((priv->scroll_to_y == -1) && (priv->scroll_to_y == -1)) {
3067 hildon_pannable_area_launch_fade_timeout (area, 1.0);
3070 priv->idle_id = gdk_threads_add_timeout ((gint) (1000.0 / (gdouble) priv->sps),
3072 hildon_pannable_area_timeout, area);
3076 * hildon_pannable_area_jump_to:
3077 * @area: A #HildonPannableArea.
3078 * @x: The x coordinate of the destination point or -1 to ignore this axis.
3079 * @y: The y coordinate of the destination point or -1 to ignore this axis.
3081 * Jumps the position of @area to ensure that (@x, @y) is a visible
3082 * point in the widget. In order to move in only one coordinate, you
3083 * must set the other one to -1. See hildon_pannable_area_scroll_to()
3084 * function for an example of how to calculate the position of
3085 * children in scrollable widgets like #GtkTreeview.
3087 * There is a precondition to this function: the widget must be
3088 * already realized. Check the hildon_pannable_area_jump_to_child() for
3089 * more tips regarding how to call this function during
3095 hildon_pannable_area_jump_to (HildonPannableArea *area,
3096 const gint x, const gint y)
3098 HildonPannableAreaPrivate *priv;
3102 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3103 g_return_if_fail (GTK_WIDGET_REALIZED (area));
3104 g_return_if_fail (x >= -1 && y >= -1);
3106 if (x == -1 && y == -1) {
3112 width = priv->hadjust->upper - priv->hadjust->lower;
3113 height = priv->vadjust->upper - priv->vadjust->lower;
3115 g_return_if_fail (x < width || y < height);
3117 hv = priv->hadjust->value;
3118 vv = priv->vadjust->value;
3121 gdouble jump_to = x - priv->hadjust->page_size/2;
3123 priv->hadjust->value = CLAMP (jump_to,
3124 priv->hadjust->lower,
3125 priv->hadjust->upper -
3126 priv->hadjust->page_size);
3130 gdouble jump_to = y - priv->vadjust->page_size/2;
3132 priv->vadjust->value = CLAMP (jump_to,
3133 priv->vadjust->lower,
3134 priv->vadjust->upper -
3135 priv->vadjust->page_size);
3138 if (hv != priv->hadjust->value)
3139 gtk_adjustment_value_changed (priv->hadjust);
3141 if (vv != priv->vadjust->value)
3142 gtk_adjustment_value_changed (priv->vadjust);
3144 priv->scroll_indicator_alpha = 1.0;
3146 if (priv->scroll_indicator_timeout) {
3147 g_source_remove (priv->scroll_indicator_timeout);
3148 priv->scroll_indicator_timeout = 0;
3151 if (priv->idle_id) {
3154 priv->overshooting_x = 0;
3155 priv->overshooting_y = 0;
3157 if ((priv->overshot_dist_x>0)||(priv->overshot_dist_y>0)) {
3158 priv->overshot_dist_x = 0;
3159 priv->overshot_dist_y = 0;
3161 gtk_widget_queue_resize (GTK_WIDGET (area));
3164 g_source_remove (priv->idle_id);
3170 * hildon_pannable_area_scroll_to_child:
3171 * @area: A #HildonPannableArea.
3172 * @child: A #GtkWidget, descendant of @area.
3174 * Smoothly scrolls until @child is visible inside @area. @child must
3175 * be a descendant of @area. If you need to scroll inside a scrollable
3176 * widget, e.g., #GtkTreeview, see hildon_pannable_area_scroll_to().
3178 * There is a precondition to this function: the widget must be
3179 * already realized. Check the hildon_pannable_area_jump_to_child() for
3180 * more tips regarding how to call this function during
3186 hildon_pannable_area_scroll_to_child (HildonPannableArea *area, GtkWidget *child)
3188 GtkWidget *bin_child;
3191 g_return_if_fail (GTK_WIDGET_REALIZED (area));
3192 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3193 g_return_if_fail (GTK_IS_WIDGET (child));
3194 g_return_if_fail (gtk_widget_is_ancestor (child, GTK_WIDGET (area)));
3196 if (GTK_BIN (area)->child == NULL)
3199 /* We need to get to check the child of the inside the area */
3200 bin_child = GTK_BIN (area)->child;
3202 /* we check if we added a viewport */
3203 if (GTK_IS_VIEWPORT (bin_child)) {
3204 bin_child = GTK_BIN (bin_child)->child;
3207 if (gtk_widget_translate_coordinates (child, bin_child, 0, 0, &x, &y))
3208 hildon_pannable_area_scroll_to (area, x, y);
3212 * hildon_pannable_area_jump_to_child:
3213 * @area: A #HildonPannableArea.
3214 * @child: A #GtkWidget, descendant of @area.
3216 * Jumps to make sure @child is visible inside @area. @child must
3217 * be a descendant of @area. If you want to move inside a scrollable
3218 * widget, like, #GtkTreeview, see hildon_pannable_area_scroll_to().
3220 * There is a precondition to this function: the widget must be
3221 * already realized. You can control if the widget is ready with the
3222 * GTK_WIDGET_REALIZED macro. If you want to call this function during
3223 * the initialization process of the widget do it inside a callback to
3224 * the ::realize signal, using g_signal_connect_after() function.
3229 hildon_pannable_area_jump_to_child (HildonPannableArea *area, GtkWidget *child)
3231 GtkWidget *bin_child;
3234 g_return_if_fail (GTK_WIDGET_REALIZED (area));
3235 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3236 g_return_if_fail (GTK_IS_WIDGET (child));
3237 g_return_if_fail (gtk_widget_is_ancestor (child, GTK_WIDGET (area)));
3239 if (gtk_bin_get_child (GTK_BIN (area)) == NULL)
3242 /* We need to get to check the child of the inside the area */
3243 bin_child = gtk_bin_get_child (GTK_BIN (area));
3245 /* we check if we added a viewport */
3246 if (GTK_IS_VIEWPORT (bin_child)) {
3247 bin_child = gtk_bin_get_child (GTK_BIN (bin_child));
3250 if (gtk_widget_translate_coordinates (child, bin_child, 0, 0, &x, &y))
3251 hildon_pannable_area_jump_to (area, x, y);
3255 * hildon_pannable_get_child_widget_at:
3256 * @area: A #HildonPannableArea.
3257 * @x: horizontal coordinate of the point
3258 * @y: vertical coordinate of the point
3260 * Get the widget at the point (x, y) inside the pannable area. In
3261 * case no widget found it returns NULL.
3263 * returns: the #GtkWidget if we find a widget, NULL in any other case
3268 hildon_pannable_get_child_widget_at (HildonPannableArea *area,
3269 gdouble x, gdouble y)
3271 GdkWindow *window = NULL;
3272 GtkWidget *child_widget = NULL;
3274 window = hildon_pannable_area_get_topmost
3275 (gtk_bin_get_child (GTK_BIN (area))->window,
3276 x, y, NULL, NULL, GDK_ALL_EVENTS_MASK);
3278 gdk_window_get_user_data (window, (gpointer) &child_widget);
3280 return child_widget;
3285 * hildon_pannable_area_get_hadjustment:
3286 * @area: A #HildonPannableArea.
3288 * Returns the horizontal adjustment. This adjustment is the internal
3289 * widget adjustment used to control the animations. Do not modify it
3290 * directly to change the position of the pannable, to do that use the
3291 * pannable API. If you modify the object directly it could cause
3292 * artifacts in the animations.
3294 * returns: The horizontal #GtkAdjustment
3299 hildon_pannable_area_get_hadjustment (HildonPannableArea *area)
3302 g_return_val_if_fail (HILDON_IS_PANNABLE_AREA (area), NULL);
3304 return area->priv->hadjust;
3308 * hildon_pannable_area_get_vadjustment:
3309 * @area: A #HildonPannableArea.
3311 * Returns the vertical adjustment. This adjustment is the internal
3312 * widget adjustment used to control the animations. Do not modify it
3313 * directly to change the position of the pannable, to do that use the
3314 * pannable API. If you modify the object directly it could cause
3315 * artifacts in the animations.
3317 * returns: The vertical #GtkAdjustment
3322 hildon_pannable_area_get_vadjustment (HildonPannableArea *area)
3324 g_return_val_if_fail (HILDON_IS_PANNABLE_AREA (area), NULL);
3326 return area->priv->vadjust;
3331 * hildon_pannable_area_get_size_request_policy:
3332 * @area: A #HildonPannableArea.
3334 * This function returns the current size request policy of the
3335 * widget. That policy controls the way the size_request is done in
3336 * the pannable area. Check
3337 * hildon_pannable_area_set_size_request_policy() for a more detailed
3340 * returns: the policy is currently being used in the widget
3341 * #HildonSizeRequestPolicy.
3345 HildonSizeRequestPolicy
3346 hildon_pannable_area_get_size_request_policy (HildonPannableArea *area)
3348 HildonPannableAreaPrivate *priv;
3350 g_return_val_if_fail (HILDON_IS_PANNABLE_AREA (area), FALSE);
3354 return priv->size_request_policy;
3358 * hildon_pannable_area_set_size_request_policy:
3359 * @area: A #HildonPannableArea.
3360 * @size_request_policy: One of the allowed #HildonSizeRequestPolicy
3362 * This function sets the pannable area size request policy. That
3363 * policy controls the way the size_request is done in the pannable
3364 * area. Pannable can use the size request of its children
3365 * (#HILDON_SIZE_REQUEST_CHILDREN) or the minimum size required for
3366 * the area itself (#HILDON_SIZE_REQUEST_MINIMUM), the latter is the
3367 * default. Recall this size depends on the scrolling policy you are
3368 * requesting to the pannable area, if you set #GTK_POLICY_NEVER this
3369 * parameter will not have any effect with
3370 * #HILDON_SIZE_REQUEST_MINIMUM set.
3374 * Deprecated: This method and the policy request is deprecated, DO
3375 * NOT use it in future code, the only policy properly supported in
3376 * gtk+ nowadays is the minimum size. Use #gtk_window_set_default_size
3377 * or #gtk_window_set_geometry_hints with the proper size in your case
3378 * to define the height of your dialogs.
3381 hildon_pannable_area_set_size_request_policy (HildonPannableArea *area,
3382 HildonSizeRequestPolicy size_request_policy)
3384 HildonPannableAreaPrivate *priv;
3386 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3390 if (priv->size_request_policy == size_request_policy)
3393 priv->size_request_policy = size_request_policy;
3395 gtk_widget_queue_resize (GTK_WIDGET (area));
3397 g_object_notify (G_OBJECT (area), "size-request-policy");