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;
674 GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
676 GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
678 g_object_ref_sink (G_OBJECT (priv->hadjust));
679 g_object_ref_sink (G_OBJECT (priv->vadjust));
681 g_signal_connect_swapped (priv->hadjust, "value-changed",
682 G_CALLBACK (hildon_pannable_area_adjust_value_changed), area);
683 g_signal_connect_swapped (priv->vadjust, "value-changed",
684 G_CALLBACK (hildon_pannable_area_adjust_value_changed), area);
685 g_signal_connect_swapped (priv->hadjust, "changed",
686 G_CALLBACK (hildon_pannable_area_adjust_changed), area);
687 g_signal_connect_swapped (priv->vadjust, "changed",
688 G_CALLBACK (hildon_pannable_area_adjust_changed), area);
689 g_signal_connect (area, "grab-notify",
690 G_CALLBACK (hildon_pannable_area_grab_notify), NULL);
694 hildon_pannable_area_get_property (GObject * object,
699 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (object)->priv;
701 switch (property_id) {
703 g_value_set_boolean (value, priv->enabled);
706 g_value_set_enum (value, priv->mode);
708 case PROP_MOVEMENT_MODE:
709 g_value_set_flags (value, priv->mov_mode);
711 case PROP_VELOCITY_MIN:
712 g_value_set_double (value, priv->vmin);
714 case PROP_VELOCITY_MAX:
715 g_value_set_double (value, priv->vmax);
717 case PROP_VEL_MAX_OVERSHOOTING:
718 g_value_set_double (value, priv->vmax_overshooting);
720 case PROP_VELOCITY_FAST_FACTOR:
721 g_value_set_double (value, priv->vfast_factor);
723 case PROP_DECELERATION:
724 g_value_set_double (value, priv->decel);
726 case PROP_DRAG_INERTIA:
727 g_value_set_double (value, priv->drag_inertia);
730 g_value_set_uint (value, priv->sps);
732 case PROP_PANNING_THRESHOLD:
733 g_value_set_uint (value, priv->panning_threshold);
735 case PROP_SCROLLBAR_FADE_DELAY:
736 /* convert to miliseconds */
737 g_value_set_uint (value, priv->scrollbar_fade_delay * SCROLL_FADE_TIMEOUT);
739 case PROP_BOUNCE_STEPS:
740 g_value_set_uint (value, priv->bounce_steps);
743 g_value_set_uint (value, priv->force);
745 case PROP_DIRECTION_ERROR_MARGIN:
746 g_value_set_uint (value, priv->direction_error_margin);
748 case PROP_VSCROLLBAR_POLICY:
749 g_value_set_enum (value, priv->vscrollbar_policy);
751 case PROP_HSCROLLBAR_POLICY:
752 g_value_set_enum (value, priv->hscrollbar_policy);
754 case PROP_VOVERSHOOT_MAX:
755 g_value_set_int (value, priv->vovershoot_max);
757 case PROP_HOVERSHOOT_MAX:
758 g_value_set_int (value, priv->hovershoot_max);
760 case PROP_SCROLL_TIME:
761 g_value_set_double (value, priv->scroll_time);
763 case PROP_INITIAL_HINT:
764 g_value_set_boolean (value, priv->initial_hint);
766 case PROP_LOW_FRICTION_MODE:
767 g_value_set_boolean (value, priv->low_friction_mode);
769 case PROP_SIZE_REQUEST_POLICY:
770 g_value_set_enum (value, priv->size_request_policy);
772 case PROP_HADJUSTMENT:
773 g_value_set_object (value,
774 hildon_pannable_area_get_hadjustment
775 (HILDON_PANNABLE_AREA (object)));
777 case PROP_VADJUSTMENT:
778 g_value_set_object (value,
779 hildon_pannable_area_get_vadjustment
780 (HILDON_PANNABLE_AREA (object)));
783 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
788 hildon_pannable_area_set_property (GObject * object,
790 const GValue * value,
793 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (object)->priv;
796 switch (property_id) {
798 enabled = g_value_get_boolean (value);
800 if ((priv->enabled != enabled) && (GTK_WIDGET_REALIZED (object))) {
802 gdk_window_raise (priv->event_window);
804 gdk_window_lower (priv->event_window);
807 priv->enabled = enabled;
810 priv->mode = g_value_get_enum (value);
812 case PROP_MOVEMENT_MODE:
813 priv->mov_mode = g_value_get_flags (value);
815 case PROP_VELOCITY_MIN:
816 priv->vmin = g_value_get_double (value);
818 case PROP_VELOCITY_MAX:
819 priv->vmax = g_value_get_double (value);
821 case PROP_VEL_MAX_OVERSHOOTING:
822 priv->vmax_overshooting = g_value_get_double (value);
824 case PROP_VELOCITY_FAST_FACTOR:
825 priv->vfast_factor = g_value_get_double (value);
827 case PROP_DECELERATION:
828 priv->decel = g_value_get_double (value);
829 hildon_pannable_calculate_vel_factor (HILDON_PANNABLE_AREA (object));
831 case PROP_DRAG_INERTIA:
832 priv->drag_inertia = g_value_get_double (value);
835 priv->sps = g_value_get_uint (value);
837 case PROP_PANNING_THRESHOLD:
839 GtkSettings *settings = gtk_settings_get_default ();
840 GtkSettingsValue svalue = { NULL, { 0, }, };
842 priv->panning_threshold = g_value_get_uint (value);
844 /* insure gtk dnd is the same we are using, not allowed
845 different thresholds in the same application */
846 svalue.origin = "panning_threshold";
847 g_value_init (&svalue.value, G_TYPE_LONG);
848 g_value_set_long (&svalue.value, priv->panning_threshold);
849 gtk_settings_set_property_value (settings, "gtk-dnd-drag-threshold", &svalue);
850 g_value_unset (&svalue.value);
853 case PROP_SCROLLBAR_FADE_DELAY:
854 /* convert to miliseconds */
855 priv->scrollbar_fade_delay = g_value_get_uint (value)/(SCROLL_FADE_TIMEOUT);
857 case PROP_BOUNCE_STEPS:
858 priv->bounce_steps = g_value_get_uint (value);
861 priv->force = g_value_get_uint (value);
863 case PROP_DIRECTION_ERROR_MARGIN:
864 priv->direction_error_margin = g_value_get_uint (value);
866 case PROP_VSCROLLBAR_POLICY:
867 priv->vscrollbar_policy = g_value_get_enum (value);
869 gtk_widget_queue_resize (GTK_WIDGET (object));
871 case PROP_HSCROLLBAR_POLICY:
872 priv->hscrollbar_policy = g_value_get_enum (value);
874 gtk_widget_queue_resize (GTK_WIDGET (object));
876 case PROP_VOVERSHOOT_MAX:
877 priv->vovershoot_max = g_value_get_int (value);
879 case PROP_HOVERSHOOT_MAX:
880 priv->hovershoot_max = g_value_get_int (value);
882 case PROP_SCROLL_TIME:
883 priv->scroll_time = g_value_get_double (value);
885 hildon_pannable_calculate_vel_factor (HILDON_PANNABLE_AREA (object));
887 case PROP_INITIAL_HINT:
888 priv->initial_hint = g_value_get_boolean (value);
890 case PROP_LOW_FRICTION_MODE:
891 priv->low_friction_mode = g_value_get_boolean (value);
893 case PROP_SIZE_REQUEST_POLICY:
894 hildon_pannable_area_set_size_request_policy (HILDON_PANNABLE_AREA (object),
895 g_value_get_enum (value));
899 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
904 hildon_pannable_area_dispose (GObject * object)
906 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (object)->priv;
907 GtkWidget *child = gtk_bin_get_child (GTK_BIN (object));
910 g_source_remove (priv->idle_id);
914 if (priv->scroll_indicator_timeout){
915 g_source_remove (priv->scroll_indicator_timeout);
916 priv->scroll_indicator_timeout = 0;
919 if (priv->motion_event_scroll_timeout){
920 g_source_remove (priv->motion_event_scroll_timeout);
921 priv->motion_event_scroll_timeout = 0;
925 g_signal_handlers_disconnect_by_func (child,
926 hildon_pannable_area_child_mapped,
930 g_signal_handlers_disconnect_by_func (object,
931 hildon_pannable_area_grab_notify,
935 g_signal_handlers_disconnect_by_func (priv->hadjust,
936 hildon_pannable_area_adjust_value_changed,
938 g_signal_handlers_disconnect_by_func (priv->hadjust,
939 hildon_pannable_area_adjust_changed,
941 g_object_unref (priv->hadjust);
942 priv->hadjust = NULL;
946 g_signal_handlers_disconnect_by_func (priv->vadjust,
947 hildon_pannable_area_adjust_value_changed,
949 g_signal_handlers_disconnect_by_func (priv->vadjust,
950 hildon_pannable_area_adjust_changed,
952 g_object_unref (priv->vadjust);
953 priv->vadjust = NULL;
956 if (G_OBJECT_CLASS (hildon_pannable_area_parent_class)->dispose)
957 G_OBJECT_CLASS (hildon_pannable_area_parent_class)->dispose (object);
961 hildon_pannable_area_realize (GtkWidget * widget)
963 GdkWindowAttr attributes;
964 gint attributes_mask;
966 HildonPannableAreaPrivate *priv;
968 priv = HILDON_PANNABLE_AREA (widget)->priv;
970 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
972 border_width = GTK_CONTAINER (widget)->border_width;
974 attributes.x = widget->allocation.x + border_width;
975 attributes.y = widget->allocation.y + border_width;
976 attributes.width = MAX (widget->allocation.width - 2 * border_width, 0);
977 attributes.height = MAX (widget->allocation.height - 2 * border_width, 0);
978 attributes.window_type = GDK_WINDOW_CHILD;
980 /* avoid using the hildon_window */
981 attributes.visual = gtk_widget_get_visual (widget);
982 attributes.colormap = gtk_widget_get_colormap (widget);
983 attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK;
984 attributes.wclass = GDK_INPUT_OUTPUT;
986 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
988 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
989 &attributes, attributes_mask);
990 gdk_window_set_user_data (widget->window, widget);
992 /* create the events window */
995 attributes.event_mask = gtk_widget_get_events (widget)
996 | GDK_BUTTON_MOTION_MASK
997 | GDK_BUTTON_PRESS_MASK
998 | GDK_BUTTON_RELEASE_MASK
1000 | GDK_POINTER_MOTION_HINT_MASK
1001 | GDK_EXPOSURE_MASK | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK;
1002 attributes.wclass = GDK_INPUT_ONLY;
1004 attributes_mask = GDK_WA_X | GDK_WA_Y;
1006 priv->event_window = gdk_window_new (widget->window,
1007 &attributes, attributes_mask);
1008 gdk_window_set_user_data (priv->event_window, widget);
1010 widget->style = gtk_style_attach (widget->style, widget->window);
1011 gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
1013 priv->scrollbars_gc = gdk_gc_new (GDK_DRAWABLE (widget->window));
1014 gdk_gc_copy (priv->scrollbars_gc, widget->style->fg_gc[GTK_STATE_INSENSITIVE]);
1018 hildon_pannable_area_unrealize (GtkWidget * widget)
1020 HildonPannableAreaPrivate *priv;
1022 priv = HILDON_PANNABLE_AREA (widget)->priv;
1024 if (priv->event_window != NULL) {
1025 gdk_window_set_user_data (priv->event_window, NULL);
1026 gdk_window_destroy (priv->event_window);
1027 priv->event_window = NULL;
1030 gdk_gc_unref (priv->scrollbars_gc);
1032 if (GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->unrealize)
1033 (*GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->unrealize)(widget);
1037 hildon_pannable_area_size_request (GtkWidget * widget,
1038 GtkRequisition * requisition)
1040 GtkRequisition child_requisition = {0};
1041 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1042 GtkWidget *child = gtk_bin_get_child (GTK_BIN (widget));
1044 if (child && GTK_WIDGET_VISIBLE (child))
1046 gtk_widget_size_request (child, &child_requisition);
1049 if (priv->hscrollbar_policy == GTK_POLICY_NEVER) {
1050 requisition->width = child_requisition.width;
1052 switch (priv->size_request_policy) {
1053 case HILDON_SIZE_REQUEST_CHILDREN:
1054 requisition->width = MIN (PANNABLE_MAX_WIDTH,
1055 child_requisition.width);
1057 case HILDON_SIZE_REQUEST_MINIMUM:
1059 requisition->width = priv->indicator_width;
1063 if (priv->vscrollbar_policy == GTK_POLICY_NEVER) {
1064 requisition->height = child_requisition.height;
1066 switch (priv->size_request_policy) {
1067 case HILDON_SIZE_REQUEST_CHILDREN:
1068 requisition->height = MIN (PANNABLE_MAX_HEIGHT,
1069 child_requisition.height);
1071 case HILDON_SIZE_REQUEST_MINIMUM:
1073 requisition->height = priv->indicator_width;
1077 requisition->width += 2 * GTK_CONTAINER (widget)->border_width;
1078 requisition->height += 2 * GTK_CONTAINER (widget)->border_width;
1082 hildon_pannable_area_child_allocate_calculate (GtkWidget * widget,
1083 GtkAllocation * allocation,
1084 GtkAllocation * child_allocation)
1087 HildonPannableAreaPrivate *priv;
1089 border_width = GTK_CONTAINER (widget)->border_width;
1091 priv = HILDON_PANNABLE_AREA (widget)->priv;
1093 child_allocation->x = 0;
1094 child_allocation->y = 0;
1095 child_allocation->width = MAX (allocation->width - 2 * border_width -
1096 (priv->vscroll_visible ? priv->vscroll_rect.width : 0), 0);
1097 child_allocation->height = MAX (allocation->height - 2 * border_width -
1098 (priv->hscroll_visible ? priv->hscroll_rect.height : 0), 0);
1100 if (priv->overshot_dist_y > 0) {
1101 child_allocation->y = MIN (child_allocation->y + priv->overshot_dist_y,
1102 child_allocation->height);
1103 child_allocation->height = MAX (child_allocation->height - priv->overshot_dist_y, 0);
1104 } else if (priv->overshot_dist_y < 0) {
1105 child_allocation->height = MAX (child_allocation->height + priv->overshot_dist_y, 0);
1108 if (priv->overshot_dist_x > 0) {
1109 child_allocation->x = MIN (child_allocation->x + priv->overshot_dist_x,
1110 child_allocation->width);
1111 child_allocation->width = MAX (child_allocation->width - priv->overshot_dist_x, 0);
1112 } else if (priv->overshot_dist_x < 0) {
1113 child_allocation->width = MAX (child_allocation->width + priv->overshot_dist_x, 0);
1118 hildon_pannable_area_size_allocate (GtkWidget * widget,
1119 GtkAllocation * allocation)
1121 GtkAllocation child_allocation;
1122 HildonPannableAreaPrivate *priv;
1123 GtkWidget *child = gtk_bin_get_child (GTK_BIN (widget));
1127 border_width = GTK_CONTAINER (widget)->border_width;
1129 widget->allocation = *allocation;
1131 priv = HILDON_PANNABLE_AREA (widget)->priv;
1133 if (GTK_WIDGET_REALIZED (widget)) {
1134 gdk_window_move_resize (widget->window,
1135 allocation->x + border_width,
1136 allocation->y + border_width,
1137 allocation->width - border_width * 2,
1138 allocation->height - border_width * 2);
1139 gdk_window_move_resize (priv->event_window,
1142 allocation->width - border_width * 2,
1143 allocation->height - border_width * 2);
1146 if (child && GTK_WIDGET_VISIBLE (child)) {
1148 hildon_pannable_area_child_allocate_calculate (widget,
1152 gtk_widget_size_allocate (child, &child_allocation);
1154 if (hildon_pannable_area_check_scrollbars (HILDON_PANNABLE_AREA (widget))) {
1155 hildon_pannable_area_child_allocate_calculate (widget,
1159 gtk_widget_size_allocate (child, &child_allocation);
1162 hv = priv->hadjust->value;
1163 vv = priv->vadjust->value;
1165 /* we have to do this after child size_allocate because page_size is
1166 * changed when we allocate the size of the children */
1167 if (priv->overshot_dist_y < 0) {
1168 priv->vadjust->value = priv->vadjust->upper - priv->vadjust->page_size;
1171 if (priv->overshot_dist_x < 0) {
1172 priv->hadjust->value = priv->hadjust->upper - priv->hadjust->page_size;
1175 if (hv != priv->hadjust->value)
1176 gtk_adjustment_value_changed (priv->hadjust);
1178 if (vv != priv->vadjust->value)
1179 gtk_adjustment_value_changed (priv->vadjust);
1182 hildon_pannable_area_check_scrollbars (HILDON_PANNABLE_AREA (widget));
1187 hildon_pannable_area_style_set (GtkWidget * widget,
1188 GtkStyle * previous_style)
1190 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1192 GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->
1193 style_set (widget, previous_style);
1195 gtk_widget_style_get (widget, "indicator-width", &priv->indicator_width, NULL);
1199 hildon_pannable_area_map (GtkWidget * widget)
1201 HildonPannableAreaPrivate *priv;
1203 priv = HILDON_PANNABLE_AREA (widget)->priv;
1205 gdk_window_show (widget->window);
1207 if (priv->event_window != NULL && !priv->enabled)
1208 gdk_window_show (priv->event_window);
1210 (*GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->map) (widget);
1212 if (priv->event_window != NULL && priv->enabled)
1213 gdk_window_show (priv->event_window);
1217 hildon_pannable_area_unmap (GtkWidget * widget)
1219 HildonPannableAreaPrivate *priv;
1221 priv = HILDON_PANNABLE_AREA (widget)->priv;
1223 if (priv->event_window != NULL)
1224 gdk_window_hide (priv->event_window);
1226 gdk_window_hide (widget->window);
1228 (*GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->unmap) (widget);
1232 hildon_pannable_area_grab_notify (GtkWidget *widget,
1233 gboolean was_grabbed,
1236 /* an internal widget has grabbed the focus and now has returned it,
1237 we have to do some release actions */
1239 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1241 priv->scroll_indicator_event_interrupt = 0;
1243 if ((!priv->scroll_indicator_timeout)&&(priv->scroll_indicator_alpha)>0.1) {
1244 priv->scroll_delay_counter = priv->scrollbar_fade_delay;
1246 hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget),
1247 priv->scroll_indicator_alpha);
1250 priv->last_type = 3;
1251 priv->moved = FALSE;
1255 #if USE_CAIRO_SCROLLBARS == 1
1258 rgb_from_gdkcolor (GdkColor *color, gdouble *r, gdouble *g, gdouble *b)
1260 *r = (color->red >> 8) / 255.0;
1261 *g = (color->green >> 8) / 255.0;
1262 *b = (color->blue >> 8) / 255.0;
1266 hildon_pannable_draw_vscroll (GtkWidget * widget,
1267 GdkColor *back_color,
1268 GdkColor *scroll_color)
1270 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1273 cairo_pattern_t *pattern;
1275 gint radius = (priv->vscroll_rect.width/2) - 1;
1277 cr = gdk_cairo_create(widget->window);
1279 /* Draw the background */
1280 rgb_from_gdkcolor (back_color, &r, &g, &b);
1281 cairo_set_source_rgb (cr, r, g, b);
1282 cairo_rectangle(cr, priv->vscroll_rect.x, priv->vscroll_rect.y,
1283 priv->vscroll_rect.width,
1284 priv->vscroll_rect.height);
1285 cairo_fill_preserve (cr);
1288 /* Calculate the scroll bar height and position */
1289 y = ((priv->vadjust->value - priv->vadjust->lower) / (priv->vadjust->upper - priv->vadjust->lower)) *
1290 (widget->allocation.height -
1291 (priv->hscroll_visible ? priv->indicator_width : 0));
1292 height = ((((priv->vadjust->value - priv->vadjust->lower) +
1293 priv->vadjust->page_size) /
1294 (priv->vadjust->upper - priv->vadjust->lower)) *
1295 (widget->allocation.height -
1296 (priv->hscroll_visible ? priv->indicator_width : 0))) - y;
1298 /* Set a minimum height */
1299 height = MAX (SCROLL_BAR_MIN_SIZE, height);
1301 /* Check the max y position */
1302 y = MIN (y, widget->allocation.height -
1303 (priv->hscroll_visible ? priv->hscroll_rect.height : 0) -
1306 /* Draw the scrollbar */
1307 rgb_from_gdkcolor (scroll_color, &r, &g, &b);
1309 pattern = cairo_pattern_create_linear(radius+1, y, radius+1,y + height);
1310 cairo_pattern_add_color_stop_rgb(pattern, 0, r, g, b);
1311 cairo_pattern_add_color_stop_rgb(pattern, 1, r/2, g/2, b/2);
1312 cairo_set_source(cr, pattern);
1314 cairo_pattern_destroy(pattern);
1316 cairo_arc(cr, priv->vscroll_rect.x + radius + 1, y + radius + 1, radius, G_PI, 0);
1317 cairo_line_to(cr, priv->vscroll_rect.x + (radius * 2) + 1, y + height - radius);
1318 cairo_arc(cr, priv->vscroll_rect.x + radius + 1, y + height - radius, radius, 0, G_PI);
1319 cairo_line_to(cr, priv->vscroll_rect.x + 1, y + height - radius);
1322 cairo_paint_with_alpha(cr, priv->scroll_indicator_alpha);
1328 hildon_pannable_draw_hscroll (GtkWidget * widget,
1329 GdkColor *back_color,
1330 GdkColor *scroll_color)
1332 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1335 cairo_pattern_t *pattern;
1337 gint radius = (priv->hscroll_rect.height/2) - 1;
1339 cr = gdk_cairo_create(widget->window);
1341 /* Draw the background */
1342 rgb_from_gdkcolor (back_color, &r, &g, &b);
1343 cairo_set_source_rgb (cr, r, g, b);
1344 cairo_rectangle(cr, priv->hscroll_rect.x, priv->hscroll_rect.y,
1345 priv->hscroll_rect.width,
1346 priv->hscroll_rect.height);
1347 cairo_fill_preserve (cr);
1350 /* calculate the scrollbar width and position */
1351 x = ((priv->hadjust->value - priv->hadjust->lower) / (priv->hadjust->upper - priv->hadjust->lower)) *
1352 (widget->allocation.width - (priv->vscroll_visible ? priv->indicator_width : 0));
1353 width =((((priv->hadjust->value - priv->hadjust->lower) +
1354 priv->hadjust->page_size) / (priv->hadjust->upper - priv->hadjust->lower)) *
1355 (widget->allocation.width -
1356 (priv->vscroll_visible ? priv->indicator_width : 0))) - x;
1358 /* Set a minimum width */
1359 width = MAX (SCROLL_BAR_MIN_SIZE, width);
1361 /* Check the max x position */
1362 x = MIN (x, widget->allocation.width -
1363 (priv->vscroll_visible ? priv->vscroll_rect.width : 0) -
1366 /* Draw the scrollbar */
1367 rgb_from_gdkcolor (scroll_color, &r, &g, &b);
1369 pattern = cairo_pattern_create_linear(x, radius+1, x+width, radius+1);
1370 cairo_pattern_add_color_stop_rgb(pattern, 0, r, g, b);
1371 cairo_pattern_add_color_stop_rgb(pattern, 1, r/2, g/2, b/2);
1372 cairo_set_source(cr, pattern);
1374 cairo_pattern_destroy(pattern);
1376 cairo_arc_negative(cr, x + radius + 1, priv->hscroll_rect.y + radius + 1, radius, 3*G_PI_2, G_PI_2);
1377 cairo_line_to(cr, x + width - radius, priv->hscroll_rect.y + (radius * 2) + 1);
1378 cairo_arc_negative(cr, x + width - radius, priv->hscroll_rect.y + radius + 1, radius, G_PI_2, 3*G_PI_2);
1379 cairo_line_to(cr, x + width - radius, priv->hscroll_rect.y + 1);
1382 cairo_paint_with_alpha(cr, priv->scroll_indicator_alpha);
1387 #else /* USE_CAIRO_SCROLLBARS */
1390 tranparency_color (GdkColor *color,
1393 gdouble transparency)
1397 diff = colora.red - colorb.red;
1398 color->red = colora.red-diff*transparency;
1400 diff = colora.green - colorb.green;
1401 color->green = colora.green-diff*transparency;
1403 diff = colora.blue - colorb.blue;
1404 color->blue = colora.blue-diff*transparency;
1408 hildon_pannable_draw_vscroll (GtkWidget *widget,
1409 GdkColor *back_color,
1410 GdkColor *scroll_color)
1412 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1414 GdkColor transp_color;
1415 GdkGC *gc = widget->style->fg_gc[GTK_STATE_INSENSITIVE];
1417 gdk_draw_rectangle (widget->window,
1418 widget->style->bg_gc[GTK_STATE_NORMAL],
1420 priv->vscroll_rect.x, priv->vscroll_rect.y,
1421 priv->vscroll_rect.width,
1422 priv->vscroll_rect.height);
1424 y = ((priv->vadjust->value - priv->vadjust->lower) / (priv->vadjust->upper - priv->vadjust->lower)) *
1425 (widget->allocation.height - (priv->hscroll_visible ? priv->indicator_width : 0));
1426 height = ((((priv->vadjust->value - priv->vadjust->lower) + priv->vadjust->page_size) /
1427 (priv->vadjust->upper - priv->vadjust->lower)) *
1428 (widget->allocation.height -
1429 (priv->hscroll_visible ? priv->indicator_width : 0))) - y;
1431 /* Set a minimum height */
1432 height = MAX (SCROLL_BAR_MIN_SIZE, height);
1434 /* Check the max y position */
1435 y = MIN (y, widget->allocation.height -
1436 (priv->hscroll_visible ? priv->hscroll_rect.height : 0) -
1439 if (priv->scroll_indicator_alpha < 1.0) {
1440 tranparency_color (&transp_color, *back_color, *scroll_color,
1441 priv->scroll_indicator_alpha);
1443 gdk_gc_set_rgb_fg_color (priv->scrollbars_gc, &transp_color);
1445 gc = priv->scrollbars_gc;
1448 gdk_draw_rectangle (widget->window, gc,
1449 TRUE, priv->vscroll_rect.x, y,
1450 priv->vscroll_rect.width, height);
1454 hildon_pannable_draw_hscroll (GtkWidget *widget,
1455 GdkColor *back_color,
1456 GdkColor *scroll_color)
1458 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1460 GdkColor transp_color;
1461 GdkGC *gc = widget->style->fg_gc[GTK_STATE_INSENSITIVE];
1463 gdk_draw_rectangle (widget->window,
1464 widget->style->bg_gc[GTK_STATE_INSENSITIVE],
1466 priv->hscroll_rect.x, priv->hscroll_rect.y,
1467 priv->hscroll_rect.width,
1468 priv->hscroll_rect.height);
1470 /* calculate the scrollbar width and position */
1471 x = ((priv->hadjust->value - priv->hadjust->lower) / (priv->hadjust->upper - priv->hadjust->lower)) *
1472 (widget->allocation.width - (priv->vscroll_visible ? priv->indicator_width : 0));
1473 width =((((priv->hadjust->value - priv->hadjust->lower) +
1474 priv->hadjust->page_size) / (priv->hadjust->upper - priv->hadjust->lower)) *
1475 (widget->allocation.width -
1476 (priv->vscroll_visible ? priv->indicator_width : 0))) - x;
1478 /* Set a minimum width */
1479 width = MAX (SCROLL_BAR_MIN_SIZE, width);
1481 /* Check the max x position */
1482 x = MIN (x, widget->allocation.width -
1483 (priv->vscroll_visible ? priv->vscroll_rect.width : 0) -
1486 if (priv->scroll_indicator_alpha < 1.0) {
1487 tranparency_color (&transp_color, *back_color, *scroll_color,
1488 priv->scroll_indicator_alpha);
1490 gdk_gc_set_rgb_fg_color (priv->scrollbars_gc, &transp_color);
1492 gc = priv->scrollbars_gc;
1495 gdk_draw_rectangle (widget->window, gc,
1496 TRUE, x, priv->hscroll_rect.y, width,
1497 priv->hscroll_rect.height);
1500 #endif /* USE_CAIRO_SCROLLBARS */
1503 hildon_pannable_area_initial_effect (GtkWidget * widget)
1505 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1507 if (priv->initial_hint) {
1508 if (priv->vscroll_visible || priv->hscroll_visible) {
1510 priv->scroll_indicator_event_interrupt = 0;
1511 priv->scroll_delay_counter = priv->scrollbar_fade_delay;
1513 hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget), 1.0);
1519 hildon_pannable_area_launch_fade_timeout (HildonPannableArea * area,
1522 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (area)->priv;
1524 priv->scroll_indicator_alpha = alpha;
1526 if (!priv->scroll_indicator_timeout)
1527 priv->scroll_indicator_timeout =
1528 gdk_threads_add_timeout (SCROLL_FADE_TIMEOUT,
1529 (GSourceFunc) hildon_pannable_area_scroll_indicator_fade,
1534 hildon_pannable_area_adjust_changed (HildonPannableArea * area,
1537 if (GTK_WIDGET_REALIZED (area))
1538 hildon_pannable_area_refresh (area);
1542 hildon_pannable_area_adjust_value_changed (HildonPannableArea * area,
1545 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (area)->priv;
1547 gint x = priv->x_offset;
1548 gint y = priv->y_offset;
1550 priv->x_offset = priv->hadjust->value;
1551 xdiff = x - priv->x_offset;
1552 priv->y_offset = priv->vadjust->value;
1553 ydiff = y - priv->y_offset;
1555 if ((xdiff || ydiff) && GTK_WIDGET_DRAWABLE (area)) {
1556 hildon_pannable_area_redraw (area);
1558 if ((priv->vscroll_visible) || (priv->hscroll_visible)) {
1559 priv->scroll_indicator_event_interrupt = 0;
1560 priv->scroll_delay_counter = priv->scrollbar_fade_delay;
1562 hildon_pannable_area_launch_fade_timeout (area, 1.0);
1568 hildon_pannable_area_redraw (HildonPannableArea * area)
1570 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (area)->priv;
1572 /* Redraw scroll indicators */
1573 if (GTK_WIDGET_DRAWABLE (area)) {
1574 if (priv->hscroll_visible) {
1575 gdk_window_invalidate_rect (GTK_WIDGET (area)->window,
1576 &priv->hscroll_rect, FALSE);
1579 if (priv->vscroll_visible) {
1580 gdk_window_invalidate_rect (GTK_WIDGET (area)->window,
1581 &priv->vscroll_rect, FALSE);
1587 hildon_pannable_area_scroll_indicator_fade(HildonPannableArea * area)
1589 HildonPannableAreaPrivate *priv = area->priv;
1591 /* if moving do not fade out */
1592 if (((ABS (priv->vel_y)>priv->vmin)||
1593 (ABS (priv->vel_x)>priv->vmin))&&(!priv->button_pressed)) {
1598 if (priv->scroll_indicator_event_interrupt) {
1599 /* Stop a fade out, and fade back in */
1600 if (priv->scroll_indicator_alpha > 0.9) {
1601 priv->scroll_indicator_alpha = 1.0;
1602 priv->scroll_indicator_timeout = 0;
1606 priv->scroll_indicator_alpha += 0.2;
1607 hildon_pannable_area_redraw (area);
1613 if ((priv->scroll_indicator_alpha > 0.9) &&
1614 (priv->scroll_delay_counter > 0)) {
1615 priv->scroll_delay_counter--;
1620 if (!priv->scroll_indicator_event_interrupt) {
1621 /* Continue fade out */
1622 if (priv->scroll_indicator_alpha < 0.1) {
1623 priv->scroll_indicator_timeout = 0;
1624 priv->scroll_indicator_alpha = 0.0;
1628 priv->scroll_indicator_alpha -= 0.2;
1629 hildon_pannable_area_redraw (area);
1639 hildon_pannable_area_expose_event (GtkWidget * widget,
1640 GdkEventExpose * event)
1643 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1644 #if USE_CAIRO_SCROLLBARS == 1
1645 GdkColor back_color = widget->style->bg[GTK_STATE_NORMAL];
1646 GdkColor scroll_color = widget->style->base[GTK_STATE_SELECTED];
1647 #else /* USE_CAIRO_SCROLLBARS */
1648 GdkColor back_color = widget->style->bg[GTK_STATE_NORMAL];
1649 GdkColor scroll_color = widget->style->fg[GTK_STATE_INSENSITIVE];
1652 if (G_UNLIKELY (priv->initial_effect)) {
1653 hildon_pannable_area_initial_effect (widget);
1655 priv->initial_effect = FALSE;
1658 if (gtk_bin_get_child (GTK_BIN (widget))) {
1660 if (priv->scroll_indicator_alpha > 0.1) {
1661 if (priv->vscroll_visible) {
1662 hildon_pannable_draw_vscroll (widget, &back_color, &scroll_color);
1664 if (priv->hscroll_visible) {
1665 hildon_pannable_draw_hscroll (widget, &back_color, &scroll_color);
1669 /* draw overshooting rectangles */
1670 if (priv->overshot_dist_y > 0) {
1671 gint overshot_height;
1673 overshot_height = MIN (priv->overshot_dist_y, widget->allocation.height -
1674 (priv->hscroll_visible ? priv->hscroll_rect.height : 0));
1676 gdk_draw_rectangle (widget->window,
1677 widget->style->bg_gc[GTK_STATE_NORMAL],
1681 widget->allocation.width -
1682 (priv->vscroll_visible ? priv->vscroll_rect.width : 0),
1684 } else if (priv->overshot_dist_y < 0) {
1685 gint overshot_height;
1689 MAX (priv->overshot_dist_y,
1690 -(widget->allocation.height -
1691 (priv->hscroll_visible ? priv->hscroll_rect.height : 0)));
1693 overshot_y = MAX (widget->allocation.height +
1695 (priv->hscroll_visible ? priv->hscroll_rect.height : 0), 0);
1697 gdk_draw_rectangle (widget->window,
1698 widget->style->bg_gc[GTK_STATE_NORMAL],
1702 widget->allocation.width -
1703 priv->vscroll_rect.width,
1707 if (priv->overshot_dist_x > 0) {
1708 gint overshot_width;
1710 overshot_width = MIN (priv->overshot_dist_x, widget->allocation.width -
1711 (priv->vscroll_visible ? priv->vscroll_rect.width : 0));
1713 gdk_draw_rectangle (widget->window,
1714 widget->style->bg_gc[GTK_STATE_NORMAL],
1719 widget->allocation.height -
1720 (priv->hscroll_visible ? priv->hscroll_rect.height : 0));
1721 } else if (priv->overshot_dist_x < 0) {
1722 gint overshot_width;
1726 MAX (priv->overshot_dist_x,
1727 -(widget->allocation.width -
1728 (priv->vscroll_visible ? priv->vscroll_rect.width : 0)));
1730 overshot_x = MAX (widget->allocation.width +
1732 (priv->vscroll_visible ? priv->vscroll_rect.width : 0), 0);
1734 gdk_draw_rectangle (widget->window,
1735 widget->style->bg_gc[GTK_STATE_NORMAL],
1740 widget->allocation.height -
1741 priv->hscroll_rect.height);
1746 return GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->expose_event (widget, event);
1750 hildon_pannable_area_get_topmost (GdkWindow * window,
1752 gint * tx, gint * ty,
1755 /* Find the GdkWindow at the given point, by recursing from a given
1756 * parent GdkWindow. Optionally return the co-ordinates transformed
1757 * relative to the child window.
1760 GList *c, *children;
1761 GdkWindow *selected_window = NULL;
1763 gdk_drawable_get_size (GDK_DRAWABLE (window), &width, &height);
1764 if ((x < 0) || (x >= width) || (y < 0) || (y >= height))
1767 children = gdk_window_peek_children (window);
1774 selected_window = window;
1777 for (c = children; c; c = c->next) {
1778 GdkWindow *child = (GdkWindow *) c->data;
1781 gdk_drawable_get_size (GDK_DRAWABLE (child), &width, &height);
1782 gdk_window_get_position (child, &wx, &wy);
1784 if ((x >= wx) && (x < (wx + width)) && (y >= wy) && (y < (wy + height)) &&
1785 (gdk_window_is_visible (child))) {
1787 if (gdk_window_peek_children (child)) {
1788 selected_window = hildon_pannable_area_get_topmost (child, x-wx, y-wy,
1790 if (!selected_window) {
1795 selected_window = child;
1798 if ((gdk_window_get_events (child)&mask)) {
1803 selected_window = child;
1809 return selected_window;
1813 synth_crossing (GdkWindow * child,
1815 gint x_root, gint y_root,
1816 guint32 time, gboolean in)
1818 GdkEventCrossing *crossing_event;
1819 GdkEventType type = in ? GDK_ENTER_NOTIFY : GDK_LEAVE_NOTIFY;
1821 /* Send synthetic enter event */
1822 crossing_event = (GdkEventCrossing *) gdk_event_new (type);
1823 ((GdkEventAny *) crossing_event)->type = type;
1824 ((GdkEventAny *) crossing_event)->window = g_object_ref (child);
1825 ((GdkEventAny *) crossing_event)->send_event = FALSE;
1826 crossing_event->subwindow = g_object_ref (child);
1827 crossing_event->time = time;
1828 crossing_event->x = x;
1829 crossing_event->y = y;
1830 crossing_event->x_root = x_root;
1831 crossing_event->y_root = y_root;
1832 crossing_event->mode = GDK_CROSSING_NORMAL;
1833 crossing_event->detail = GDK_NOTIFY_UNKNOWN;
1834 crossing_event->focus = FALSE;
1835 crossing_event->state = 0;
1836 gdk_event_put ((GdkEvent *) crossing_event);
1837 gdk_event_free ((GdkEvent *) crossing_event);
1841 hildon_pannable_area_button_press_cb (GtkWidget * widget,
1842 GdkEventButton * event)
1845 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1847 if ((!priv->enabled) || (event->button != 1) ||
1848 ((event->time == priv->last_time) &&
1849 (priv->last_type == 1)) || (gtk_bin_get_child (GTK_BIN (widget)) == NULL))
1852 priv->scroll_indicator_event_interrupt = 1;
1854 hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget),
1855 priv->scroll_indicator_alpha);
1857 priv->last_time = event->time;
1858 priv->last_type = 1;
1860 priv->scroll_to_x = -1;
1861 priv->scroll_to_y = -1;
1863 if (priv->button_pressed && priv->child) {
1864 /* Widget stole focus on last click, send crossing-out event */
1865 synth_crossing (priv->child, 0, 0, event->x_root, event->y_root,
1866 event->time, FALSE);
1874 /* Don't allow a click if we're still moving fast */
1875 if ((ABS (priv->vel_x) <= (priv->vmax * priv->vfast_factor)) &&
1876 (ABS (priv->vel_y) <= (priv->vmax * priv->vfast_factor)))
1878 hildon_pannable_area_get_topmost (gtk_bin_get_child (GTK_BIN (widget))->window,
1879 event->x, event->y, &x, &y, GDK_BUTTON_PRESS_MASK);
1883 priv->button_pressed = TRUE;
1885 /* Stop scrolling on mouse-down (so you can flick, then hold to stop) */
1891 gdk_drawable_get_size (priv->child, &priv->child_width,
1892 &priv->child_height);
1893 priv->last_in = TRUE;
1895 g_object_add_weak_pointer ((GObject *) priv->child,
1896 (gpointer) & priv->child);
1898 event = (GdkEventButton *) gdk_event_copy ((GdkEvent *) event);
1904 synth_crossing (priv->child, x, y, event->x_root,
1905 event->y_root, event->time, TRUE);
1907 /* Send synthetic click (button press/release) event */
1908 ((GdkEventAny *) event)->window = g_object_ref (priv->child);
1910 gdk_event_put ((GdkEvent *) event);
1911 gdk_event_free ((GdkEvent *) event);
1919 hildon_pannable_area_check_scrollbars (HildonPannableArea * area)
1921 HildonPannableAreaPrivate *priv = area->priv;
1922 gboolean prev_hscroll_visible, prev_vscroll_visible;
1924 prev_hscroll_visible = priv->hscroll_visible;
1925 prev_vscroll_visible = priv->vscroll_visible;
1927 if (!gtk_bin_get_child (GTK_BIN (area))) {
1928 priv->vscroll_visible = FALSE;
1929 priv->hscroll_visible = FALSE;
1931 switch (priv->hscrollbar_policy) {
1932 case GTK_POLICY_ALWAYS:
1933 priv->hscroll_visible = TRUE;
1935 case GTK_POLICY_NEVER:
1936 priv->hscroll_visible = FALSE;
1939 priv->hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
1940 priv->hadjust->page_size);
1943 switch (priv->vscrollbar_policy) {
1944 case GTK_POLICY_ALWAYS:
1945 priv->vscroll_visible = TRUE;
1947 case GTK_POLICY_NEVER:
1948 priv->vscroll_visible = FALSE;
1951 priv->vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
1952 priv->vadjust->page_size);
1955 /* Store the vscroll/hscroll areas for redrawing */
1956 if (priv->vscroll_visible) {
1957 GtkAllocation *allocation = >K_WIDGET (area)->allocation;
1958 priv->vscroll_rect.x = allocation->width - priv->indicator_width;
1959 priv->vscroll_rect.y = 0;
1960 priv->vscroll_rect.width = priv->indicator_width;
1961 priv->vscroll_rect.height = allocation->height -
1962 (priv->hscroll_visible ? priv->indicator_width : 0);
1964 if (priv->hscroll_visible) {
1965 GtkAllocation *allocation = >K_WIDGET (area)->allocation;
1966 priv->hscroll_rect.y = allocation->height - priv->indicator_width;
1967 priv->hscroll_rect.x = 0;
1968 priv->hscroll_rect.height = priv->indicator_width;
1969 priv->hscroll_rect.width = allocation->width -
1970 (priv->vscroll_visible ? priv->indicator_width : 0);
1974 return ((priv->hscroll_visible != prev_hscroll_visible) ||
1975 (priv->vscroll_visible != prev_vscroll_visible));
1979 hildon_pannable_area_refresh (HildonPannableArea * area)
1981 if (GTK_WIDGET_DRAWABLE (area) &&
1982 hildon_pannable_area_check_scrollbars (area)) {
1983 HildonPannableAreaPrivate *priv = area->priv;
1985 gtk_widget_queue_resize (GTK_WIDGET (area));
1987 if ((priv->vscroll_visible) || (priv->hscroll_visible)) {
1988 priv->scroll_indicator_event_interrupt = 0;
1989 priv->scroll_delay_counter = area->priv->scrollbar_fade_delay;
1991 hildon_pannable_area_launch_fade_timeout (area, 1.0);
1994 hildon_pannable_area_redraw (area);
1998 /* Scroll by a particular amount (in pixels). Optionally, return if
1999 * the scroll on a particular axis was successful.
2002 hildon_pannable_axis_scroll (HildonPannableArea *area,
2003 GtkAdjustment *adjust,
2007 gint *overshot_dist,
2013 HildonPannableAreaPrivate *priv = area->priv;
2015 dist = gtk_adjustment_get_value (adjust) - inc;
2018 * We use overshot_dist to define the distance of the current overshoot,
2019 * and overshooting to define the direction/whether or not we are overshot
2021 if (!(*overshooting)) {
2023 /* Initiation of the overshoot happens when the finger is released
2024 * and the current position of the pannable contents are out of range
2026 if (dist < adjust->lower) {
2029 dist = adjust->lower;
2031 if (overshoot_max!=0) {
2034 *overshot_dist = CLAMP (*overshot_dist + *vel, 0, overshoot_max);
2035 *vel = MIN (priv->vmax_overshooting, *vel);
2036 gtk_widget_queue_resize (GTK_WIDGET (area));
2040 } else if (dist > adjust->upper - adjust->page_size) {
2043 dist = adjust->upper - adjust->page_size;
2045 if (overshoot_max!=0) {
2048 *overshot_dist = CLAMP (*overshot_dist + *vel, -overshoot_max, 0);
2049 *vel = MAX (-priv->vmax_overshooting, *vel);
2050 gtk_widget_queue_resize (GTK_WIDGET (area));
2055 if ((*scroll_to) != -1) {
2056 if (((inc < 0)&&(*scroll_to <= dist))||
2057 ((inc > 0)&&(*scroll_to >= dist))) {
2065 adjust->value = dist;
2067 if (!priv->button_pressed) {
2069 /* When the overshoot has started we continue for
2070 * PROP_BOUNCE_STEPS more steps into the overshoot before we
2071 * reverse direction. The deceleration factor is calculated
2072 * based on the percentage distance from the first item with
2073 * each iteration, therefore always returning us to the
2074 * top/bottom most element
2076 if (*overshot_dist > 0) {
2078 if ((*overshooting < priv->bounce_steps) && (*vel > 0)) {
2080 *vel = (((gdouble)*overshot_dist)/overshoot_max) * (*vel);
2081 } else if ((*overshooting >= priv->bounce_steps) && (*vel > 0)) {
2083 } else if ((*overshooting > 1) && (*vel < 0)) {
2084 /* we add the MIN in order to avoid very small speeds */
2085 *vel = MIN ((((gdouble)*overshot_dist)*0.4) * -1, -2.0);
2088 *overshot_dist = CLAMP (*overshot_dist + *vel, 0, overshoot_max);
2090 gtk_widget_queue_resize (GTK_WIDGET (area));
2092 } else if (*overshot_dist < 0) {
2094 if ((*overshooting < priv->bounce_steps) && (*vel < 0)) {
2096 *vel = (((gdouble)*overshot_dist)/overshoot_max) * (*vel) * -1;
2097 } else if ((*overshooting >= priv->bounce_steps) && (*vel < 0)) {
2099 } else if ((*overshooting > 1) && (*vel > 0)) {
2100 /* we add the MAX in order to avoid very small speeds */
2101 *vel = MAX ((((gdouble)*overshot_dist)*0.4) * -1, 2.0);
2104 *overshot_dist = CLAMP (*overshot_dist + (*vel), -overshoot_max, 0);
2106 gtk_widget_queue_resize (GTK_WIDGET (area));
2111 gtk_widget_queue_resize (GTK_WIDGET (area));
2115 gint overshot_dist_old = *overshot_dist;
2117 if (*overshot_dist > 0) {
2118 *overshot_dist = CLAMP ((*overshot_dist) + inc, 0, overshoot_max);
2119 } else if (*overshot_dist < 0) {
2120 *overshot_dist = CLAMP ((*overshot_dist) + inc, -1 * overshoot_max, 0);
2123 adjust->value = CLAMP (dist,
2129 if (*overshot_dist != overshot_dist_old)
2130 gtk_widget_queue_resize (GTK_WIDGET (area));
2136 hildon_pannable_area_scroll (HildonPannableArea *area,
2137 gdouble x, gdouble y)
2140 HildonPannableAreaPrivate *priv = area->priv;
2141 gboolean hscroll_visible, vscroll_visible;
2144 if (gtk_bin_get_child (GTK_BIN (area)) == NULL)
2147 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
2148 priv->vadjust->page_size);
2149 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
2150 priv->hadjust->page_size);
2155 hv = priv->hadjust->value;
2156 vv = priv->vadjust->value;
2158 if (vscroll_visible) {
2159 hildon_pannable_axis_scroll (area, priv->vadjust, &priv->vel_y, y,
2160 &priv->overshooting_y, &priv->overshot_dist_y,
2161 &priv->scroll_to_y, priv->vovershoot_max, &sy);
2166 if (hscroll_visible) {
2167 hildon_pannable_axis_scroll (area, priv->hadjust, &priv->vel_x, x,
2168 &priv->overshooting_x, &priv->overshot_dist_x,
2169 &priv->scroll_to_x, priv->hovershoot_max, &sx);
2174 if (hv != priv->hadjust->value)
2175 gtk_adjustment_value_changed (priv->hadjust);
2177 if (vv != priv->vadjust->value)
2178 gtk_adjustment_value_changed (priv->vadjust);
2180 /* If the scroll on a particular axis wasn't succesful, reset the
2181 * initial scroll position to the new mouse co-ordinate. This means
2182 * when you get to the top of the page, dragging down works immediately.
2184 if (priv->mode == HILDON_PANNABLE_AREA_MODE_ACCEL) {
2196 hildon_pannable_area_timeout (HildonPannableArea * area)
2198 HildonPannableAreaPrivate *priv = area->priv;
2200 if ((!priv->enabled) || (priv->mode == HILDON_PANNABLE_AREA_MODE_PUSH)) {
2206 if (!priv->button_pressed) {
2207 /* Decelerate gradually when pointer is raised */
2208 if ((!priv->overshot_dist_y) &&
2209 (!priv->overshot_dist_x)) {
2211 /* in case we move to a specific point do not decelerate when arriving */
2212 if ((priv->scroll_to_x != -1)||(priv->scroll_to_y != -1)) {
2214 if (ABS (priv->vel_x) >= 1.5) {
2215 priv->vel_x *= priv->decel;
2218 if (ABS (priv->vel_y) >= 1.5) {
2219 priv->vel_y *= priv->decel;
2223 if ((!priv->low_friction_mode) ||
2224 ((priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ) &&
2225 (ABS (priv->vel_x) < 0.8*priv->vmax)))
2226 priv->vel_x *= priv->decel;
2228 if ((!priv->low_friction_mode) ||
2229 ((priv->mov_mode&HILDON_MOVEMENT_MODE_VERT) &&
2230 (ABS (priv->vel_y) < 0.8*priv->vmax)))
2231 priv->vel_y *= priv->decel;
2233 if ((ABS (priv->vel_x) < 1.0) && (ABS (priv->vel_y) < 1.0)) {
2242 } else if (priv->mode == HILDON_PANNABLE_AREA_MODE_AUTO) {
2248 hildon_pannable_area_scroll (area, priv->vel_x, priv->vel_y);
2254 hildon_pannable_area_calculate_velocity (gdouble *vel,
2258 gdouble drag_inertia,
2264 if (ABS (dist) >= RATIO_TOLERANCE) {
2265 rawvel = (dist / ABS (delta)) * force;
2266 *vel = *vel * (1 - drag_inertia) +
2267 rawvel * drag_inertia;
2268 *vel = *vel > 0 ? MIN (*vel, vmax)
2269 : MAX (*vel, -1 * vmax);
2274 hildon_pannable_area_motion_event_scroll_timeout (HildonPannableArea *area)
2276 HildonPannableAreaPrivate *priv = area->priv;
2278 if ((priv->motion_x != 0)||(priv->motion_y != 0))
2279 hildon_pannable_area_scroll (area, priv->motion_x, priv->motion_y);
2281 priv->motion_event_scroll_timeout = 0;
2287 hildon_pannable_area_motion_event_scroll (HildonPannableArea *area,
2288 gdouble x, gdouble y)
2290 HildonPannableAreaPrivate *priv = area->priv;
2292 if (priv->motion_event_scroll_timeout) {
2294 priv->motion_x += x;
2295 priv->motion_y += y;
2299 /* we do not delay the first event but the next ones */
2300 hildon_pannable_area_scroll (area, x, y);
2305 priv->motion_event_scroll_timeout = gdk_threads_add_timeout
2306 ((gint) (1000.0 / (gdouble) MOTION_EVENTS_PER_SECOND),
2307 (GSourceFunc) hildon_pannable_area_motion_event_scroll_timeout, area);
2312 hildon_pannable_area_check_move (HildonPannableArea *area,
2313 GdkEventMotion * event,
2317 HildonPannableAreaPrivate *priv = area->priv;
2319 if (priv->first_drag && (!priv->moved) &&
2320 ((ABS (*x) > (priv->panning_threshold))
2321 || (ABS (*y) > (priv->panning_threshold)))) {
2326 if (priv->first_drag) {
2327 gboolean vscroll_visible;
2328 gboolean hscroll_visible;
2330 if (ABS (priv->iy - event->y) >=
2331 ABS (priv->ix - event->x)) {
2333 g_signal_emit (area,
2334 pannable_area_signals[VERTICAL_MOVEMENT],
2335 0, (priv->iy > event->y) ?
2336 HILDON_MOVEMENT_UP :
2337 HILDON_MOVEMENT_DOWN,
2338 (gdouble)priv->ix, (gdouble)priv->iy);
2340 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
2341 priv->vadjust->page_size);
2343 if (!((vscroll_visible)&&
2344 (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT))) {
2346 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
2347 priv->hadjust->page_size);
2349 /* even in case we do not have to move we check if this
2350 could be a fake horizontal movement */
2351 if (!((hscroll_visible)&&
2352 (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ)) ||
2353 (ABS (priv->iy - event->y) -
2354 ABS (priv->ix - event->x) >= priv->direction_error_margin))
2355 priv->moved = FALSE;
2359 g_signal_emit (area,
2360 pannable_area_signals[HORIZONTAL_MOVEMENT],
2361 0, (priv->ix > event->x) ?
2362 HILDON_MOVEMENT_LEFT :
2363 HILDON_MOVEMENT_RIGHT,
2364 (gdouble)priv->ix, (gdouble)priv->iy);
2366 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
2367 priv->hadjust->page_size);
2369 if (!((hscroll_visible)&&
2370 (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ))) {
2372 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
2373 priv->vadjust->page_size);
2375 /* even in case we do not have to move we check if this
2376 could be a fake vertical movement */
2377 if (!((vscroll_visible) &&
2378 (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT)) ||
2379 (ABS (priv->ix - event->x) -
2380 ABS (priv->iy - event->y) >= priv->direction_error_margin))
2381 priv->moved = FALSE;
2385 if ((priv->moved)&&(priv->child)) {
2388 pos_x = priv->cx + (event->x - priv->ix);
2389 pos_y = priv->cy + (event->y - priv->iy);
2391 synth_crossing (priv->child, pos_x, pos_y, event->x_root,
2392 event->y_root, event->time, FALSE);
2396 priv->first_drag = FALSE;
2398 if ((priv->mode != HILDON_PANNABLE_AREA_MODE_PUSH) &&
2399 (priv->mode != HILDON_PANNABLE_AREA_MODE_AUTO)) {
2402 priv->idle_id = gdk_threads_add_timeout ((gint)
2403 (1000.0 / (gdouble) priv->sps),
2405 hildon_pannable_area_timeout, area);
2411 hildon_pannable_area_handle_move (HildonPannableArea *area,
2412 GdkEventMotion * event,
2416 HildonPannableAreaPrivate *priv = area->priv;
2419 switch (priv->mode) {
2420 case HILDON_PANNABLE_AREA_MODE_PUSH:
2421 /* Scroll by the amount of pixels the cursor has moved
2422 * since the last motion event.
2424 hildon_pannable_area_motion_event_scroll (area, *x, *y);
2428 case HILDON_PANNABLE_AREA_MODE_ACCEL:
2429 /* Set acceleration relative to the initial click */
2430 priv->ex = event->x;
2431 priv->ey = event->y;
2432 priv->vel_x = ((*x > 0) ? 1 : -1) *
2434 (gdouble) GTK_WIDGET (area)->allocation.width) *
2435 (priv->vmax - priv->vmin)) + priv->vmin);
2436 priv->vel_y = ((*y > 0) ? 1 : -1) *
2438 (gdouble) GTK_WIDGET (area)->allocation.height) *
2439 (priv->vmax - priv->vmin)) + priv->vmin);
2441 case HILDON_PANNABLE_AREA_MODE_AUTO:
2443 delta = event->time - priv->last_time;
2445 if (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT) {
2446 gdouble dist = event->y - priv->y;
2448 hildon_pannable_area_calculate_velocity (&priv->vel_y,
2460 if (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ) {
2461 gdouble dist = event->x - priv->x;
2463 hildon_pannable_area_calculate_velocity (&priv->vel_x,
2475 hildon_pannable_area_motion_event_scroll (area, *x, *y);
2477 if (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ)
2479 if (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT)
2489 hildon_pannable_area_motion_notify_cb (GtkWidget * widget,
2490 GdkEventMotion * event)
2492 HildonPannableArea *area = HILDON_PANNABLE_AREA (widget);
2493 HildonPannableAreaPrivate *priv = area->priv;
2496 if (gtk_bin_get_child (GTK_BIN (widget)) == NULL)
2499 if ((!priv->enabled) || (!priv->button_pressed) ||
2500 ((event->time == priv->last_time) && (priv->last_type == 2))) {
2501 gdk_window_get_pointer (widget->window, NULL, NULL, 0);
2505 if (priv->last_type == 1) {
2506 priv->first_drag = TRUE;
2509 x = event->x - priv->x;
2510 y = event->y - priv->y;
2513 hildon_pannable_area_check_move (area, event, &x, &y);
2517 hildon_pannable_area_handle_move (area, event, &x, &y);
2518 } else if (priv->child) {
2522 pos_x = priv->cx + (event->x - priv->ix);
2523 pos_y = priv->cy + (event->y - priv->iy);
2525 in = (((0 <= pos_x)&&(priv->child_width >= pos_x)) &&
2526 ((0 <= pos_y)&&(priv->child_height >= pos_y)));
2528 if (((!priv->last_in)&&in)||((priv->last_in)&&(!in))) {
2530 synth_crossing (priv->child, pos_x, pos_y, event->x_root,
2531 event->y_root, event->time, in);
2537 priv->last_time = event->time;
2538 priv->last_type = 2;
2541 /* Send motion notify to child */
2542 event = (GdkEventMotion *) gdk_event_copy ((GdkEvent *) event);
2543 event->x = priv->cx + (event->x - priv->ix);
2544 event->y = priv->cy + (event->y - priv->iy);
2545 event->window = g_object_ref (priv->child);
2546 gdk_event_put ((GdkEvent *) event);
2547 gdk_event_free ((GdkEvent *) event);
2550 gdk_window_get_pointer (widget->window, NULL, NULL, 0);
2556 hildon_pannable_leave_notify_event (GtkWidget *widget,
2557 GdkEventCrossing *event)
2559 HildonPannableArea *area = HILDON_PANNABLE_AREA (widget);
2560 HildonPannableAreaPrivate *priv = area->priv;
2562 if ((priv->child)&&(priv->last_in)) {
2563 priv->last_in = FALSE;
2565 synth_crossing (priv->child, 0, 0, event->x_root,
2566 event->y_root, event->time, FALSE);
2573 hildon_pannable_area_button_release_cb (GtkWidget * widget,
2574 GdkEventButton * event)
2576 HildonPannableArea *area = HILDON_PANNABLE_AREA (widget);
2577 HildonPannableAreaPrivate *priv = area->priv;
2582 if (((event->time == priv->last_time) && (priv->last_type == 3))
2583 || (gtk_bin_get_child (GTK_BIN (widget)) == NULL)
2584 || (!priv->button_pressed) || (!priv->enabled) || (event->button != 1))
2587 /* if last event was a motion-notify we have to check the movement
2588 and launch the animation */
2589 if (priv->last_type == 2) {
2591 dx = event->x - priv->x;
2592 dy = event->y - priv->y;
2594 hildon_pannable_area_check_move (area, (GdkEventMotion *) event, &dx, &dy);
2597 gdouble delta = event->time - priv->last_time;
2599 hildon_pannable_area_handle_move (area, (GdkEventMotion *) event, &dx, &dy);
2601 /* move all the way to the last position now */
2602 if (priv->motion_event_scroll_timeout) {
2603 g_source_remove (priv->motion_event_scroll_timeout);
2604 hildon_pannable_area_motion_event_scroll_timeout (HILDON_PANNABLE_AREA (widget));
2609 if ((ABS (dx) < 4.0) && (delta >= CURSOR_STOPPED_TIMEOUT))
2612 if ((ABS (dy) < 4.0) && (delta >= CURSOR_STOPPED_TIMEOUT))
2617 /* If overshoot has been initiated with a finger down, on release set max speed */
2618 if (priv->overshot_dist_y != 0) {
2619 priv->overshooting_y = priv->bounce_steps; /* Hack to stop a bounce in the finger down case */
2620 priv->vel_y = priv->vmax_overshooting;
2623 if (priv->overshot_dist_x != 0) {
2624 priv->overshooting_x = priv->bounce_steps; /* Hack to stop a bounce in the finger down case */
2625 priv->vel_x = priv->vmax_overshooting;
2628 priv->button_pressed = FALSE;
2630 if ((ABS (priv->vel_y) >= priv->vmin) ||
2631 (ABS (priv->vel_x) >= priv->vmin)) {
2633 priv->scroll_indicator_alpha = 1.0;
2635 if (ABS (priv->vel_x) > MAX_SPEED_THRESHOLD)
2636 priv->vel_x = (priv->vel_x > 0) ? priv->vmax : -priv->vmax;
2638 if (ABS (priv->vel_y) > MAX_SPEED_THRESHOLD)
2639 priv->vel_y = (priv->vel_y > 0) ? priv->vmax : -priv->vmax;
2642 priv->idle_id = gdk_threads_add_timeout ((gint) (1000.0 / (gdouble) priv->sps),
2644 hildon_pannable_area_timeout, widget);
2647 priv->scroll_indicator_event_interrupt = 0;
2648 priv->scroll_delay_counter = priv->scrollbar_fade_delay;
2650 hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget),
2651 priv->scroll_indicator_alpha);
2653 priv->last_time = event->time;
2654 priv->last_type = 3;
2657 priv->moved = FALSE;
2662 hildon_pannable_area_get_topmost (gtk_bin_get_child (GTK_BIN (widget))->window,
2663 event->x, event->y, &x, &y, GDK_BUTTON_RELEASE_MASK);
2665 event = (GdkEventButton *) gdk_event_copy ((GdkEvent *) event);
2669 /* Leave the widget if we've moved - This doesn't break selection,
2670 * but stops buttons from being clicked.
2672 if ((child != priv->child) || (priv->moved)) {
2673 /* Send synthetic leave event */
2674 synth_crossing (priv->child, x, y, event->x_root,
2675 event->y_root, event->time, FALSE);
2676 /* insure no click will happen for widgets that do not handle
2680 /* Send synthetic button release event */
2681 ((GdkEventAny *) event)->window = g_object_ref (priv->child);
2682 gdk_event_put ((GdkEvent *) event);
2684 /* Send synthetic button release event */
2685 ((GdkEventAny *) event)->window = g_object_ref (child);
2686 gdk_event_put ((GdkEvent *) event);
2687 /* Send synthetic leave event */
2688 synth_crossing (priv->child, x, y, event->x_root,
2689 event->y_root, event->time, FALSE);
2691 g_object_remove_weak_pointer ((GObject *) priv->child,
2692 (gpointer) & priv->child);
2694 priv->moved = FALSE;
2695 gdk_event_free ((GdkEvent *) event);
2700 /* utility event handler */
2702 hildon_pannable_area_scroll_cb (GtkWidget *widget,
2703 GdkEventScroll *event)
2705 GtkAdjustment *adj = NULL;
2706 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
2708 if ((!priv->enabled) ||
2709 (gtk_bin_get_child (GTK_BIN (widget)) == NULL))
2712 priv->scroll_indicator_event_interrupt = 0;
2713 priv->scroll_delay_counter = priv->scrollbar_fade_delay + 20;
2715 hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget), 1.0);
2717 /* Stop inertial scrolling */
2718 if (priv->idle_id) {
2721 priv->overshooting_x = 0;
2722 priv->overshooting_y = 0;
2724 if ((priv->overshot_dist_x>0)||(priv->overshot_dist_y>0)) {
2725 priv->overshot_dist_x = 0;
2726 priv->overshot_dist_y = 0;
2728 gtk_widget_queue_resize (GTK_WIDGET (widget));
2731 g_source_remove (priv->idle_id);
2735 if (event->direction == GDK_SCROLL_UP || event->direction == GDK_SCROLL_DOWN)
2736 adj = priv->vadjust;
2738 adj = priv->hadjust;
2742 gdouble delta, new_value;
2744 /* from gtkrange.c calculate delta*/
2745 delta = pow (adj->page_size, 2.0 / 3.0);
2747 if (event->direction == GDK_SCROLL_UP ||
2748 event->direction == GDK_SCROLL_LEFT)
2751 new_value = CLAMP (adj->value + delta, adj->lower, adj->upper - adj->page_size);
2753 gtk_adjustment_set_value (adj, new_value);
2760 hildon_pannable_area_child_mapped (GtkWidget *widget,
2764 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (user_data)->priv;
2766 if (priv->event_window != NULL && priv->enabled)
2767 gdk_window_raise (priv->event_window);
2771 hildon_pannable_area_add (GtkContainer *container, GtkWidget *child)
2773 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (container)->priv;
2775 g_return_if_fail (gtk_bin_get_child (GTK_BIN (container)) == NULL);
2777 gtk_widget_set_parent (child, GTK_WIDGET (container));
2778 GTK_BIN (container)->child = child;
2780 g_signal_connect_after (child, "map-event",
2781 G_CALLBACK (hildon_pannable_area_child_mapped),
2784 if (!gtk_widget_set_scroll_adjustments (child, priv->hadjust, priv->vadjust)) {
2785 g_warning ("%s: cannot add non scrollable widget, "
2786 "wrap it in a viewport", __FUNCTION__);
2791 hildon_pannable_area_remove (GtkContainer *container, GtkWidget *child)
2793 g_return_if_fail (HILDON_IS_PANNABLE_AREA (container));
2794 g_return_if_fail (child != NULL);
2795 g_return_if_fail (gtk_bin_get_child (GTK_BIN (container)) == child);
2797 gtk_widget_set_scroll_adjustments (child, NULL, NULL);
2799 g_signal_handlers_disconnect_by_func (child,
2800 hildon_pannable_area_child_mapped,
2803 /* chain parent class handler to remove child */
2804 GTK_CONTAINER_CLASS (hildon_pannable_area_parent_class)->remove (container, child);
2808 * This method calculates a factor necessary to determine the initial distance
2809 * to jump in hildon_pannable_area_scroll_to(). For fixed time and frames per
2810 * second, we know in how many frames 'n' we need to reach the destination
2811 * point. We know that, for a distance d,
2813 * d = d_0 + d_1 + ... + d_n
2815 * where d_i is the distance travelled in the i-th frame and decel_factor is
2816 * the deceleration factor. This can be rewritten as
2818 * d = d_0 + (d_0 * decel_factor) + ... + (d_n-1 * decel_factor),
2820 * since the distance travelled on each frame is the distance travelled in the
2821 * previous frame reduced by the deceleration factor. Reducing this and
2822 * factoring d_0 out, we get
2824 * d = d_0 (1 + decel_factor + ... + decel_factor^(n-1)).
2826 * Since the sum is independent of the distance to be travelled, we can define
2829 * vel_factor = 1 + decel_factor + ... + decel_factor^(n-1).
2831 * That's the gem we calculate in this method.
2834 hildon_pannable_calculate_vel_factor (HildonPannableArea * self)
2836 HildonPannableAreaPrivate *priv = self->priv;
2841 n = ceil (priv->sps * priv->scroll_time);
2843 for (i = 1; i < n && fct_i >= RATIO_TOLERANCE; i++) {
2844 fct_i *= priv->decel;
2848 priv->vel_factor = fct;
2852 * hildon_pannable_area_new:
2854 * Create a new pannable area widget
2856 * Returns: the newly created #HildonPannableArea
2862 hildon_pannable_area_new (void)
2864 return g_object_new (HILDON_TYPE_PANNABLE_AREA, NULL);
2868 * hildon_pannable_area_new_full:
2869 * @mode: #HildonPannableAreaMode
2870 * @enabled: Value for the enabled property
2871 * @vel_min: Value for the velocity-min property
2872 * @vel_max: Value for the velocity-max property
2873 * @decel: Value for the deceleration property
2874 * @sps: Value for the sps property
2876 * Create a new #HildonPannableArea widget and set various properties
2878 * returns: the newly create #HildonPannableArea
2884 hildon_pannable_area_new_full (gint mode, gboolean enabled,
2885 gdouble vel_min, gdouble vel_max,
2886 gdouble decel, guint sps)
2888 return g_object_new (HILDON_TYPE_PANNABLE_AREA,
2891 "velocity_min", vel_min,
2892 "velocity_max", vel_max,
2893 "deceleration", decel, "sps", sps, NULL);
2897 * hildon_pannable_area_add_with_viewport:
2898 * @area: A #HildonPannableArea
2899 * @child: Child widget to add to the viewport
2901 * Convenience function used to add a child to a #GtkViewport, and add the
2902 * viewport to the scrolled window.
2908 hildon_pannable_area_add_with_viewport (HildonPannableArea * area,
2912 GtkWidget *viewport;
2914 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
2915 g_return_if_fail (GTK_IS_WIDGET (child));
2916 g_return_if_fail (child->parent == NULL);
2918 bin = GTK_BIN (area);
2920 if (bin->child != NULL)
2922 g_return_if_fail (GTK_IS_VIEWPORT (bin->child));
2923 g_return_if_fail (GTK_BIN (bin->child)->child == NULL);
2925 viewport = bin->child;
2929 HildonPannableAreaPrivate *priv = area->priv;
2931 viewport = gtk_viewport_new (priv->hadjust,
2933 gtk_viewport_set_shadow_type (GTK_VIEWPORT (viewport), GTK_SHADOW_NONE);
2934 gtk_container_add (GTK_CONTAINER (area), viewport);
2937 gtk_widget_show (viewport);
2938 gtk_container_add (GTK_CONTAINER (viewport), child);
2942 * hildon_pannable_area_scroll_to:
2943 * @area: A #HildonPannableArea.
2944 * @x: The x coordinate of the destination point or -1 to ignore this axis.
2945 * @y: The y coordinate of the destination point or -1 to ignore this axis.
2947 * Smoothly scrolls @area to ensure that (@x, @y) is a visible point
2948 * on the widget. To move in only one coordinate, you must set the other one
2949 * to -1. Notice that, in %HILDON_PANNABLE_AREA_MODE_PUSH mode, this function
2950 * works just like hildon_pannable_area_jump_to().
2952 * This function is useful if you need to present the user with a particular
2953 * element inside a scrollable widget, like #GtkTreeView. For instance,
2954 * the following example shows how to scroll inside a #GtkTreeView to
2955 * make visible an item, indicated by the #GtkTreeIter @iter.
2959 * GtkTreePath *path;
2960 * GdkRectangle *rect;
2962 * path = gtk_tree_model_get_path (model, &iter);
2963 * gtk_tree_view_get_background_area (GTK_TREE_VIEW (treeview),
2964 * path, NULL, &rect);
2965 * gtk_tree_view_convert_bin_window_to_tree_coords (GTK_TREE_VIEW (treeview),
2966 * 0, rect.y, NULL, &y);
2967 * hildon_pannable_area_scroll_to (panarea, -1, y);
2968 * gtk_tree_path_free (path);
2972 * If you want to present a child widget in simpler scenarios,
2973 * use hildon_pannable_area_scroll_to_child() instead.
2975 * There is a precondition to this function: the widget must be
2976 * already realized. Check the hildon_pannable_area_jump_to_child() for
2977 * more tips regarding how to call this function during
2983 hildon_pannable_area_scroll_to (HildonPannableArea *area,
2984 const gint x, const gint y)
2986 HildonPannableAreaPrivate *priv;
2988 gint dist_x, dist_y;
2989 gboolean hscroll_visible, vscroll_visible;
2991 g_return_if_fail (GTK_WIDGET_REALIZED (area));
2992 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
2996 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
2997 priv->vadjust->page_size);
2998 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
2999 priv->hadjust->page_size);
3001 if (((!vscroll_visible)&&(!hscroll_visible)) ||
3002 (x == -1 && y == -1)) {
3006 if (priv->mode == HILDON_PANNABLE_AREA_MODE_PUSH)
3007 hildon_pannable_area_jump_to (area, x, y);
3009 width = priv->hadjust->upper - priv->hadjust->lower;
3010 height = priv->vadjust->upper - priv->vadjust->lower;
3012 g_return_if_fail (x < width || y < height);
3014 if ((x > -1)&&(hscroll_visible)) {
3015 priv->scroll_to_x = x - priv->hadjust->page_size/2;
3016 dist_x = priv->scroll_to_x - priv->hadjust->value;
3018 priv->scroll_to_x = -1;
3020 priv->vel_x = - dist_x/priv->vel_factor;
3023 priv->scroll_to_x = -1;
3026 if ((y > -1)&&(vscroll_visible)) {
3027 priv->scroll_to_y = y - priv->vadjust->page_size/2;
3028 dist_y = priv->scroll_to_y - priv->vadjust->value;
3030 priv->scroll_to_y = -1;
3032 priv->vel_y = - dist_y/priv->vel_factor;
3035 priv->scroll_to_y = y;
3038 if ((priv->scroll_to_y == -1) && (priv->scroll_to_y == -1)) {
3042 hildon_pannable_area_launch_fade_timeout (area, 1.0);
3045 priv->idle_id = gdk_threads_add_timeout ((gint) (1000.0 / (gdouble) priv->sps),
3047 hildon_pannable_area_timeout, area);
3051 * hildon_pannable_area_jump_to:
3052 * @area: A #HildonPannableArea.
3053 * @x: The x coordinate of the destination point or -1 to ignore this axis.
3054 * @y: The y coordinate of the destination point or -1 to ignore this axis.
3056 * Jumps the position of @area to ensure that (@x, @y) is a visible
3057 * point in the widget. In order to move in only one coordinate, you
3058 * must set the other one to -1. See hildon_pannable_area_scroll_to()
3059 * function for an example of how to calculate the position of
3060 * children in scrollable widgets like #GtkTreeview.
3062 * There is a precondition to this function: the widget must be
3063 * already realized. Check the hildon_pannable_area_jump_to_child() for
3064 * more tips regarding how to call this function during
3070 hildon_pannable_area_jump_to (HildonPannableArea *area,
3071 const gint x, const gint y)
3073 HildonPannableAreaPrivate *priv;
3077 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3078 g_return_if_fail (GTK_WIDGET_REALIZED (area));
3079 g_return_if_fail (x >= -1 && y >= -1);
3081 if (x == -1 && y == -1) {
3087 width = priv->hadjust->upper - priv->hadjust->lower;
3088 height = priv->vadjust->upper - priv->vadjust->lower;
3090 g_return_if_fail (x < width || y < height);
3092 hv = priv->hadjust->value;
3093 vv = priv->vadjust->value;
3096 gdouble jump_to = x - priv->hadjust->page_size/2;
3098 priv->hadjust->value = CLAMP (jump_to,
3099 priv->hadjust->lower,
3100 priv->hadjust->upper -
3101 priv->hadjust->page_size);
3105 gdouble jump_to = y - priv->vadjust->page_size/2;
3107 priv->vadjust->value = CLAMP (jump_to,
3108 priv->vadjust->lower,
3109 priv->vadjust->upper -
3110 priv->vadjust->page_size);
3113 if (hv != priv->hadjust->value)
3114 gtk_adjustment_value_changed (priv->hadjust);
3116 if (vv != priv->vadjust->value)
3117 gtk_adjustment_value_changed (priv->vadjust);
3119 priv->scroll_indicator_alpha = 1.0;
3121 if (priv->scroll_indicator_timeout) {
3122 g_source_remove (priv->scroll_indicator_timeout);
3123 priv->scroll_indicator_timeout = 0;
3126 if (priv->idle_id) {
3129 priv->overshooting_x = 0;
3130 priv->overshooting_y = 0;
3132 if ((priv->overshot_dist_x>0)||(priv->overshot_dist_y>0)) {
3133 priv->overshot_dist_x = 0;
3134 priv->overshot_dist_y = 0;
3136 gtk_widget_queue_resize (GTK_WIDGET (area));
3139 g_source_remove (priv->idle_id);
3145 * hildon_pannable_area_scroll_to_child:
3146 * @area: A #HildonPannableArea.
3147 * @child: A #GtkWidget, descendant of @area.
3149 * Smoothly scrolls until @child is visible inside @area. @child must
3150 * be a descendant of @area. If you need to scroll inside a scrollable
3151 * widget, e.g., #GtkTreeview, see hildon_pannable_area_scroll_to().
3153 * There is a precondition to this function: the widget must be
3154 * already realized. Check the hildon_pannable_area_jump_to_child() for
3155 * more tips regarding how to call this function during
3161 hildon_pannable_area_scroll_to_child (HildonPannableArea *area, GtkWidget *child)
3163 GtkWidget *bin_child;
3166 g_return_if_fail (GTK_WIDGET_REALIZED (area));
3167 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3168 g_return_if_fail (GTK_IS_WIDGET (child));
3169 g_return_if_fail (gtk_widget_is_ancestor (child, GTK_WIDGET (area)));
3171 if (GTK_BIN (area)->child == NULL)
3174 /* We need to get to check the child of the inside the area */
3175 bin_child = GTK_BIN (area)->child;
3177 /* we check if we added a viewport */
3178 if (GTK_IS_VIEWPORT (bin_child)) {
3179 bin_child = GTK_BIN (bin_child)->child;
3182 if (gtk_widget_translate_coordinates (child, bin_child, 0, 0, &x, &y))
3183 hildon_pannable_area_scroll_to (area, x, y);
3187 * hildon_pannable_area_jump_to_child:
3188 * @area: A #HildonPannableArea.
3189 * @child: A #GtkWidget, descendant of @area.
3191 * Jumps to make sure @child is visible inside @area. @child must
3192 * be a descendant of @area. If you want to move inside a scrollable
3193 * widget, like, #GtkTreeview, see hildon_pannable_area_scroll_to().
3195 * There is a precondition to this function: the widget must be
3196 * already realized. You can control if the widget is ready with the
3197 * GTK_WIDGET_REALIZED macro. If you want to call this function during
3198 * the initialization process of the widget do it inside a callback to
3199 * the ::realize signal, using g_signal_connect_after() function.
3204 hildon_pannable_area_jump_to_child (HildonPannableArea *area, GtkWidget *child)
3206 GtkWidget *bin_child;
3209 g_return_if_fail (GTK_WIDGET_REALIZED (area));
3210 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3211 g_return_if_fail (GTK_IS_WIDGET (child));
3212 g_return_if_fail (gtk_widget_is_ancestor (child, GTK_WIDGET (area)));
3214 if (gtk_bin_get_child (GTK_BIN (area)) == NULL)
3217 /* We need to get to check the child of the inside the area */
3218 bin_child = gtk_bin_get_child (GTK_BIN (area));
3220 /* we check if we added a viewport */
3221 if (GTK_IS_VIEWPORT (bin_child)) {
3222 bin_child = gtk_bin_get_child (GTK_BIN (bin_child));
3225 if (gtk_widget_translate_coordinates (child, bin_child, 0, 0, &x, &y))
3226 hildon_pannable_area_jump_to (area, x, y);
3230 * hildon_pannable_get_child_widget_at:
3231 * @area: A #HildonPannableArea.
3232 * @x: horizontal coordinate of the point
3233 * @y: vertical coordinate of the point
3235 * Get the widget at the point (x, y) inside the pannable area. In
3236 * case no widget found it returns NULL.
3238 * returns: the #GtkWidget if we find a widget, NULL in any other case
3243 hildon_pannable_get_child_widget_at (HildonPannableArea *area,
3244 gdouble x, gdouble y)
3246 GdkWindow *window = NULL;
3247 GtkWidget *child_widget = NULL;
3249 window = hildon_pannable_area_get_topmost
3250 (gtk_bin_get_child (GTK_BIN (area))->window,
3251 x, y, NULL, NULL, GDK_ALL_EVENTS_MASK);
3253 gdk_window_get_user_data (window, (gpointer) &child_widget);
3255 return child_widget;
3260 * hildon_pannable_area_get_hadjustment:
3261 * @area: A #HildonPannableArea.
3263 * Returns the horizontal adjustment. This adjustment is the internal
3264 * widget adjustment used to control the animations. Do not modify it
3265 * directly to change the position of the pannable, to do that use the
3266 * pannable API. If you modify the object directly it could cause
3267 * artifacts in the animations.
3269 * returns: The horizontal #GtkAdjustment
3274 hildon_pannable_area_get_hadjustment (HildonPannableArea *area)
3277 g_return_val_if_fail (HILDON_IS_PANNABLE_AREA (area), NULL);
3279 return area->priv->hadjust;
3283 * hildon_pannable_area_get_vadjustment:
3284 * @area: A #HildonPannableArea.
3286 * Returns the vertical adjustment. This adjustment is the internal
3287 * widget adjustment used to control the animations. Do not modify it
3288 * directly to change the position of the pannable, to do that use the
3289 * pannable API. If you modify the object directly it could cause
3290 * artifacts in the animations.
3292 * returns: The vertical #GtkAdjustment
3297 hildon_pannable_area_get_vadjustment (HildonPannableArea *area)
3299 g_return_val_if_fail (HILDON_IS_PANNABLE_AREA (area), NULL);
3301 return area->priv->vadjust;
3306 * hildon_pannable_area_get_size_request_policy:
3307 * @area: A #HildonPannableArea.
3309 * This function returns the current size request policy of the
3310 * widget. That policy controls the way the size_request is done in
3311 * the pannable area. Check
3312 * hildon_pannable_area_set_size_request_policy() for a more detailed
3315 * returns: the policy is currently being used in the widget
3316 * #HildonSizeRequestPolicy.
3320 HildonSizeRequestPolicy
3321 hildon_pannable_area_get_size_request_policy (HildonPannableArea *area)
3323 HildonPannableAreaPrivate *priv;
3325 g_return_val_if_fail (HILDON_IS_PANNABLE_AREA (area), FALSE);
3329 return priv->size_request_policy;
3333 * hildon_pannable_area_set_size_request_policy:
3334 * @area: A #HildonPannableArea.
3335 * @size_request_policy: One of the allowed #HildonSizeRequestPolicy
3337 * This function sets the pannable area size request policy. That
3338 * policy controls the way the size_request is done in the pannable
3339 * area. Pannable can use the size request of its children
3340 * (#HILDON_SIZE_REQUEST_CHILDREN) or the minimum size required for
3341 * the area itself (#HILDON_SIZE_REQUEST_MINIMUM), the latter is the
3342 * default. Recall this size depends on the scrolling policy you are
3343 * requesting to the pannable area, if you set #GTK_POLICY_NEVER this
3344 * parameter will not have any effect with
3345 * #HILDON_SIZE_REQUEST_MINIMUM set.
3349 * Deprecated: This method and the policy request is deprecated, DO
3350 * NOT use it in future code, the only policy properly supported in
3351 * gtk+ nowadays is the minimum size. Use #gtk_window_set_default_size
3352 * or #gtk_window_set_geometry_hints with the proper size in your case
3353 * to define the height of your dialogs.
3356 hildon_pannable_area_set_size_request_policy (HildonPannableArea *area,
3357 HildonSizeRequestPolicy size_request_policy)
3359 HildonPannableAreaPrivate *priv;
3361 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3365 if (priv->size_request_policy == size_request_policy)
3368 priv->size_request_policy = size_request_policy;
3370 gtk_widget_queue_resize (GTK_WIDGET (area));
3372 g_object_notify (G_OBJECT (area), "size-request-policy");