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 200
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 */
81 guint32 last_press_time;
87 gdouble vmax_overshooting;
94 guint panning_threshold;
95 guint scrollbar_fade_delay;
98 guint direction_error_margin;
106 gint ix; /* Initial click mouse co-ordinates */
108 gint cx; /* Initial click child window mouse co-ordinates */
115 gint overshot_dist_x;
116 gint overshot_dist_y;
119 gdouble scroll_indicator_alpha;
120 gint motion_event_scroll_timeout;
121 gint scroll_indicator_timeout;
122 gint scroll_indicator_event_interrupt;
123 gint scroll_delay_counter;
126 gboolean initial_hint;
127 gboolean initial_effect;
128 gboolean low_friction_mode;
131 gboolean size_request_policy;
132 gboolean hscroll_visible;
133 gboolean vscroll_visible;
134 GdkRectangle hscroll_rect;
135 GdkRectangle vscroll_rect;
136 guint indicator_width;
138 GtkAdjustment *hadjust;
139 GtkAdjustment *vadjust;
143 GtkPolicyType vscrollbar_policy;
144 GtkPolicyType hscrollbar_policy;
146 GdkGC *scrollbars_gc;
147 GdkColor scroll_color;
149 gboolean center_on_child_focus;
150 gboolean center_on_child_focus_pending;
162 static guint pannable_area_signals [LAST_SIGNAL] = { 0 };
170 PROP_VEL_MAX_OVERSHOOTING,
171 PROP_VELOCITY_FAST_FACTOR,
175 PROP_PANNING_THRESHOLD,
176 PROP_SCROLLBAR_FADE_DELAY,
179 PROP_DIRECTION_ERROR_MARGIN,
180 PROP_VSCROLLBAR_POLICY,
181 PROP_HSCROLLBAR_POLICY,
186 PROP_LOW_FRICTION_MODE,
187 PROP_SIZE_REQUEST_POLICY,
190 PROP_CENTER_ON_CHILD_FOCUS,
194 static void hildon_pannable_area_class_init (HildonPannableAreaClass * klass);
195 static void hildon_pannable_area_init (HildonPannableArea * area);
196 static void hildon_pannable_area_get_property (GObject * object,
200 static void hildon_pannable_area_set_property (GObject * object,
202 const GValue * value,
204 static void hildon_pannable_area_remove_timeouts (GtkWidget * widget);
205 static void hildon_pannable_area_dispose (GObject * object);
206 static void hildon_pannable_area_realize (GtkWidget * widget);
207 static void hildon_pannable_area_unrealize (GtkWidget * widget);
208 static void hildon_pannable_area_size_request (GtkWidget * widget,
209 GtkRequisition * requisition);
210 static void hildon_pannable_area_size_allocate (GtkWidget * widget,
211 GtkAllocation * allocation);
212 static void hildon_pannable_area_child_allocate_calculate (GtkWidget * widget,
213 GtkAllocation * allocation,
214 GtkAllocation * child_allocation);
215 static void hildon_pannable_area_style_set (GtkWidget * widget,
216 GtkStyle * previous_style);
217 static void hildon_pannable_area_map (GtkWidget * widget);
218 static void hildon_pannable_area_unmap (GtkWidget * widget);
219 static void hildon_pannable_area_grab_notify (GtkWidget *widget,
220 gboolean was_grabbed,
222 #if USE_CAIRO_SCROLLBARS == 1
223 static void rgb_from_gdkcolor (GdkColor *color, gdouble *r, gdouble *g, gdouble *b);
224 #else /* USE_CAIRO_SCROLLBARS */
225 static void tranparency_color (GdkColor *color,
228 gdouble transparency);
229 #endif /* USE_CAIRO_SCROLLBARS */
230 static void hildon_pannable_draw_vscroll (GtkWidget * widget,
231 GdkColor *back_color,
232 GdkColor *scroll_color);
233 static void hildon_pannable_draw_hscroll (GtkWidget * widget,
234 GdkColor *back_color,
235 GdkColor *scroll_color);
236 static void hildon_pannable_area_initial_effect (GtkWidget * widget);
237 static void hildon_pannable_area_redraw (HildonPannableArea * area);
238 static void hildon_pannable_area_launch_fade_timeout (HildonPannableArea * area,
240 static void hildon_pannable_area_adjust_value_changed (HildonPannableArea * area,
242 static void hildon_pannable_area_adjust_changed (HildonPannableArea * area,
244 static gboolean hildon_pannable_area_scroll_indicator_fade(HildonPannableArea * area);
245 static gboolean hildon_pannable_area_expose_event (GtkWidget * widget,
246 GdkEventExpose * event);
247 static GdkWindow * hildon_pannable_area_get_topmost (GdkWindow * window,
249 gint * tx, gint * ty,
251 static void synth_crossing (GdkWindow * child,
253 gint x_root, gint y_root,
254 guint32 time, gboolean in);
255 static gboolean hildon_pannable_area_button_press_cb (GtkWidget * widget,
256 GdkEventButton * event);
257 static void hildon_pannable_area_refresh (HildonPannableArea * area);
258 static gboolean hildon_pannable_area_check_scrollbars (HildonPannableArea * area);
259 static void hildon_pannable_axis_scroll (HildonPannableArea *area,
260 GtkAdjustment *adjust,
268 static void hildon_pannable_area_scroll (HildonPannableArea *area,
269 gdouble x, gdouble y);
270 static gboolean hildon_pannable_area_timeout (HildonPannableArea * area);
271 static void hildon_pannable_area_calculate_velocity (gdouble *vel,
275 gdouble drag_inertia,
278 static gboolean hildon_pannable_area_motion_event_scroll_timeout (HildonPannableArea *area);
279 static void hildon_pannable_area_motion_event_scroll (HildonPannableArea *area,
280 gdouble x, gdouble y);
281 static void hildon_pannable_area_check_move (HildonPannableArea *area,
282 GdkEventMotion * event,
285 static void hildon_pannable_area_handle_move (HildonPannableArea *area,
286 GdkEventMotion * event,
289 static gboolean hildon_pannable_area_motion_notify_cb (GtkWidget * widget,
290 GdkEventMotion * event);
291 static gboolean hildon_pannable_leave_notify_event (GtkWidget *widget,
292 GdkEventCrossing *event);
293 static gboolean hildon_pannable_area_button_release_cb (GtkWidget * widget,
294 GdkEventButton * event);
295 static gboolean hildon_pannable_area_scroll_cb (GtkWidget *widget,
296 GdkEventScroll *event);
297 static void hildon_pannable_area_child_mapped (GtkWidget *widget,
300 static void hildon_pannable_area_add (GtkContainer *container, GtkWidget *child);
301 static void hildon_pannable_area_remove (GtkContainer *container, GtkWidget *child);
302 static void hildon_pannable_calculate_vel_factor (HildonPannableArea * self);
303 static void hildon_pannable_area_set_focus_child (GtkContainer *container,
305 static void hildon_pannable_area_center_on_child_focus (HildonPannableArea *area);
309 hildon_pannable_area_class_init (HildonPannableAreaClass * klass)
311 GObjectClass *object_class = G_OBJECT_CLASS (klass);
312 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
313 GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
316 g_type_class_add_private (klass, sizeof (HildonPannableAreaPrivate));
318 object_class->get_property = hildon_pannable_area_get_property;
319 object_class->set_property = hildon_pannable_area_set_property;
320 object_class->dispose = hildon_pannable_area_dispose;
322 widget_class->realize = hildon_pannable_area_realize;
323 widget_class->unrealize = hildon_pannable_area_unrealize;
324 widget_class->map = hildon_pannable_area_map;
325 widget_class->unmap = hildon_pannable_area_unmap;
326 widget_class->size_request = hildon_pannable_area_size_request;
327 widget_class->size_allocate = hildon_pannable_area_size_allocate;
328 widget_class->expose_event = hildon_pannable_area_expose_event;
329 widget_class->style_set = hildon_pannable_area_style_set;
330 widget_class->button_press_event = hildon_pannable_area_button_press_cb;
331 widget_class->button_release_event = hildon_pannable_area_button_release_cb;
332 widget_class->motion_notify_event = hildon_pannable_area_motion_notify_cb;
333 widget_class->leave_notify_event = hildon_pannable_leave_notify_event;
334 widget_class->scroll_event = hildon_pannable_area_scroll_cb;
336 container_class->add = hildon_pannable_area_add;
337 container_class->remove = hildon_pannable_area_remove;
338 container_class->set_focus_child = hildon_pannable_area_set_focus_child;
340 klass->horizontal_movement = NULL;
341 klass->vertical_movement = NULL;
343 g_object_class_install_property (object_class,
345 g_param_spec_boolean ("enabled",
347 "Enable or disable finger-scroll.",
352 g_object_class_install_property (object_class,
353 PROP_VSCROLLBAR_POLICY,
354 g_param_spec_enum ("vscrollbar_policy",
356 "Visual policy of the vertical scrollbar",
357 GTK_TYPE_POLICY_TYPE,
358 GTK_POLICY_AUTOMATIC,
362 g_object_class_install_property (object_class,
363 PROP_HSCROLLBAR_POLICY,
364 g_param_spec_enum ("hscrollbar_policy",
366 "Visual policy of the horizontal scrollbar",
367 GTK_TYPE_POLICY_TYPE,
368 GTK_POLICY_AUTOMATIC,
372 g_object_class_install_property (object_class,
374 g_param_spec_enum ("mode",
376 "Change the finger-scrolling mode.",
377 HILDON_TYPE_PANNABLE_AREA_MODE,
378 HILDON_PANNABLE_AREA_MODE_AUTO,
382 g_object_class_install_property (object_class,
384 g_param_spec_flags ("mov_mode",
385 "Scroll movement mode",
386 "Controls if the widget can scroll vertically, horizontally or both",
387 HILDON_TYPE_MOVEMENT_MODE,
388 HILDON_MOVEMENT_MODE_VERT,
392 g_object_class_install_property (object_class,
394 g_param_spec_double ("velocity_min",
395 "Minimum scroll velocity",
396 "Minimum distance the child widget should scroll "
397 "per 'frame', in pixels per frame.",
402 g_object_class_install_property (object_class,
404 g_param_spec_double ("velocity_max",
405 "Maximum scroll velocity",
406 "Maximum distance the child widget should scroll "
407 "per 'frame', in pixels per frame.",
412 g_object_class_install_property (object_class,
413 PROP_VEL_MAX_OVERSHOOTING,
414 g_param_spec_double ("velocity_overshooting_max",
415 "Maximum scroll velocity when overshooting",
416 "Maximum distance the child widget should scroll "
417 "per 'frame', in pixels per frame when it overshoots after hitting the edge.",
422 g_object_class_install_property (object_class,
423 PROP_VELOCITY_FAST_FACTOR,
424 g_param_spec_double ("velocity_fast_factor",
425 "Fast velocity factor",
426 "Minimum velocity that is considered 'fast': "
427 "children widgets won't receive button presses. "
428 "Expressed as a fraction of the maximum velocity.",
433 g_object_class_install_property (object_class,
435 g_param_spec_double ("deceleration",
436 "Deceleration multiplier",
437 "The multiplier used when decelerating when in "
438 "acceleration scrolling mode.",
443 g_object_class_install_property (object_class,
445 g_param_spec_double ("drag_inertia",
446 "Inertia of the cursor dragging",
447 "Percentage of the calculated speed in each moment we are are going to use"
448 "to calculate the launch speed, the other part would be the speed"
449 "calculated previously",
454 g_object_class_install_property (object_class,
456 g_param_spec_uint ("sps",
457 "Scrolls per second",
458 "Amount of scroll events to generate per second.",
463 g_object_class_install_property (object_class,
464 PROP_PANNING_THRESHOLD,
465 g_param_spec_uint ("panning_threshold",
466 "Threshold to consider a motion event an scroll",
467 "Amount of pixels to consider a motion event an scroll, if it is less"
468 "it is a click detected incorrectly by the touch screen.",
473 g_object_class_install_property (object_class,
474 PROP_SCROLLBAR_FADE_DELAY,
475 g_param_spec_uint ("scrollbar_fade_delay",
476 "Time before starting to fade the scrollbar",
477 "Time the scrollbar is going to be visible if the widget is not in"
478 "action in miliseconds",
483 g_object_class_install_property (object_class,
485 g_param_spec_uint ("bounce_steps",
487 "Number of steps that is going to be used to bounce when hitting the"
488 "edge, the rubberband effect depends on it",
493 g_object_class_install_property (object_class,
495 g_param_spec_uint ("force",
496 "Multiplier of the calculated speed",
497 "Force applied to the movement, multiplies the calculated speed of the"
498 "user movement the cursor in the screen",
503 g_object_class_install_property (object_class,
504 PROP_DIRECTION_ERROR_MARGIN,
505 g_param_spec_uint ("direction_error_margin",
506 "Margin in the direction detection",
507 "After detecting the direction of the movement (horizontal or"
508 "vertical), we can add this margin of error to allow the movement in"
509 "the other direction even apparently it is not",
514 g_object_class_install_property (object_class,
516 g_param_spec_int ("vovershoot_max",
517 "Vertical overshoot distance",
518 "Space we allow the widget to pass over its vertical limits when"
519 "hitting the edges, set 0 in order to deactivate overshooting.",
524 g_object_class_install_property (object_class,
526 g_param_spec_int ("hovershoot_max",
527 "Horizontal overshoot distance",
528 "Space we allow the widget to pass over its horizontal limits when"
529 "hitting the edges, set 0 in order to deactivate overshooting.",
534 g_object_class_install_property (object_class,
536 g_param_spec_double ("scroll_time",
537 "Time to scroll to a position",
538 "The time to scroll to a position when calling the hildon_pannable_scroll_to function",
543 g_object_class_install_property (object_class,
545 g_param_spec_boolean ("initial-hint",
547 "Whether to hint the user about the pannability of the container.",
552 g_object_class_install_property (object_class,
553 PROP_LOW_FRICTION_MODE,
554 g_param_spec_boolean ("low-friction-mode",
555 "Do not decelerate the initial velocity",
556 "Avoid decelerating the panning movement, like no friction, the widget"
557 "will stop in the edges or if the user clicks.",
562 g_object_class_install_property (object_class,
563 PROP_SIZE_REQUEST_POLICY,
564 g_param_spec_enum ("size-request-policy",
565 "Size Requisition policy",
566 "Controls the size request policy of the widget",
567 HILDON_TYPE_SIZE_REQUEST_POLICY,
568 HILDON_SIZE_REQUEST_MINIMUM,
572 g_object_class_install_property (object_class,
574 g_param_spec_object ("hadjustment",
575 "Horizontal Adjustment",
576 "The GtkAdjustment for the horizontal position",
579 g_object_class_install_property (object_class,
581 g_param_spec_object ("vadjustment",
582 "Vertical Adjustment",
583 "The GtkAdjustment for the vertical position",
587 g_object_class_install_property (object_class,
588 PROP_CENTER_ON_CHILD_FOCUS,
589 g_param_spec_boolean ("center-on-child-focus",
590 "Center on the child with the focus",
591 "Whether to center the pannable on the child that receives the focus.",
597 gtk_widget_class_install_style_property (widget_class,
600 "Width of the scroll indicators",
601 "Pixel width used to draw the scroll indicators.",
606 * HildonPannableArea::horizontal-movement:
607 * @hildonpannable: the object which received the signal
608 * @direction: the direction of the movement #HILDON_MOVEMENT_LEFT or #HILDON_MOVEMENT_RIGHT
609 * @initial_x: the x coordinate of the point where the user clicked to start the movement
610 * @initial_y: the y coordinate of the point where the user clicked to start the movement
612 * The horizontal-movement signal is emitted when the pannable area
613 * detects a horizontal movement. The detection does not mean the
614 * widget is going to move (i.e. maybe the children are smaller
615 * horizontally than the screen).
619 pannable_area_signals[HORIZONTAL_MOVEMENT] =
620 g_signal_new ("horizontal_movement",
621 G_TYPE_FROM_CLASS (object_class),
622 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
623 G_STRUCT_OFFSET (HildonPannableAreaClass, horizontal_movement),
625 _hildon_marshal_VOID__INT_DOUBLE_DOUBLE,
632 * HildonPannableArea::vertical-movement:
633 * @hildonpannable: the object which received the signal
634 * @direction: the direction of the movement #HILDON_MOVEMENT_UP or #HILDON_MOVEMENT_DOWN
635 * @initial_x: the x coordinate of the point where the user clicked to start the movement
636 * @initial_y: the y coordinate of the point where the user clicked to start the movement
638 * The vertical-movement signal is emitted when the pannable area
639 * detects a vertical movement. The detection does not mean the
640 * widget is going to move (i.e. maybe the children are smaller
641 * vertically than the screen).
645 pannable_area_signals[VERTICAL_MOVEMENT] =
646 g_signal_new ("vertical_movement",
647 G_TYPE_FROM_CLASS (object_class),
648 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
649 G_STRUCT_OFFSET (HildonPannableAreaClass, vertical_movement),
651 _hildon_marshal_VOID__INT_DOUBLE_DOUBLE,
658 * HildonPannableArea::panning-started:
659 * @hildonpannable: the pannable area object that is going to start
662 * This signal is emitted before the panning starts. Applications
663 * can return %TRUE to avoid the panning. The main difference with
664 * the vertical-movement and horizontal-movement signals is those
665 * gesture signals are launched no matter if the widget is going to
666 * move, this signal means the widget is going to start moving. It
667 * could even happen that the widget moves and there was no gesture
668 * (i.e. click meanwhile the pannable is overshooting).
670 * Returns: %TRUE to stop the panning launch. %FALSE to continue
675 pannable_area_signals[PANNING_STARTED] =
676 g_signal_new ("panning-started",
677 G_TYPE_FROM_CLASS (object_class),
681 _hildon_marshal_BOOLEAN__VOID,
685 * HildonPannableArea::panning-finished:
686 * @hildonpannable: the pannable area object that finished the
689 * This signal is emitted after the kinetic panning has
694 pannable_area_signals[PANNING_FINISHED] =
695 g_signal_new ("panning-finished",
696 G_TYPE_FROM_CLASS (object_class),
700 _hildon_marshal_VOID__VOID,
706 hildon_pannable_area_init (HildonPannableArea * area)
708 HildonPannableAreaPrivate *priv = PANNABLE_AREA_PRIVATE (area);
710 GTK_WIDGET_UNSET_FLAGS (area, GTK_NO_WINDOW);
715 priv->button_pressed = FALSE;
717 priv->last_press_time = 0;
719 priv->vscroll_visible = TRUE;
720 priv->hscroll_visible = TRUE;
721 priv->indicator_width = 6;
722 priv->overshot_dist_x = 0;
723 priv->overshot_dist_y = 0;
724 priv->overshooting_y = 0;
725 priv->overshooting_x = 0;
731 priv->scroll_indicator_alpha = 0.0;
732 priv->scroll_indicator_timeout = 0;
733 priv->motion_event_scroll_timeout = 0;
734 priv->scroll_indicator_event_interrupt = 0;
735 priv->scroll_delay_counter = 0;
736 priv->scrollbar_fade_delay = 0;
737 priv->scroll_to_x = -1;
738 priv->scroll_to_y = -1;
739 priv->first_drag = TRUE;
740 priv->initial_effect = TRUE;
741 priv->child_width = 0;
742 priv->child_height = 0;
743 priv->last_in = TRUE;
746 priv->center_on_child_focus_pending = FALSE;
748 gtk_style_lookup_color (GTK_WIDGET (area)->style,
749 "SecondaryTextColor", &priv->scroll_color);
752 GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
754 GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
756 g_object_ref_sink (G_OBJECT (priv->hadjust));
757 g_object_ref_sink (G_OBJECT (priv->vadjust));
759 g_signal_connect_swapped (priv->hadjust, "value-changed",
760 G_CALLBACK (hildon_pannable_area_adjust_value_changed), area);
761 g_signal_connect_swapped (priv->vadjust, "value-changed",
762 G_CALLBACK (hildon_pannable_area_adjust_value_changed), area);
763 g_signal_connect_swapped (priv->hadjust, "changed",
764 G_CALLBACK (hildon_pannable_area_adjust_changed), area);
765 g_signal_connect_swapped (priv->vadjust, "changed",
766 G_CALLBACK (hildon_pannable_area_adjust_changed), area);
767 g_signal_connect (area, "grab-notify",
768 G_CALLBACK (hildon_pannable_area_grab_notify), NULL);
772 hildon_pannable_area_get_property (GObject * object,
777 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (object)->priv;
779 switch (property_id) {
781 g_value_set_boolean (value, priv->enabled);
784 g_value_set_enum (value, priv->mode);
786 case PROP_MOVEMENT_MODE:
787 g_value_set_flags (value, priv->mov_mode);
789 case PROP_VELOCITY_MIN:
790 g_value_set_double (value, priv->vmin);
792 case PROP_VELOCITY_MAX:
793 g_value_set_double (value, priv->vmax);
795 case PROP_VEL_MAX_OVERSHOOTING:
796 g_value_set_double (value, priv->vmax_overshooting);
798 case PROP_VELOCITY_FAST_FACTOR:
799 g_value_set_double (value, priv->vfast_factor);
801 case PROP_DECELERATION:
802 g_value_set_double (value, priv->decel);
804 case PROP_DRAG_INERTIA:
805 g_value_set_double (value, priv->drag_inertia);
808 g_value_set_uint (value, priv->sps);
810 case PROP_PANNING_THRESHOLD:
811 g_value_set_uint (value, priv->panning_threshold);
813 case PROP_SCROLLBAR_FADE_DELAY:
814 /* convert to miliseconds */
815 g_value_set_uint (value, priv->scrollbar_fade_delay * SCROLL_FADE_TIMEOUT);
817 case PROP_BOUNCE_STEPS:
818 g_value_set_uint (value, priv->bounce_steps);
821 g_value_set_uint (value, priv->force);
823 case PROP_DIRECTION_ERROR_MARGIN:
824 g_value_set_uint (value, priv->direction_error_margin);
826 case PROP_VSCROLLBAR_POLICY:
827 g_value_set_enum (value, priv->vscrollbar_policy);
829 case PROP_HSCROLLBAR_POLICY:
830 g_value_set_enum (value, priv->hscrollbar_policy);
832 case PROP_VOVERSHOOT_MAX:
833 g_value_set_int (value, priv->vovershoot_max);
835 case PROP_HOVERSHOOT_MAX:
836 g_value_set_int (value, priv->hovershoot_max);
838 case PROP_SCROLL_TIME:
839 g_value_set_double (value, priv->scroll_time);
841 case PROP_INITIAL_HINT:
842 g_value_set_boolean (value, priv->initial_hint);
844 case PROP_LOW_FRICTION_MODE:
845 g_value_set_boolean (value, priv->low_friction_mode);
847 case PROP_SIZE_REQUEST_POLICY:
848 g_value_set_enum (value, priv->size_request_policy);
850 case PROP_HADJUSTMENT:
851 g_value_set_object (value,
852 hildon_pannable_area_get_hadjustment
853 (HILDON_PANNABLE_AREA (object)));
855 case PROP_VADJUSTMENT:
856 g_value_set_object (value,
857 hildon_pannable_area_get_vadjustment
858 (HILDON_PANNABLE_AREA (object)));
860 case PROP_CENTER_ON_CHILD_FOCUS:
861 g_value_set_boolean (value, priv->center_on_child_focus);
864 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
869 hildon_pannable_area_set_property (GObject * object,
871 const GValue * value,
874 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (object)->priv;
877 switch (property_id) {
879 enabled = g_value_get_boolean (value);
881 if ((priv->enabled != enabled) && (GTK_WIDGET_REALIZED (object))) {
883 gdk_window_raise (priv->event_window);
885 gdk_window_lower (priv->event_window);
888 priv->enabled = enabled;
891 priv->mode = g_value_get_enum (value);
893 case PROP_MOVEMENT_MODE:
894 priv->mov_mode = g_value_get_flags (value);
896 case PROP_VELOCITY_MIN:
897 priv->vmin = g_value_get_double (value);
899 case PROP_VELOCITY_MAX:
900 priv->vmax = g_value_get_double (value);
902 case PROP_VEL_MAX_OVERSHOOTING:
903 priv->vmax_overshooting = g_value_get_double (value);
905 case PROP_VELOCITY_FAST_FACTOR:
906 priv->vfast_factor = g_value_get_double (value);
908 case PROP_DECELERATION:
909 priv->decel = g_value_get_double (value);
910 hildon_pannable_calculate_vel_factor (HILDON_PANNABLE_AREA (object));
912 case PROP_DRAG_INERTIA:
913 priv->drag_inertia = g_value_get_double (value);
916 priv->sps = g_value_get_uint (value);
918 case PROP_PANNING_THRESHOLD:
920 GtkSettings *settings = gtk_settings_get_default ();
921 GtkSettingsValue svalue = { NULL, { 0, }, };
923 priv->panning_threshold = g_value_get_uint (value);
925 /* insure gtk dnd is the same we are using, not allowed
926 different thresholds in the same application */
927 svalue.origin = "panning_threshold";
928 g_value_init (&svalue.value, G_TYPE_LONG);
929 g_value_set_long (&svalue.value, priv->panning_threshold);
930 gtk_settings_set_property_value (settings, "gtk-dnd-drag-threshold", &svalue);
931 g_value_unset (&svalue.value);
934 case PROP_SCROLLBAR_FADE_DELAY:
935 /* convert to miliseconds */
936 priv->scrollbar_fade_delay = g_value_get_uint (value)/(SCROLL_FADE_TIMEOUT);
938 case PROP_BOUNCE_STEPS:
939 priv->bounce_steps = g_value_get_uint (value);
942 priv->force = g_value_get_uint (value);
944 case PROP_DIRECTION_ERROR_MARGIN:
945 priv->direction_error_margin = g_value_get_uint (value);
947 case PROP_VSCROLLBAR_POLICY:
948 priv->vscrollbar_policy = g_value_get_enum (value);
950 gtk_widget_queue_resize (GTK_WIDGET (object));
952 case PROP_HSCROLLBAR_POLICY:
953 priv->hscrollbar_policy = g_value_get_enum (value);
955 gtk_widget_queue_resize (GTK_WIDGET (object));
957 case PROP_VOVERSHOOT_MAX:
958 priv->vovershoot_max = g_value_get_int (value);
960 case PROP_HOVERSHOOT_MAX:
961 priv->hovershoot_max = g_value_get_int (value);
963 case PROP_SCROLL_TIME:
964 priv->scroll_time = g_value_get_double (value);
966 hildon_pannable_calculate_vel_factor (HILDON_PANNABLE_AREA (object));
968 case PROP_INITIAL_HINT:
969 priv->initial_hint = g_value_get_boolean (value);
971 case PROP_LOW_FRICTION_MODE:
972 priv->low_friction_mode = g_value_get_boolean (value);
974 case PROP_SIZE_REQUEST_POLICY:
975 hildon_pannable_area_set_size_request_policy (HILDON_PANNABLE_AREA (object),
976 g_value_get_enum (value));
978 case PROP_CENTER_ON_CHILD_FOCUS:
979 priv->center_on_child_focus = g_value_get_boolean (value);
983 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
988 hildon_pannable_area_dispose (GObject * object)
990 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (object)->priv;
991 GtkWidget *child = gtk_bin_get_child (GTK_BIN (object));
993 hildon_pannable_area_remove_timeouts (GTK_WIDGET (object));
996 g_signal_handlers_disconnect_by_func (child,
997 hildon_pannable_area_child_mapped,
1001 g_signal_handlers_disconnect_by_func (object,
1002 hildon_pannable_area_grab_notify,
1005 if (priv->hadjust) {
1006 g_signal_handlers_disconnect_by_func (priv->hadjust,
1007 hildon_pannable_area_adjust_value_changed,
1009 g_signal_handlers_disconnect_by_func (priv->hadjust,
1010 hildon_pannable_area_adjust_changed,
1012 g_object_unref (priv->hadjust);
1013 priv->hadjust = NULL;
1016 if (priv->vadjust) {
1017 g_signal_handlers_disconnect_by_func (priv->vadjust,
1018 hildon_pannable_area_adjust_value_changed,
1020 g_signal_handlers_disconnect_by_func (priv->vadjust,
1021 hildon_pannable_area_adjust_changed,
1023 g_object_unref (priv->vadjust);
1024 priv->vadjust = NULL;
1027 if (G_OBJECT_CLASS (hildon_pannable_area_parent_class)->dispose)
1028 G_OBJECT_CLASS (hildon_pannable_area_parent_class)->dispose (object);
1032 hildon_pannable_area_realize (GtkWidget * widget)
1034 GdkWindowAttr attributes;
1035 gint attributes_mask;
1037 HildonPannableAreaPrivate *priv;
1039 priv = HILDON_PANNABLE_AREA (widget)->priv;
1041 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
1043 border_width = GTK_CONTAINER (widget)->border_width;
1045 attributes.x = widget->allocation.x + border_width;
1046 attributes.y = widget->allocation.y + border_width;
1047 attributes.width = MAX (widget->allocation.width - 2 * border_width, 0);
1048 attributes.height = MAX (widget->allocation.height - 2 * border_width, 0);
1049 attributes.window_type = GDK_WINDOW_CHILD;
1051 /* avoid using the hildon_window */
1052 attributes.visual = gtk_widget_get_visual (widget);
1053 attributes.colormap = gtk_widget_get_colormap (widget);
1054 attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK;
1055 attributes.wclass = GDK_INPUT_OUTPUT;
1057 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1059 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
1060 &attributes, attributes_mask);
1061 gdk_window_set_user_data (widget->window, widget);
1063 /* create the events window */
1066 attributes.event_mask = gtk_widget_get_events (widget)
1067 | GDK_BUTTON_MOTION_MASK
1068 | GDK_BUTTON_PRESS_MASK
1069 | GDK_BUTTON_RELEASE_MASK
1071 | GDK_POINTER_MOTION_HINT_MASK
1072 | GDK_EXPOSURE_MASK | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK;
1073 attributes.wclass = GDK_INPUT_ONLY;
1075 attributes_mask = GDK_WA_X | GDK_WA_Y;
1077 priv->event_window = gdk_window_new (widget->window,
1078 &attributes, attributes_mask);
1079 gdk_window_set_user_data (priv->event_window, widget);
1081 widget->style = gtk_style_attach (widget->style, widget->window);
1082 gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
1084 priv->scrollbars_gc = gdk_gc_new (GDK_DRAWABLE (widget->window));
1085 gdk_gc_copy (priv->scrollbars_gc, widget->style->fg_gc[GTK_STATE_INSENSITIVE]);
1090 hildon_pannable_area_remove_timeouts (GtkWidget * widget)
1092 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1094 if (priv->idle_id) {
1095 g_signal_emit (widget, pannable_area_signals[PANNING_FINISHED], 0);
1096 g_source_remove (priv->idle_id);
1100 if (priv->scroll_indicator_timeout){
1101 g_source_remove (priv->scroll_indicator_timeout);
1102 priv->scroll_indicator_timeout = 0;
1105 if (priv->motion_event_scroll_timeout){
1106 g_source_remove (priv->motion_event_scroll_timeout);
1107 priv->motion_event_scroll_timeout = 0;
1112 hildon_pannable_area_unrealize (GtkWidget * widget)
1114 HildonPannableAreaPrivate *priv;
1116 priv = HILDON_PANNABLE_AREA (widget)->priv;
1118 hildon_pannable_area_remove_timeouts (widget);
1120 if (priv->event_window != NULL) {
1121 gdk_window_set_user_data (priv->event_window, NULL);
1122 gdk_window_destroy (priv->event_window);
1123 priv->event_window = NULL;
1126 gdk_gc_unref (priv->scrollbars_gc);
1128 if (GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->unrealize)
1129 (*GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->unrealize)(widget);
1133 hildon_pannable_area_size_request (GtkWidget * widget,
1134 GtkRequisition * requisition)
1136 GtkRequisition child_requisition = {0};
1137 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1138 GtkWidget *child = gtk_bin_get_child (GTK_BIN (widget));
1140 if (child && GTK_WIDGET_VISIBLE (child))
1142 gtk_widget_size_request (child, &child_requisition);
1145 if (priv->hscrollbar_policy == GTK_POLICY_NEVER) {
1146 requisition->width = child_requisition.width;
1148 switch (priv->size_request_policy) {
1149 case HILDON_SIZE_REQUEST_CHILDREN:
1150 requisition->width = MIN (PANNABLE_MAX_WIDTH,
1151 child_requisition.width);
1153 case HILDON_SIZE_REQUEST_MINIMUM:
1155 requisition->width = priv->indicator_width;
1159 if (priv->vscrollbar_policy == GTK_POLICY_NEVER) {
1160 requisition->height = child_requisition.height;
1162 switch (priv->size_request_policy) {
1163 case HILDON_SIZE_REQUEST_CHILDREN:
1164 requisition->height = MIN (PANNABLE_MAX_HEIGHT,
1165 child_requisition.height);
1167 case HILDON_SIZE_REQUEST_MINIMUM:
1169 requisition->height = priv->indicator_width;
1173 requisition->width += 2 * GTK_CONTAINER (widget)->border_width;
1174 requisition->height += 2 * GTK_CONTAINER (widget)->border_width;
1178 hildon_pannable_area_child_allocate_calculate (GtkWidget * widget,
1179 GtkAllocation * allocation,
1180 GtkAllocation * child_allocation)
1183 HildonPannableAreaPrivate *priv;
1185 border_width = GTK_CONTAINER (widget)->border_width;
1187 priv = HILDON_PANNABLE_AREA (widget)->priv;
1189 child_allocation->x = 0;
1190 child_allocation->y = 0;
1191 child_allocation->width = MAX (allocation->width - 2 * border_width -
1192 (priv->vscroll_visible ? priv->vscroll_rect.width : 0), 0);
1193 child_allocation->height = MAX (allocation->height - 2 * border_width -
1194 (priv->hscroll_visible ? priv->hscroll_rect.height : 0), 0);
1196 if (priv->overshot_dist_y > 0) {
1197 child_allocation->y = MIN (child_allocation->y + priv->overshot_dist_y,
1198 child_allocation->height);
1199 child_allocation->height = MAX (child_allocation->height - priv->overshot_dist_y, 0);
1200 } else if (priv->overshot_dist_y < 0) {
1201 child_allocation->height = MAX (child_allocation->height + priv->overshot_dist_y, 0);
1204 if (priv->overshot_dist_x > 0) {
1205 child_allocation->x = MIN (child_allocation->x + priv->overshot_dist_x,
1206 child_allocation->width);
1207 child_allocation->width = MAX (child_allocation->width - priv->overshot_dist_x, 0);
1208 } else if (priv->overshot_dist_x < 0) {
1209 child_allocation->width = MAX (child_allocation->width + priv->overshot_dist_x, 0);
1214 hildon_pannable_area_size_allocate (GtkWidget * widget,
1215 GtkAllocation * allocation)
1217 GtkAllocation child_allocation;
1218 HildonPannableAreaPrivate *priv;
1219 GtkWidget *child = gtk_bin_get_child (GTK_BIN (widget));
1223 border_width = GTK_CONTAINER (widget)->border_width;
1225 widget->allocation = *allocation;
1227 priv = HILDON_PANNABLE_AREA (widget)->priv;
1229 if (GTK_WIDGET_REALIZED (widget)) {
1230 gdk_window_move_resize (widget->window,
1231 allocation->x + border_width,
1232 allocation->y + border_width,
1233 allocation->width - border_width * 2,
1234 allocation->height - border_width * 2);
1235 gdk_window_move_resize (priv->event_window,
1238 allocation->width - border_width * 2,
1239 allocation->height - border_width * 2);
1242 if (child && GTK_WIDGET_VISIBLE (child)) {
1244 hildon_pannable_area_check_scrollbars (HILDON_PANNABLE_AREA (widget));
1246 hildon_pannable_area_child_allocate_calculate (widget,
1250 gtk_widget_size_allocate (child, &child_allocation);
1252 if (hildon_pannable_area_check_scrollbars (HILDON_PANNABLE_AREA (widget))) {
1253 hildon_pannable_area_child_allocate_calculate (widget,
1257 gtk_widget_size_allocate (child, &child_allocation);
1260 hv = priv->hadjust->value;
1261 vv = priv->vadjust->value;
1263 /* we have to do this after child size_allocate because page_size is
1264 * changed when we allocate the size of the children */
1265 if (priv->overshot_dist_y < 0) {
1266 priv->vadjust->value = priv->vadjust->upper - priv->vadjust->page_size;
1269 if (priv->overshot_dist_x < 0) {
1270 priv->hadjust->value = priv->hadjust->upper - priv->hadjust->page_size;
1273 if (hv != priv->hadjust->value)
1274 gtk_adjustment_value_changed (priv->hadjust);
1276 if (vv != priv->vadjust->value)
1277 gtk_adjustment_value_changed (priv->vadjust);
1280 hildon_pannable_area_check_scrollbars (HILDON_PANNABLE_AREA (widget));
1285 hildon_pannable_area_style_set (GtkWidget * widget,
1286 GtkStyle * previous_style)
1288 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1290 GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->
1291 style_set (widget, previous_style);
1293 gtk_style_lookup_color (widget->style, "SecondaryTextColor", &priv->scroll_color);
1294 gtk_widget_style_get (widget, "indicator-width", &priv->indicator_width, NULL);
1298 hildon_pannable_area_map (GtkWidget * widget)
1300 HildonPannableAreaPrivate *priv;
1302 priv = HILDON_PANNABLE_AREA (widget)->priv;
1304 gdk_window_show (widget->window);
1306 if (priv->event_window != NULL && !priv->enabled)
1307 gdk_window_show (priv->event_window);
1309 (*GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->map) (widget);
1311 if (priv->event_window != NULL && priv->enabled)
1312 gdk_window_show (priv->event_window);
1316 hildon_pannable_area_unmap (GtkWidget * widget)
1318 HildonPannableAreaPrivate *priv;
1320 priv = HILDON_PANNABLE_AREA (widget)->priv;
1322 if (priv->event_window != NULL)
1323 gdk_window_hide (priv->event_window);
1325 gdk_window_hide (widget->window);
1327 (*GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->unmap) (widget);
1331 hildon_pannable_area_grab_notify (GtkWidget *widget,
1332 gboolean was_grabbed,
1335 /* an internal widget has grabbed the focus and now has returned it,
1336 we have to do some release actions */
1338 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1340 priv->scroll_indicator_event_interrupt = 0;
1342 if ((!priv->scroll_indicator_timeout)&&(priv->scroll_indicator_alpha)>0.1) {
1343 priv->scroll_delay_counter = priv->scrollbar_fade_delay;
1345 hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget),
1346 priv->scroll_indicator_alpha);
1349 priv->last_type = 3;
1350 priv->moved = FALSE;
1354 #if USE_CAIRO_SCROLLBARS == 1
1357 rgb_from_gdkcolor (GdkColor *color, gdouble *r, gdouble *g, gdouble *b)
1359 *r = (color->red >> 8) / 255.0;
1360 *g = (color->green >> 8) / 255.0;
1361 *b = (color->blue >> 8) / 255.0;
1365 hildon_pannable_draw_vscroll (GtkWidget * widget,
1366 GdkColor *back_color,
1367 GdkColor *scroll_color)
1369 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1372 cairo_pattern_t *pattern;
1374 gint radius = (priv->vscroll_rect.width/2) - 1;
1376 cr = gdk_cairo_create(widget->window);
1378 /* Draw the background */
1379 rgb_from_gdkcolor (back_color, &r, &g, &b);
1380 cairo_set_source_rgb (cr, r, g, b);
1381 cairo_rectangle(cr, priv->vscroll_rect.x, priv->vscroll_rect.y,
1382 priv->vscroll_rect.width,
1383 priv->vscroll_rect.height);
1384 cairo_fill_preserve (cr);
1387 /* Calculate the scroll bar height and position */
1388 y = ((priv->vadjust->value - priv->vadjust->lower) / (priv->vadjust->upper - priv->vadjust->lower)) *
1389 (widget->allocation.height -
1390 (priv->hscroll_visible ? priv->indicator_width : 0));
1391 height = ((((priv->vadjust->value - priv->vadjust->lower) +
1392 priv->vadjust->page_size) /
1393 (priv->vadjust->upper - priv->vadjust->lower)) *
1394 (widget->allocation.height -
1395 (priv->hscroll_visible ? priv->indicator_width : 0))) - y;
1397 /* Set a minimum height */
1398 height = MAX (SCROLL_BAR_MIN_SIZE, height);
1400 /* Check the max y position */
1401 y = MIN (y, widget->allocation.height -
1402 (priv->hscroll_visible ? priv->hscroll_rect.height : 0) -
1405 /* Draw the scrollbar */
1406 rgb_from_gdkcolor (scroll_color, &r, &g, &b);
1408 pattern = cairo_pattern_create_linear(radius+1, y, radius+1,y + height);
1409 cairo_pattern_add_color_stop_rgb(pattern, 0, r, g, b);
1410 cairo_pattern_add_color_stop_rgb(pattern, 1, r/2, g/2, b/2);
1411 cairo_set_source(cr, pattern);
1413 cairo_pattern_destroy(pattern);
1415 cairo_arc(cr, priv->vscroll_rect.x + radius + 1, y + radius + 1, radius, G_PI, 0);
1416 cairo_line_to(cr, priv->vscroll_rect.x + (radius * 2) + 1, y + height - radius);
1417 cairo_arc(cr, priv->vscroll_rect.x + radius + 1, y + height - radius, radius, 0, G_PI);
1418 cairo_line_to(cr, priv->vscroll_rect.x + 1, y + height - radius);
1421 cairo_paint_with_alpha(cr, priv->scroll_indicator_alpha);
1427 hildon_pannable_draw_hscroll (GtkWidget * widget,
1428 GdkColor *back_color,
1429 GdkColor *scroll_color)
1431 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1434 cairo_pattern_t *pattern;
1436 gint radius = (priv->hscroll_rect.height/2) - 1;
1438 cr = gdk_cairo_create(widget->window);
1440 /* Draw the background */
1441 rgb_from_gdkcolor (back_color, &r, &g, &b);
1442 cairo_set_source_rgb (cr, r, g, b);
1443 cairo_rectangle(cr, priv->hscroll_rect.x, priv->hscroll_rect.y,
1444 priv->hscroll_rect.width,
1445 priv->hscroll_rect.height);
1446 cairo_fill_preserve (cr);
1449 /* calculate the scrollbar width and position */
1450 x = ((priv->hadjust->value - priv->hadjust->lower) / (priv->hadjust->upper - priv->hadjust->lower)) *
1451 (widget->allocation.width - (priv->vscroll_visible ? priv->indicator_width : 0));
1452 width =((((priv->hadjust->value - priv->hadjust->lower) +
1453 priv->hadjust->page_size) / (priv->hadjust->upper - priv->hadjust->lower)) *
1454 (widget->allocation.width -
1455 (priv->vscroll_visible ? priv->indicator_width : 0))) - x;
1457 /* Set a minimum width */
1458 width = MAX (SCROLL_BAR_MIN_SIZE, width);
1460 /* Check the max x position */
1461 x = MIN (x, widget->allocation.width -
1462 (priv->vscroll_visible ? priv->vscroll_rect.width : 0) -
1465 /* Draw the scrollbar */
1466 rgb_from_gdkcolor (scroll_color, &r, &g, &b);
1468 pattern = cairo_pattern_create_linear(x, radius+1, x+width, radius+1);
1469 cairo_pattern_add_color_stop_rgb(pattern, 0, r, g, b);
1470 cairo_pattern_add_color_stop_rgb(pattern, 1, r/2, g/2, b/2);
1471 cairo_set_source(cr, pattern);
1473 cairo_pattern_destroy(pattern);
1475 cairo_arc_negative(cr, x + radius + 1, priv->hscroll_rect.y + radius + 1, radius, 3*G_PI_2, G_PI_2);
1476 cairo_line_to(cr, x + width - radius, priv->hscroll_rect.y + (radius * 2) + 1);
1477 cairo_arc_negative(cr, x + width - radius, priv->hscroll_rect.y + radius + 1, radius, G_PI_2, 3*G_PI_2);
1478 cairo_line_to(cr, x + width - radius, priv->hscroll_rect.y + 1);
1481 cairo_paint_with_alpha(cr, priv->scroll_indicator_alpha);
1486 #else /* USE_CAIRO_SCROLLBARS */
1489 tranparency_color (GdkColor *color,
1492 gdouble transparency)
1496 diff = colora.red - colorb.red;
1497 color->red = colora.red-diff*transparency;
1499 diff = colora.green - colorb.green;
1500 color->green = colora.green-diff*transparency;
1502 diff = colora.blue - colorb.blue;
1503 color->blue = colora.blue-diff*transparency;
1507 hildon_pannable_draw_vscroll (GtkWidget *widget,
1508 GdkColor *back_color,
1509 GdkColor *scroll_color)
1511 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1513 GdkColor transp_color;
1514 GdkGC *gc = priv->scrollbars_gc;
1516 gdk_draw_rectangle (widget->window,
1517 widget->style->bg_gc[GTK_STATE_NORMAL],
1519 priv->vscroll_rect.x, priv->vscroll_rect.y,
1520 priv->vscroll_rect.width,
1521 priv->vscroll_rect.height);
1523 y = ((priv->vadjust->value - priv->vadjust->lower) / (priv->vadjust->upper - priv->vadjust->lower)) *
1524 (widget->allocation.height - (priv->hscroll_visible ? priv->indicator_width : 0));
1525 height = ((((priv->vadjust->value - priv->vadjust->lower) + priv->vadjust->page_size) /
1526 (priv->vadjust->upper - priv->vadjust->lower)) *
1527 (widget->allocation.height -
1528 (priv->hscroll_visible ? priv->indicator_width : 0))) - y;
1530 /* Set a minimum height */
1531 height = MAX (SCROLL_BAR_MIN_SIZE, height);
1533 /* Check the max y position */
1534 y = MIN (y, widget->allocation.height -
1535 (priv->hscroll_visible ? priv->hscroll_rect.height : 0) -
1538 if (priv->scroll_indicator_alpha == 1.0) {
1539 transp_color = priv->scroll_color;
1540 } else if (priv->scroll_indicator_alpha < 1.0) {
1541 tranparency_color (&transp_color, *back_color, *scroll_color,
1542 priv->scroll_indicator_alpha);
1544 gdk_gc_set_rgb_fg_color (gc, &transp_color);
1546 gdk_draw_rectangle (widget->window, gc,
1547 TRUE, priv->vscroll_rect.x, y,
1548 priv->vscroll_rect.width, height);
1552 hildon_pannable_draw_hscroll (GtkWidget *widget,
1553 GdkColor *back_color,
1554 GdkColor *scroll_color)
1556 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1558 GdkColor transp_color;
1559 GdkGC *gc = priv->scrollbars_gc;
1561 gdk_draw_rectangle (widget->window,
1562 widget->style->bg_gc[GTK_STATE_INSENSITIVE],
1564 priv->hscroll_rect.x, priv->hscroll_rect.y,
1565 priv->hscroll_rect.width,
1566 priv->hscroll_rect.height);
1568 /* calculate the scrollbar width and position */
1569 x = ((priv->hadjust->value - priv->hadjust->lower) / (priv->hadjust->upper - priv->hadjust->lower)) *
1570 (widget->allocation.width - (priv->vscroll_visible ? priv->indicator_width : 0));
1571 width =((((priv->hadjust->value - priv->hadjust->lower) +
1572 priv->hadjust->page_size) / (priv->hadjust->upper - priv->hadjust->lower)) *
1573 (widget->allocation.width -
1574 (priv->vscroll_visible ? priv->indicator_width : 0))) - x;
1576 /* Set a minimum width */
1577 width = MAX (SCROLL_BAR_MIN_SIZE, width);
1579 /* Check the max x position */
1580 x = MIN (x, widget->allocation.width -
1581 (priv->vscroll_visible ? priv->vscroll_rect.width : 0) -
1584 if (priv->scroll_indicator_alpha == 1.0) {
1585 transp_color = priv->scroll_color;
1586 } else if (priv->scroll_indicator_alpha < 1.0) {
1587 tranparency_color (&transp_color, *back_color, *scroll_color,
1588 priv->scroll_indicator_alpha);
1590 gdk_gc_set_rgb_fg_color (gc, &transp_color);
1592 gdk_draw_rectangle (widget->window, gc,
1593 TRUE, x, priv->hscroll_rect.y, width,
1594 priv->hscroll_rect.height);
1597 #endif /* USE_CAIRO_SCROLLBARS */
1600 hildon_pannable_area_initial_effect (GtkWidget * widget)
1602 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1604 if (priv->initial_hint) {
1605 if (priv->vscroll_visible || priv->hscroll_visible) {
1607 priv->scroll_indicator_event_interrupt = 0;
1608 priv->scroll_delay_counter = priv->scrollbar_fade_delay;
1610 hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget), 1.0);
1616 hildon_pannable_area_launch_fade_timeout (HildonPannableArea * area,
1619 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (area)->priv;
1621 priv->scroll_indicator_alpha = alpha;
1623 if (!priv->scroll_indicator_timeout)
1624 priv->scroll_indicator_timeout =
1625 gdk_threads_add_timeout_full (G_PRIORITY_HIGH_IDLE + 20,
1626 SCROLL_FADE_TIMEOUT,
1627 (GSourceFunc) hildon_pannable_area_scroll_indicator_fade,
1633 hildon_pannable_area_adjust_changed (HildonPannableArea * area,
1636 if (GTK_WIDGET_REALIZED (area))
1637 hildon_pannable_area_refresh (area);
1641 hildon_pannable_area_adjust_value_changed (HildonPannableArea * area,
1644 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (area)->priv;
1646 gint x = priv->x_offset;
1647 gint y = priv->y_offset;
1649 priv->x_offset = priv->hadjust->value;
1650 xdiff = x - priv->x_offset;
1651 priv->y_offset = priv->vadjust->value;
1652 ydiff = y - priv->y_offset;
1654 if ((xdiff || ydiff) && GTK_WIDGET_DRAWABLE (area)) {
1655 hildon_pannable_area_redraw (area);
1657 if ((priv->vscroll_visible) || (priv->hscroll_visible)) {
1658 priv->scroll_indicator_event_interrupt = 0;
1659 priv->scroll_delay_counter = priv->scrollbar_fade_delay;
1661 hildon_pannable_area_launch_fade_timeout (area, 1.0);
1667 hildon_pannable_area_redraw (HildonPannableArea * area)
1669 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (area)->priv;
1671 /* Redraw scroll indicators */
1672 if (GTK_WIDGET_DRAWABLE (area)) {
1673 if (priv->hscroll_visible) {
1674 gdk_window_invalidate_rect (GTK_WIDGET (area)->window,
1675 &priv->hscroll_rect, FALSE);
1678 if (priv->vscroll_visible) {
1679 gdk_window_invalidate_rect (GTK_WIDGET (area)->window,
1680 &priv->vscroll_rect, FALSE);
1686 hildon_pannable_area_scroll_indicator_fade(HildonPannableArea * area)
1688 HildonPannableAreaPrivate *priv = area->priv;
1690 /* if moving do not fade out */
1691 if (((ABS (priv->vel_y)>priv->vmin)||
1692 (ABS (priv->vel_x)>priv->vmin))&&(!priv->button_pressed)) {
1697 if (priv->scroll_indicator_event_interrupt) {
1698 /* Stop a fade out, and fade back in */
1699 if (priv->scroll_indicator_alpha > 0.9) {
1700 priv->scroll_indicator_alpha = 1.0;
1701 priv->scroll_indicator_timeout = 0;
1705 priv->scroll_indicator_alpha += 0.2;
1706 hildon_pannable_area_redraw (area);
1712 if ((priv->scroll_indicator_alpha > 0.9) &&
1713 (priv->scroll_delay_counter > 0)) {
1714 priv->scroll_delay_counter--;
1719 if (!priv->scroll_indicator_event_interrupt) {
1720 /* Continue fade out */
1721 if (priv->scroll_indicator_alpha < 0.1) {
1722 priv->scroll_indicator_timeout = 0;
1723 priv->scroll_indicator_alpha = 0.0;
1727 priv->scroll_indicator_alpha -= 0.2;
1728 hildon_pannable_area_redraw (area);
1738 hildon_pannable_area_expose_event (GtkWidget * widget,
1739 GdkEventExpose * event)
1742 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1743 #if USE_CAIRO_SCROLLBARS == 1
1744 GdkColor back_color = widget->style->bg[GTK_STATE_NORMAL];
1745 GdkColor scroll_color = widget->style->base[GTK_STATE_SELECTED];
1746 #else /* USE_CAIRO_SCROLLBARS */
1747 GdkColor back_color = widget->style->bg[GTK_STATE_NORMAL];
1748 GdkColor scroll_color = priv->scroll_color;
1751 if (G_UNLIKELY (priv->initial_effect)) {
1752 hildon_pannable_area_initial_effect (widget);
1754 priv->initial_effect = FALSE;
1757 if (gtk_bin_get_child (GTK_BIN (widget))) {
1759 if (priv->scroll_indicator_alpha > 0.1) {
1760 if (priv->vscroll_visible) {
1761 hildon_pannable_draw_vscroll (widget, &back_color, &scroll_color);
1763 if (priv->hscroll_visible) {
1764 hildon_pannable_draw_hscroll (widget, &back_color, &scroll_color);
1768 /* draw overshooting rectangles */
1769 if (priv->overshot_dist_y > 0) {
1770 gint overshot_height;
1772 overshot_height = MIN (priv->overshot_dist_y, widget->allocation.height -
1773 (priv->hscroll_visible ? priv->hscroll_rect.height : 0));
1775 gdk_draw_rectangle (widget->window,
1776 widget->style->bg_gc[GTK_STATE_NORMAL],
1780 widget->allocation.width -
1781 (priv->vscroll_visible ? priv->vscroll_rect.width : 0),
1783 } else if (priv->overshot_dist_y < 0) {
1784 gint overshot_height;
1788 MAX (priv->overshot_dist_y,
1789 -(widget->allocation.height -
1790 (priv->hscroll_visible ? priv->hscroll_rect.height : 0)));
1792 overshot_y = MAX (widget->allocation.height +
1794 (priv->hscroll_visible ? priv->hscroll_rect.height : 0), 0);
1796 gdk_draw_rectangle (widget->window,
1797 widget->style->bg_gc[GTK_STATE_NORMAL],
1801 widget->allocation.width -
1802 priv->vscroll_rect.width,
1806 if (priv->overshot_dist_x > 0) {
1807 gint overshot_width;
1809 overshot_width = MIN (priv->overshot_dist_x, widget->allocation.width -
1810 (priv->vscroll_visible ? priv->vscroll_rect.width : 0));
1812 gdk_draw_rectangle (widget->window,
1813 widget->style->bg_gc[GTK_STATE_NORMAL],
1818 widget->allocation.height -
1819 (priv->hscroll_visible ? priv->hscroll_rect.height : 0));
1820 } else if (priv->overshot_dist_x < 0) {
1821 gint overshot_width;
1825 MAX (priv->overshot_dist_x,
1826 -(widget->allocation.width -
1827 (priv->vscroll_visible ? priv->vscroll_rect.width : 0)));
1829 overshot_x = MAX (widget->allocation.width +
1831 (priv->vscroll_visible ? priv->vscroll_rect.width : 0), 0);
1833 gdk_draw_rectangle (widget->window,
1834 widget->style->bg_gc[GTK_STATE_NORMAL],
1839 widget->allocation.height -
1840 priv->hscroll_rect.height);
1845 return GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->expose_event (widget, event);
1849 hildon_pannable_area_get_topmost (GdkWindow * window,
1851 gint * tx, gint * ty,
1854 /* Find the GdkWindow at the given point, by recursing from a given
1855 * parent GdkWindow. Optionally return the co-ordinates transformed
1856 * relative to the child window.
1859 GList *c, *children;
1860 GdkWindow *selected_window = NULL;
1862 gdk_drawable_get_size (GDK_DRAWABLE (window), &width, &height);
1863 if ((x < 0) || (x >= width) || (y < 0) || (y >= height))
1866 children = gdk_window_peek_children (window);
1873 selected_window = window;
1876 for (c = children; c; c = c->next) {
1877 GdkWindow *child = (GdkWindow *) c->data;
1880 gdk_drawable_get_size (GDK_DRAWABLE (child), &width, &height);
1881 gdk_window_get_position (child, &wx, &wy);
1883 if ((x >= wx) && (x < (wx + width)) && (y >= wy) && (y < (wy + height)) &&
1884 (gdk_window_is_visible (child))) {
1886 if (gdk_window_peek_children (child)) {
1887 selected_window = hildon_pannable_area_get_topmost (child, x-wx, y-wy,
1889 if (!selected_window) {
1894 selected_window = child;
1897 if ((gdk_window_get_events (child)&mask)) {
1902 selected_window = child;
1908 return selected_window;
1912 synth_crossing (GdkWindow * child,
1914 gint x_root, gint y_root,
1915 guint32 time, gboolean in)
1917 GdkEventCrossing *crossing_event;
1918 GdkEventType type = in ? GDK_ENTER_NOTIFY : GDK_LEAVE_NOTIFY;
1920 /* Send synthetic enter event */
1921 crossing_event = (GdkEventCrossing *) gdk_event_new (type);
1922 ((GdkEventAny *) crossing_event)->type = type;
1923 ((GdkEventAny *) crossing_event)->window = g_object_ref (child);
1924 ((GdkEventAny *) crossing_event)->send_event = FALSE;
1925 crossing_event->subwindow = g_object_ref (child);
1926 crossing_event->time = time;
1927 crossing_event->x = x;
1928 crossing_event->y = y;
1929 crossing_event->x_root = x_root;
1930 crossing_event->y_root = y_root;
1931 crossing_event->mode = GDK_CROSSING_NORMAL;
1932 crossing_event->detail = GDK_NOTIFY_UNKNOWN;
1933 crossing_event->focus = FALSE;
1934 crossing_event->state = 0;
1935 gdk_event_put ((GdkEvent *) crossing_event);
1936 gdk_event_free ((GdkEvent *) crossing_event);
1940 hildon_pannable_area_button_press_cb (GtkWidget * widget,
1941 GdkEventButton * event)
1944 HildonPannableArea *area = HILDON_PANNABLE_AREA (widget);
1945 HildonPannableAreaPrivate *priv = area->priv;
1947 if ((!priv->enabled) || (event->button != 1) ||
1948 ((event->time == priv->last_time) &&
1949 (priv->last_type == 1)) || (gtk_bin_get_child (GTK_BIN (widget)) == NULL))
1952 priv->scroll_indicator_event_interrupt = 1;
1954 hildon_pannable_area_launch_fade_timeout (area,
1955 priv->scroll_indicator_alpha);
1957 priv->last_time = event->time;
1958 priv->last_press_time = event->time;
1959 priv->last_type = 1;
1961 priv->scroll_to_x = -1;
1962 priv->scroll_to_y = -1;
1964 if (priv->button_pressed && priv->child) {
1965 /* Widget stole focus on last click, send crossing-out event */
1966 synth_crossing (priv->child, 0, 0, event->x_root, event->y_root,
1967 event->time, FALSE);
1975 /* Don't allow a click if we're still moving fast */
1976 if ((ABS (priv->vel_x) <= (priv->vmax * priv->vfast_factor)) &&
1977 (ABS (priv->vel_y) <= (priv->vmax * priv->vfast_factor)))
1979 hildon_pannable_area_get_topmost (gtk_bin_get_child (GTK_BIN (widget))->window,
1980 event->x, event->y, &x, &y, GDK_BUTTON_PRESS_MASK);
1984 priv->button_pressed = TRUE;
1986 /* Stop scrolling on mouse-down (so you can flick, then hold to stop) */
1987 priv->old_vel_x = priv->vel_x;
1988 priv->old_vel_y = priv->vel_y;
1991 if (priv->idle_id) {
1992 g_source_remove (priv->idle_id);
1994 g_signal_emit (area, pannable_area_signals[PANNING_FINISHED], 0);
1999 gdk_drawable_get_size (priv->child, &priv->child_width,
2000 &priv->child_height);
2001 priv->last_in = TRUE;
2003 g_object_add_weak_pointer ((GObject *) priv->child,
2004 (gpointer) & priv->child);
2006 event = (GdkEventButton *) gdk_event_copy ((GdkEvent *) event);
2007 /* remove the reference we added with the copy */
2008 g_object_unref (priv->event_window);
2014 synth_crossing (priv->child, x, y, event->x_root,
2015 event->y_root, event->time, TRUE);
2017 /* Send synthetic click (button press/release) event */
2018 ((GdkEventAny *) event)->window = g_object_ref (priv->child);
2020 gdk_event_put ((GdkEvent *) event);
2021 gdk_event_free ((GdkEvent *) event);
2029 hildon_pannable_area_check_scrollbars (HildonPannableArea * area)
2031 HildonPannableAreaPrivate *priv = area->priv;
2032 gboolean prev_hscroll_visible, prev_vscroll_visible;
2034 prev_hscroll_visible = priv->hscroll_visible;
2035 prev_vscroll_visible = priv->vscroll_visible;
2037 if (!gtk_bin_get_child (GTK_BIN (area))) {
2038 priv->vscroll_visible = FALSE;
2039 priv->hscroll_visible = FALSE;
2041 switch (priv->hscrollbar_policy) {
2042 case GTK_POLICY_ALWAYS:
2043 priv->hscroll_visible = TRUE;
2045 case GTK_POLICY_NEVER:
2046 priv->hscroll_visible = FALSE;
2049 priv->hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
2050 priv->hadjust->page_size);
2053 switch (priv->vscrollbar_policy) {
2054 case GTK_POLICY_ALWAYS:
2055 priv->vscroll_visible = TRUE;
2057 case GTK_POLICY_NEVER:
2058 priv->vscroll_visible = FALSE;
2061 priv->vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
2062 priv->vadjust->page_size);
2065 /* Store the vscroll/hscroll areas for redrawing */
2066 if (priv->vscroll_visible) {
2067 GtkAllocation *allocation = >K_WIDGET (area)->allocation;
2068 priv->vscroll_rect.x = allocation->width - priv->indicator_width;
2069 priv->vscroll_rect.y = 0;
2070 priv->vscroll_rect.width = priv->indicator_width;
2071 priv->vscroll_rect.height = allocation->height -
2072 (priv->hscroll_visible ? priv->indicator_width : 0);
2074 if (priv->hscroll_visible) {
2075 GtkAllocation *allocation = >K_WIDGET (area)->allocation;
2076 priv->hscroll_rect.y = allocation->height - priv->indicator_width;
2077 priv->hscroll_rect.x = 0;
2078 priv->hscroll_rect.height = priv->indicator_width;
2079 priv->hscroll_rect.width = allocation->width -
2080 (priv->vscroll_visible ? priv->indicator_width : 0);
2084 return ((priv->hscroll_visible != prev_hscroll_visible) ||
2085 (priv->vscroll_visible != prev_vscroll_visible));
2089 hildon_pannable_area_refresh (HildonPannableArea * area)
2091 if (GTK_WIDGET_DRAWABLE (area) &&
2092 hildon_pannable_area_check_scrollbars (area)) {
2093 HildonPannableAreaPrivate *priv = area->priv;
2095 gtk_widget_queue_resize (GTK_WIDGET (area));
2097 if ((priv->vscroll_visible) || (priv->hscroll_visible)) {
2098 priv->scroll_indicator_event_interrupt = 0;
2099 priv->scroll_delay_counter = area->priv->scrollbar_fade_delay;
2101 hildon_pannable_area_launch_fade_timeout (area, 1.0);
2104 hildon_pannable_area_redraw (area);
2108 /* Scroll by a particular amount (in pixels). Optionally, return if
2109 * the scroll on a particular axis was successful.
2112 hildon_pannable_axis_scroll (HildonPannableArea *area,
2113 GtkAdjustment *adjust,
2117 gint *overshot_dist,
2123 HildonPannableAreaPrivate *priv = area->priv;
2125 dist = gtk_adjustment_get_value (adjust) - inc;
2128 * We use overshot_dist to define the distance of the current overshoot,
2129 * and overshooting to define the direction/whether or not we are overshot
2131 if (!(*overshooting)) {
2133 /* Initiation of the overshoot happens when the finger is released
2134 * and the current position of the pannable contents are out of range
2136 if (dist < adjust->lower) {
2139 dist = adjust->lower;
2141 if (overshoot_max!=0) {
2144 *overshot_dist = CLAMP (*overshot_dist + *vel, 0, overshoot_max);
2145 *vel = MIN (priv->vmax_overshooting, *vel);
2146 gtk_widget_queue_resize (GTK_WIDGET (area));
2150 } else if (dist > adjust->upper - adjust->page_size) {
2153 dist = adjust->upper - adjust->page_size;
2155 if (overshoot_max!=0) {
2158 *overshot_dist = CLAMP (*overshot_dist + *vel, -overshoot_max, 0);
2159 *vel = MAX (-priv->vmax_overshooting, *vel);
2160 gtk_widget_queue_resize (GTK_WIDGET (area));
2165 if ((*scroll_to) != -1) {
2166 if (((inc < 0)&&(*scroll_to <= dist))||
2167 ((inc > 0)&&(*scroll_to >= dist))) {
2175 adjust->value = dist;
2177 if (!priv->button_pressed) {
2179 /* When the overshoot has started we continue for
2180 * PROP_BOUNCE_STEPS more steps into the overshoot before we
2181 * reverse direction. The deceleration factor is calculated
2182 * based on the percentage distance from the first item with
2183 * each iteration, therefore always returning us to the
2184 * top/bottom most element
2186 if (*overshot_dist > 0) {
2188 if ((*overshooting < priv->bounce_steps) && (*vel > 0)) {
2190 *vel = (((gdouble)*overshot_dist)/overshoot_max) * (*vel);
2191 } else if ((*overshooting >= priv->bounce_steps) && (*vel > 0)) {
2193 } else if ((*overshooting > 1) && (*vel < 0)) {
2194 /* we add the MIN in order to avoid very small speeds */
2195 *vel = MIN (((((gdouble)*overshot_dist)*0.8) * -1), -10.0);
2198 *overshot_dist = CLAMP (*overshot_dist + *vel, 0, overshoot_max);
2200 gtk_widget_queue_resize (GTK_WIDGET (area));
2202 } else if (*overshot_dist < 0) {
2204 if ((*overshooting < priv->bounce_steps) && (*vel < 0)) {
2206 *vel = (((gdouble)*overshot_dist)/overshoot_max) * (*vel) * -1;
2207 } else if ((*overshooting >= priv->bounce_steps) && (*vel < 0)) {
2209 } else if ((*overshooting > 1) && (*vel > 0)) {
2210 /* we add the MAX in order to avoid very small speeds */
2211 *vel = MAX (((((gdouble)*overshot_dist)*0.8) * -1), 10.0);
2214 *overshot_dist = CLAMP (*overshot_dist + (*vel), -overshoot_max, 0);
2216 gtk_widget_queue_resize (GTK_WIDGET (area));
2221 gtk_widget_queue_resize (GTK_WIDGET (area));
2225 gint overshot_dist_old = *overshot_dist;
2227 if (*overshot_dist > 0) {
2228 *overshot_dist = CLAMP ((*overshot_dist) + inc, 0, overshoot_max);
2229 } else if (*overshot_dist < 0) {
2230 *overshot_dist = CLAMP ((*overshot_dist) + inc, -1 * overshoot_max, 0);
2233 adjust->value = CLAMP (dist,
2239 if (*overshot_dist != overshot_dist_old)
2240 gtk_widget_queue_resize (GTK_WIDGET (area));
2246 hildon_pannable_area_scroll (HildonPannableArea *area,
2247 gdouble x, gdouble y)
2250 HildonPannableAreaPrivate *priv = area->priv;
2251 gboolean hscroll_visible, vscroll_visible;
2254 if (gtk_bin_get_child (GTK_BIN (area)) == NULL)
2257 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
2258 priv->vadjust->page_size);
2259 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
2260 priv->hadjust->page_size);
2265 hv = priv->hadjust->value;
2266 vv = priv->vadjust->value;
2268 if (vscroll_visible) {
2269 hildon_pannable_axis_scroll (area, priv->vadjust, &priv->vel_y, y,
2270 &priv->overshooting_y, &priv->overshot_dist_y,
2271 &priv->scroll_to_y, priv->vovershoot_max, &sy);
2276 if (hscroll_visible) {
2277 hildon_pannable_axis_scroll (area, priv->hadjust, &priv->vel_x, x,
2278 &priv->overshooting_x, &priv->overshot_dist_x,
2279 &priv->scroll_to_x, priv->hovershoot_max, &sx);
2284 if (hv != priv->hadjust->value)
2285 gtk_adjustment_value_changed (priv->hadjust);
2287 if (vv != priv->vadjust->value)
2288 gtk_adjustment_value_changed (priv->vadjust);
2290 /* If the scroll on a particular axis wasn't succesful, reset the
2291 * initial scroll position to the new mouse co-ordinate. This means
2292 * when you get to the top of the page, dragging down works immediately.
2294 if (priv->mode == HILDON_PANNABLE_AREA_MODE_ACCEL) {
2306 hildon_pannable_area_timeout (HildonPannableArea * area)
2308 HildonPannableAreaPrivate *priv = area->priv;
2310 if ((!priv->enabled) || (priv->mode == HILDON_PANNABLE_AREA_MODE_PUSH)) {
2312 g_signal_emit (area, pannable_area_signals[PANNING_FINISHED], 0);
2317 if (!priv->button_pressed) {
2318 /* Decelerate gradually when pointer is raised */
2319 if ((!priv->overshot_dist_y) &&
2320 (!priv->overshot_dist_x)) {
2322 /* in case we move to a specific point do not decelerate when arriving */
2323 if ((priv->scroll_to_x != -1)||(priv->scroll_to_y != -1)) {
2325 if (ABS (priv->vel_x) >= 1.5) {
2326 priv->vel_x *= priv->decel;
2329 if (ABS (priv->vel_y) >= 1.5) {
2330 priv->vel_y *= priv->decel;
2334 if ((!priv->low_friction_mode) ||
2335 ((priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ) &&
2336 (ABS (priv->vel_x) < 0.8*priv->vmax)))
2337 priv->vel_x *= priv->decel;
2339 if ((!priv->low_friction_mode) ||
2340 ((priv->mov_mode&HILDON_MOVEMENT_MODE_VERT) &&
2341 (ABS (priv->vel_y) < 0.8*priv->vmax)))
2342 priv->vel_y *= priv->decel;
2344 if ((ABS (priv->vel_x) < 1.0) && (ABS (priv->vel_y) < 1.0)) {
2349 g_signal_emit (area, pannable_area_signals[PANNING_FINISHED], 0);
2355 } else if (priv->mode == HILDON_PANNABLE_AREA_MODE_AUTO) {
2361 hildon_pannable_area_scroll (area, priv->vel_x, priv->vel_y);
2363 gdk_window_process_updates (GTK_WIDGET (area)->window, FALSE);
2369 hildon_pannable_area_calculate_velocity (gdouble *vel,
2373 gdouble drag_inertia,
2379 if (ABS (dist) >= RATIO_TOLERANCE) {
2380 rawvel = (dist / ABS (delta)) * force;
2381 *vel = *vel * (1 - drag_inertia) +
2382 rawvel * drag_inertia;
2383 *vel = *vel > 0 ? MIN (*vel, vmax)
2384 : MAX (*vel, -1 * vmax);
2389 hildon_pannable_area_motion_event_scroll_timeout (HildonPannableArea *area)
2391 HildonPannableAreaPrivate *priv = area->priv;
2393 if ((priv->motion_x != 0)||(priv->motion_y != 0))
2394 hildon_pannable_area_scroll (area, priv->motion_x, priv->motion_y);
2396 priv->motion_event_scroll_timeout = 0;
2402 hildon_pannable_area_motion_event_scroll (HildonPannableArea *area,
2403 gdouble x, gdouble y)
2405 HildonPannableAreaPrivate *priv = area->priv;
2407 if (priv->motion_event_scroll_timeout) {
2409 priv->motion_x += x;
2410 priv->motion_y += y;
2414 /* we do not delay the first event but the next ones */
2415 hildon_pannable_area_scroll (area, x, y);
2420 priv->motion_event_scroll_timeout = gdk_threads_add_timeout_full
2421 (G_PRIORITY_HIGH_IDLE + 20,
2422 (gint) (1000.0 / (gdouble) MOTION_EVENTS_PER_SECOND),
2423 (GSourceFunc) hildon_pannable_area_motion_event_scroll_timeout, area, NULL);
2428 hildon_pannable_area_check_move (HildonPannableArea *area,
2429 GdkEventMotion * event,
2433 HildonPannableAreaPrivate *priv = area->priv;
2435 if (priv->first_drag && (!priv->moved) &&
2436 ((ABS (*x) > (priv->panning_threshold))
2437 || (ABS (*y) > (priv->panning_threshold)))) {
2442 if (priv->first_drag) {
2443 gboolean vscroll_visible;
2444 gboolean hscroll_visible;
2446 if (ABS (priv->iy - event->y) >=
2447 ABS (priv->ix - event->x)) {
2449 g_signal_emit (area,
2450 pannable_area_signals[VERTICAL_MOVEMENT],
2451 0, (priv->iy > event->y) ?
2452 HILDON_MOVEMENT_UP :
2453 HILDON_MOVEMENT_DOWN,
2454 (gdouble)priv->ix, (gdouble)priv->iy);
2456 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
2457 priv->vadjust->page_size);
2459 if (!((vscroll_visible)&&
2460 (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT))) {
2462 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
2463 priv->hadjust->page_size);
2465 /* even in case we do not have to move we check if this
2466 could be a fake horizontal movement */
2467 if (!((hscroll_visible)&&
2468 (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ)) ||
2469 (ABS (priv->iy - event->y) -
2470 ABS (priv->ix - event->x) >= priv->direction_error_margin))
2471 priv->moved = FALSE;
2475 g_signal_emit (area,
2476 pannable_area_signals[HORIZONTAL_MOVEMENT],
2477 0, (priv->ix > event->x) ?
2478 HILDON_MOVEMENT_LEFT :
2479 HILDON_MOVEMENT_RIGHT,
2480 (gdouble)priv->ix, (gdouble)priv->iy);
2482 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
2483 priv->hadjust->page_size);
2485 if (!((hscroll_visible)&&
2486 (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ))) {
2488 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
2489 priv->vadjust->page_size);
2491 /* even in case we do not have to move we check if this
2492 could be a fake vertical movement */
2493 if (!((vscroll_visible) &&
2494 (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT)) ||
2495 (ABS (priv->ix - event->x) -
2496 ABS (priv->iy - event->y) >= priv->direction_error_margin))
2497 priv->moved = FALSE;
2501 if ((priv->moved)&&(priv->child)) {
2504 pos_x = priv->cx + (event->x - priv->ix);
2505 pos_y = priv->cy + (event->y - priv->iy);
2507 synth_crossing (priv->child, pos_x, pos_y, event->x_root,
2508 event->y_root, event->time, FALSE);
2512 gboolean result_val;
2514 g_signal_emit (area,
2515 pannable_area_signals[PANNING_STARTED],
2518 priv->moved = !result_val;
2522 priv->first_drag = FALSE;
2524 if ((priv->mode != HILDON_PANNABLE_AREA_MODE_PUSH) &&
2525 (priv->mode != HILDON_PANNABLE_AREA_MODE_AUTO)) {
2528 priv->idle_id = gdk_threads_add_timeout_full
2529 (G_PRIORITY_HIGH_IDLE + 20,
2530 (gint)(1000.0 / (gdouble) priv->sps),
2532 hildon_pannable_area_timeout, area, NULL);
2538 hildon_pannable_area_handle_move (HildonPannableArea *area,
2539 GdkEventMotion * event,
2543 HildonPannableAreaPrivate *priv = area->priv;
2546 switch (priv->mode) {
2547 case HILDON_PANNABLE_AREA_MODE_PUSH:
2548 /* Scroll by the amount of pixels the cursor has moved
2549 * since the last motion event.
2551 hildon_pannable_area_motion_event_scroll (area, *x, *y);
2555 case HILDON_PANNABLE_AREA_MODE_ACCEL:
2556 /* Set acceleration relative to the initial click */
2557 priv->ex = event->x;
2558 priv->ey = event->y;
2559 priv->vel_x = ((*x > 0) ? 1 : -1) *
2561 (gdouble) GTK_WIDGET (area)->allocation.width) *
2562 (priv->vmax - priv->vmin)) + priv->vmin);
2563 priv->vel_y = ((*y > 0) ? 1 : -1) *
2565 (gdouble) GTK_WIDGET (area)->allocation.height) *
2566 (priv->vmax - priv->vmin)) + priv->vmin);
2568 case HILDON_PANNABLE_AREA_MODE_AUTO:
2570 delta = event->time - priv->last_time;
2572 if (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT) {
2573 gdouble dist = event->y - priv->y;
2575 hildon_pannable_area_calculate_velocity (&priv->vel_y,
2587 if (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ) {
2588 gdouble dist = event->x - priv->x;
2590 hildon_pannable_area_calculate_velocity (&priv->vel_x,
2602 hildon_pannable_area_motion_event_scroll (area, *x, *y);
2604 if (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ)
2606 if (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT)
2616 hildon_pannable_area_motion_notify_cb (GtkWidget * widget,
2617 GdkEventMotion * event)
2619 HildonPannableArea *area = HILDON_PANNABLE_AREA (widget);
2620 HildonPannableAreaPrivate *priv = area->priv;
2623 if (gtk_bin_get_child (GTK_BIN (widget)) == NULL)
2626 if ((!priv->enabled) || (!priv->button_pressed) ||
2627 ((event->time == priv->last_time) && (priv->last_type == 2))) {
2628 gdk_window_get_pointer (widget->window, NULL, NULL, 0);
2632 if (priv->last_type == 1) {
2633 priv->first_drag = TRUE;
2636 x = event->x - priv->x;
2637 y = event->y - priv->y;
2640 hildon_pannable_area_check_move (area, event, &x, &y);
2644 hildon_pannable_area_handle_move (area, event, &x, &y);
2645 } else if (priv->child) {
2649 pos_x = priv->cx + (event->x - priv->ix);
2650 pos_y = priv->cy + (event->y - priv->iy);
2652 in = (((0 <= pos_x)&&(priv->child_width >= pos_x)) &&
2653 ((0 <= pos_y)&&(priv->child_height >= pos_y)));
2655 if (((!priv->last_in)&&in)||((priv->last_in)&&(!in))) {
2657 synth_crossing (priv->child, pos_x, pos_y, event->x_root,
2658 event->y_root, event->time, in);
2664 priv->last_time = event->time;
2665 priv->last_type = 2;
2668 /* Send motion notify to child */
2669 event = (GdkEventMotion *) gdk_event_copy ((GdkEvent *) event);
2670 /* remove the reference we added with the copy */
2671 g_object_unref (priv->event_window);
2672 event->x = priv->cx + (event->x - priv->ix);
2673 event->y = priv->cy + (event->y - priv->iy);
2674 event->window = g_object_ref (priv->child);
2675 gdk_event_put ((GdkEvent *) event);
2676 gdk_event_free ((GdkEvent *) event);
2679 gdk_window_get_pointer (widget->window, NULL, NULL, 0);
2685 hildon_pannable_leave_notify_event (GtkWidget *widget,
2686 GdkEventCrossing *event)
2688 HildonPannableArea *area = HILDON_PANNABLE_AREA (widget);
2689 HildonPannableAreaPrivate *priv = area->priv;
2691 if ((priv->child)&&(priv->last_in)) {
2692 priv->last_in = FALSE;
2694 synth_crossing (priv->child, 0, 0, event->x_root,
2695 event->y_root, event->time, FALSE);
2702 hildon_pannable_area_button_release_cb (GtkWidget * widget,
2703 GdkEventButton * event)
2705 HildonPannableArea *area = HILDON_PANNABLE_AREA (widget);
2706 HildonPannableAreaPrivate *priv = area->priv;
2710 gboolean force_fast = TRUE;
2712 if (((event->time == priv->last_time) && (priv->last_type == 3))
2713 || (gtk_bin_get_child (GTK_BIN (widget)) == NULL)
2714 || (!priv->button_pressed) || (!priv->enabled) || (event->button != 1))
2717 /* if last event was a motion-notify we have to check the movement
2718 and launch the animation */
2719 if (priv->last_type == 2) {
2721 dx = event->x - priv->x;
2722 dy = event->y - priv->y;
2724 hildon_pannable_area_check_move (area, (GdkEventMotion *) event, &dx, &dy);
2727 gdouble delta = event->time - priv->last_time;
2729 hildon_pannable_area_handle_move (area, (GdkEventMotion *) event, &dx, &dy);
2731 /* move all the way to the last position now */
2732 if (priv->motion_event_scroll_timeout) {
2733 g_source_remove (priv->motion_event_scroll_timeout);
2734 hildon_pannable_area_motion_event_scroll_timeout (HILDON_PANNABLE_AREA (widget));
2739 if ((ABS (dx) < 4.0) && (delta >= CURSOR_STOPPED_TIMEOUT))
2742 if ((ABS (dy) < 4.0) && (delta >= CURSOR_STOPPED_TIMEOUT))
2747 /* If overshoot has been initiated with a finger down, on release set max speed */
2748 if (priv->overshot_dist_y != 0) {
2749 priv->overshooting_y = priv->bounce_steps; /* Hack to stop a bounce in the finger down case */
2750 priv->vel_y = priv->overshot_dist_y * 0.9;
2753 if (priv->overshot_dist_x != 0) {
2754 priv->overshooting_x = priv->bounce_steps; /* Hack to stop a bounce in the finger down case */
2755 priv->vel_x = priv->overshot_dist_x * 0.9;
2758 priv->button_pressed = FALSE;
2760 /* if widget was moving fast in the panning, increase speed even more */
2761 if ((event->time - priv->last_press_time < 200) &&
2762 ((ABS (priv->old_vel_x) > priv->vmin) ||
2763 (ABS (priv->old_vel_y) > priv->vmin)))
2767 if (priv->vel_x != 0)
2768 symbol = ((priv->vel_x * priv->old_vel_x) > 0) ? 1 : -1;
2770 priv->vel_x = symbol *
2771 (priv->old_vel_x + ((priv->old_vel_x > 0) ? priv->vmax : -priv->vmax));
2775 if (priv->vel_y != 0)
2776 symbol = ((priv->vel_y * priv->old_vel_y) > 0) ? 1 : -1;
2778 priv->vel_y = symbol *
2779 (priv->old_vel_y + ((priv->old_vel_y > 0) ? priv->vmax : -priv->vmax));
2784 if ((ABS (priv->vel_y) >= priv->vmin) ||
2785 (ABS (priv->vel_x) >= priv->vmin)) {
2787 /* we have to move because we are in overshooting position*/
2789 gboolean result_val;
2791 g_signal_emit (area,
2792 pannable_area_signals[PANNING_STARTED],
2796 priv->scroll_indicator_alpha = 1.0;
2799 if (ABS (priv->vel_x) > MAX_SPEED_THRESHOLD)
2800 priv->vel_x = (priv->vel_x > 0) ? priv->vmax : -priv->vmax;
2802 if (ABS (priv->vel_y) > MAX_SPEED_THRESHOLD)
2803 priv->vel_y = (priv->vel_y > 0) ? priv->vmax : -priv->vmax;
2807 priv->idle_id = gdk_threads_add_timeout_full (G_PRIORITY_HIGH_IDLE + 20,
2808 (gint) (1000.0 / (gdouble) priv->sps),
2809 (GSourceFunc) hildon_pannable_area_timeout,
2812 if (priv->center_on_child_focus_pending) {
2813 hildon_pannable_area_center_on_child_focus (area);
2817 g_signal_emit (widget, pannable_area_signals[PANNING_FINISHED], 0);
2820 area->priv->center_on_child_focus_pending = FALSE;
2822 priv->scroll_indicator_event_interrupt = 0;
2823 priv->scroll_delay_counter = priv->scrollbar_fade_delay;
2825 hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget),
2826 priv->scroll_indicator_alpha);
2828 priv->last_time = event->time;
2829 priv->last_type = 3;
2832 priv->moved = FALSE;
2837 hildon_pannable_area_get_topmost (gtk_bin_get_child (GTK_BIN (widget))->window,
2838 event->x, event->y, &x, &y, GDK_BUTTON_RELEASE_MASK);
2840 event = (GdkEventButton *) gdk_event_copy ((GdkEvent *) event);
2841 /* remove the reference we added with the copy */
2842 g_object_unref (priv->event_window);
2846 /* Leave the widget if we've moved - This doesn't break selection,
2847 * but stops buttons from being clicked.
2849 if ((child != priv->child) || (priv->moved)) {
2850 /* Send synthetic leave event */
2851 synth_crossing (priv->child, x, y, event->x_root,
2852 event->y_root, event->time, FALSE);
2853 /* insure no click will happen for widgets that do not handle
2857 /* Send synthetic button release event */
2858 ((GdkEventAny *) event)->window = g_object_ref (priv->child);
2859 gdk_event_put ((GdkEvent *) event);
2861 /* Send synthetic button release event */
2862 ((GdkEventAny *) event)->window = g_object_ref (child);
2863 gdk_event_put ((GdkEvent *) event);
2864 /* Send synthetic leave event */
2865 synth_crossing (priv->child, x, y, event->x_root,
2866 event->y_root, event->time, FALSE);
2868 g_object_remove_weak_pointer ((GObject *) priv->child,
2869 (gpointer) & priv->child);
2871 priv->moved = FALSE;
2872 gdk_event_free ((GdkEvent *) event);
2877 /* utility event handler */
2879 hildon_pannable_area_scroll_cb (GtkWidget *widget,
2880 GdkEventScroll *event)
2882 GtkAdjustment *adj = NULL;
2883 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
2885 if ((!priv->enabled) ||
2886 (gtk_bin_get_child (GTK_BIN (widget)) == NULL))
2889 priv->scroll_indicator_event_interrupt = 0;
2890 priv->scroll_delay_counter = priv->scrollbar_fade_delay + 20;
2892 hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget), 1.0);
2894 /* Stop inertial scrolling */
2895 if (priv->idle_id) {
2898 priv->overshooting_x = 0;
2899 priv->overshooting_y = 0;
2901 if ((priv->overshot_dist_x>0)||(priv->overshot_dist_y>0)) {
2902 priv->overshot_dist_x = 0;
2903 priv->overshot_dist_y = 0;
2905 gtk_widget_queue_resize (GTK_WIDGET (widget));
2908 g_signal_emit (widget, pannable_area_signals[PANNING_FINISHED], 0);
2910 g_source_remove (priv->idle_id);
2914 if (event->direction == GDK_SCROLL_UP || event->direction == GDK_SCROLL_DOWN)
2915 adj = priv->vadjust;
2917 adj = priv->hadjust;
2921 gdouble delta, new_value;
2923 /* from gtkrange.c calculate delta*/
2924 delta = pow (adj->page_size, 2.0 / 3.0);
2926 if (event->direction == GDK_SCROLL_UP ||
2927 event->direction == GDK_SCROLL_LEFT)
2930 new_value = CLAMP (adj->value + delta, adj->lower, adj->upper - adj->page_size);
2932 gtk_adjustment_set_value (adj, new_value);
2939 hildon_pannable_area_child_mapped (GtkWidget *widget,
2943 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (user_data)->priv;
2945 if (priv->event_window != NULL && priv->enabled)
2946 gdk_window_raise (priv->event_window);
2950 hildon_pannable_area_add (GtkContainer *container, GtkWidget *child)
2952 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (container)->priv;
2954 g_return_if_fail (gtk_bin_get_child (GTK_BIN (container)) == NULL);
2956 gtk_widget_set_parent (child, GTK_WIDGET (container));
2957 GTK_BIN (container)->child = child;
2959 g_signal_connect_after (child, "map-event",
2960 G_CALLBACK (hildon_pannable_area_child_mapped),
2963 if (!gtk_widget_set_scroll_adjustments (child, priv->hadjust, priv->vadjust)) {
2964 g_warning ("%s: cannot add non scrollable widget, "
2965 "wrap it in a viewport", __FUNCTION__);
2969 /* call this function if you are not panning */
2971 hildon_pannable_area_center_on_child_focus (HildonPannableArea *area)
2973 GtkWidget *focused_child = NULL;
2974 GtkWidget *window = NULL;
2976 window = gtk_widget_get_toplevel (GTK_WIDGET (area));
2978 if (GTK_WIDGET_TOPLEVEL (window)) {
2979 focused_child = gtk_window_get_focus (GTK_WINDOW (window));
2982 if (focused_child) {
2983 hildon_pannable_area_scroll_to_child (area, focused_child);
2988 hildon_pannable_area_set_focus_child (GtkContainer *container,
2991 HildonPannableArea *area = HILDON_PANNABLE_AREA (container);
2993 if (!area->priv->center_on_child_focus) {
2997 if (GTK_IS_WIDGET (child)) {
2998 area->priv->center_on_child_focus_pending = TRUE;
3003 hildon_pannable_area_remove (GtkContainer *container, GtkWidget *child)
3005 g_return_if_fail (HILDON_IS_PANNABLE_AREA (container));
3006 g_return_if_fail (child != NULL);
3007 g_return_if_fail (gtk_bin_get_child (GTK_BIN (container)) == child);
3009 gtk_widget_set_scroll_adjustments (child, NULL, NULL);
3011 g_signal_handlers_disconnect_by_func (child,
3012 hildon_pannable_area_child_mapped,
3015 /* chain parent class handler to remove child */
3016 GTK_CONTAINER_CLASS (hildon_pannable_area_parent_class)->remove (container, child);
3020 * This method calculates a factor necessary to determine the initial distance
3021 * to jump in hildon_pannable_area_scroll_to(). For fixed time and frames per
3022 * second, we know in how many frames 'n' we need to reach the destination
3023 * point. We know that, for a distance d,
3025 * d = d_0 + d_1 + ... + d_n
3027 * where d_i is the distance travelled in the i-th frame and decel_factor is
3028 * the deceleration factor. This can be rewritten as
3030 * d = d_0 + (d_0 * decel_factor) + ... + (d_n-1 * decel_factor),
3032 * since the distance travelled on each frame is the distance travelled in the
3033 * previous frame reduced by the deceleration factor. Reducing this and
3034 * factoring d_0 out, we get
3036 * d = d_0 (1 + decel_factor + ... + decel_factor^(n-1)).
3038 * Since the sum is independent of the distance to be travelled, we can define
3041 * vel_factor = 1 + decel_factor + ... + decel_factor^(n-1).
3043 * That's the gem we calculate in this method.
3046 hildon_pannable_calculate_vel_factor (HildonPannableArea * self)
3048 HildonPannableAreaPrivate *priv = self->priv;
3053 n = ceil (priv->sps * priv->scroll_time);
3055 for (i = 1; i < n && fct_i >= RATIO_TOLERANCE; i++) {
3056 fct_i *= priv->decel;
3060 priv->vel_factor = fct;
3064 * hildon_pannable_area_new:
3066 * Create a new pannable area widget
3068 * Returns: the newly created #HildonPannableArea
3074 hildon_pannable_area_new (void)
3076 return g_object_new (HILDON_TYPE_PANNABLE_AREA, NULL);
3080 * hildon_pannable_area_new_full:
3081 * @mode: #HildonPannableAreaMode
3082 * @enabled: Value for the enabled property
3083 * @vel_min: Value for the velocity-min property
3084 * @vel_max: Value for the velocity-max property
3085 * @decel: Value for the deceleration property
3086 * @sps: Value for the sps property
3088 * Create a new #HildonPannableArea widget and set various properties
3090 * returns: the newly create #HildonPannableArea
3096 hildon_pannable_area_new_full (gint mode, gboolean enabled,
3097 gdouble vel_min, gdouble vel_max,
3098 gdouble decel, guint sps)
3100 return g_object_new (HILDON_TYPE_PANNABLE_AREA,
3103 "velocity_min", vel_min,
3104 "velocity_max", vel_max,
3105 "deceleration", decel, "sps", sps, NULL);
3109 * hildon_pannable_area_add_with_viewport:
3110 * @area: A #HildonPannableArea
3111 * @child: Child widget to add to the viewport
3113 * Convenience function used to add a child to a #GtkViewport, and add the
3114 * viewport to the scrolled window.
3120 hildon_pannable_area_add_with_viewport (HildonPannableArea * area,
3124 GtkWidget *viewport;
3126 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3127 g_return_if_fail (GTK_IS_WIDGET (child));
3128 g_return_if_fail (child->parent == NULL);
3130 bin = GTK_BIN (area);
3132 if (bin->child != NULL)
3134 g_return_if_fail (GTK_IS_VIEWPORT (bin->child));
3135 g_return_if_fail (GTK_BIN (bin->child)->child == NULL);
3137 viewport = bin->child;
3141 HildonPannableAreaPrivate *priv = area->priv;
3143 viewport = gtk_viewport_new (priv->hadjust,
3145 gtk_viewport_set_shadow_type (GTK_VIEWPORT (viewport), GTK_SHADOW_NONE);
3146 gtk_container_add (GTK_CONTAINER (area), viewport);
3149 gtk_widget_show (viewport);
3150 gtk_container_add (GTK_CONTAINER (viewport), child);
3154 * hildon_pannable_area_scroll_to:
3155 * @area: A #HildonPannableArea.
3156 * @x: The x coordinate of the destination point or -1 to ignore this axis.
3157 * @y: The y coordinate of the destination point or -1 to ignore this axis.
3159 * Smoothly scrolls @area to ensure that (@x, @y) is a visible point
3160 * on the widget. To move in only one coordinate, you must set the other one
3161 * to -1. Notice that, in %HILDON_PANNABLE_AREA_MODE_PUSH mode, this function
3162 * works just like hildon_pannable_area_jump_to().
3164 * This function is useful if you need to present the user with a particular
3165 * element inside a scrollable widget, like #GtkTreeView. For instance,
3166 * the following example shows how to scroll inside a #GtkTreeView to
3167 * make visible an item, indicated by the #GtkTreeIter @iter.
3171 * GtkTreePath *path;
3172 * GdkRectangle *rect;
3174 * path = gtk_tree_model_get_path (model, &iter);
3175 * gtk_tree_view_get_background_area (GTK_TREE_VIEW (treeview),
3176 * path, NULL, &rect);
3177 * gtk_tree_view_convert_bin_window_to_tree_coords (GTK_TREE_VIEW (treeview),
3178 * 0, rect.y, NULL, &y);
3179 * hildon_pannable_area_scroll_to (panarea, -1, y);
3180 * gtk_tree_path_free (path);
3184 * If you want to present a child widget in simpler scenarios,
3185 * use hildon_pannable_area_scroll_to_child() instead.
3187 * There is a precondition to this function: the widget must be
3188 * already realized. Check the hildon_pannable_area_jump_to_child() for
3189 * more tips regarding how to call this function during
3195 hildon_pannable_area_scroll_to (HildonPannableArea *area,
3196 const gint x, const gint y)
3198 HildonPannableAreaPrivate *priv;
3200 gint dist_x, dist_y;
3201 gboolean hscroll_visible, vscroll_visible;
3203 g_return_if_fail (GTK_WIDGET_REALIZED (area));
3204 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3208 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
3209 priv->vadjust->page_size);
3210 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
3211 priv->hadjust->page_size);
3213 if (((!vscroll_visible)&&(!hscroll_visible)) ||
3214 (x == -1 && y == -1)) {
3218 if (priv->mode == HILDON_PANNABLE_AREA_MODE_PUSH)
3219 hildon_pannable_area_jump_to (area, x, y);
3221 width = priv->hadjust->upper - priv->hadjust->lower;
3222 height = priv->vadjust->upper - priv->vadjust->lower;
3224 g_return_if_fail (x < width || y < height);
3226 if ((x > -1)&&(hscroll_visible)) {
3227 priv->scroll_to_x = CLAMP (x - priv->hadjust->page_size/2,
3228 priv->hadjust->lower,
3229 priv->hadjust->upper - priv->hadjust->page_size);
3230 dist_x = priv->scroll_to_x - priv->hadjust->value;
3232 priv->scroll_to_x = -1;
3234 priv->vel_x = - dist_x/priv->vel_factor;
3237 priv->scroll_to_x = -1;
3240 if ((y > -1)&&(vscroll_visible)) {
3241 priv->scroll_to_y = CLAMP (y - priv->vadjust->page_size/2,
3242 priv->vadjust->lower,
3243 priv->vadjust->upper - priv->vadjust->page_size);
3244 dist_y = priv->scroll_to_y - priv->vadjust->value;
3246 priv->scroll_to_y = -1;
3248 priv->vel_y = - dist_y/priv->vel_factor;
3251 priv->scroll_to_y = y;
3254 if ((priv->scroll_to_y == -1) && (priv->scroll_to_x == -1)) {
3258 hildon_pannable_area_launch_fade_timeout (area, 1.0);
3261 priv->idle_id = gdk_threads_add_timeout_full (G_PRIORITY_HIGH_IDLE + 20,
3262 (gint) (1000.0 / (gdouble) priv->sps),
3263 (GSourceFunc) hildon_pannable_area_timeout,
3268 * hildon_pannable_area_jump_to:
3269 * @area: A #HildonPannableArea.
3270 * @x: The x coordinate of the destination point or -1 to ignore this axis.
3271 * @y: The y coordinate of the destination point or -1 to ignore this axis.
3273 * Jumps the position of @area to ensure that (@x, @y) is a visible
3274 * point in the widget. In order to move in only one coordinate, you
3275 * must set the other one to -1. See hildon_pannable_area_scroll_to()
3276 * function for an example of how to calculate the position of
3277 * children in scrollable widgets like #GtkTreeview.
3279 * There is a precondition to this function: the widget must be
3280 * already realized. Check the hildon_pannable_area_jump_to_child() for
3281 * more tips regarding how to call this function during
3287 hildon_pannable_area_jump_to (HildonPannableArea *area,
3288 const gint x, const gint y)
3290 HildonPannableAreaPrivate *priv;
3294 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3295 g_return_if_fail (GTK_WIDGET_REALIZED (area));
3296 g_return_if_fail (x >= -1 && y >= -1);
3298 if (x == -1 && y == -1) {
3304 width = priv->hadjust->upper - priv->hadjust->lower;
3305 height = priv->vadjust->upper - priv->vadjust->lower;
3307 g_return_if_fail (x < width || y < height);
3309 hv = priv->hadjust->value;
3310 vv = priv->vadjust->value;
3313 gdouble jump_to = x - priv->hadjust->page_size/2;
3315 priv->hadjust->value = CLAMP (jump_to,
3316 priv->hadjust->lower,
3317 priv->hadjust->upper -
3318 priv->hadjust->page_size);
3322 gdouble jump_to = y - priv->vadjust->page_size/2;
3324 priv->vadjust->value = CLAMP (jump_to,
3325 priv->vadjust->lower,
3326 priv->vadjust->upper -
3327 priv->vadjust->page_size);
3330 if (hv != priv->hadjust->value)
3331 gtk_adjustment_value_changed (priv->hadjust);
3333 if (vv != priv->vadjust->value)
3334 gtk_adjustment_value_changed (priv->vadjust);
3336 priv->scroll_indicator_alpha = 1.0;
3338 if (priv->scroll_indicator_timeout) {
3339 g_source_remove (priv->scroll_indicator_timeout);
3340 priv->scroll_indicator_timeout = 0;
3343 if (priv->idle_id) {
3346 priv->overshooting_x = 0;
3347 priv->overshooting_y = 0;
3349 if ((priv->overshot_dist_x>0)||(priv->overshot_dist_y>0)) {
3350 priv->overshot_dist_x = 0;
3351 priv->overshot_dist_y = 0;
3353 gtk_widget_queue_resize (GTK_WIDGET (area));
3356 g_signal_emit (area, pannable_area_signals[PANNING_FINISHED], 0);
3357 g_source_remove (priv->idle_id);
3363 * hildon_pannable_area_scroll_to_child:
3364 * @area: A #HildonPannableArea.
3365 * @child: A #GtkWidget, descendant of @area.
3367 * Smoothly scrolls until @child is visible inside @area. @child must
3368 * be a descendant of @area. If you need to scroll inside a scrollable
3369 * widget, e.g., #GtkTreeview, see hildon_pannable_area_scroll_to().
3371 * There is a precondition to this function: the widget must be
3372 * already realized. Check the hildon_pannable_area_jump_to_child() for
3373 * more tips regarding how to call this function during
3379 hildon_pannable_area_scroll_to_child (HildonPannableArea *area, GtkWidget *child)
3381 GtkWidget *bin_child;
3384 g_return_if_fail (GTK_WIDGET_REALIZED (area));
3385 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3386 g_return_if_fail (GTK_IS_WIDGET (child));
3387 g_return_if_fail (gtk_widget_is_ancestor (child, GTK_WIDGET (area)));
3389 if (GTK_BIN (area)->child == NULL)
3392 /* We need to get to check the child of the inside the area */
3393 bin_child = GTK_BIN (area)->child;
3395 /* we check if we added a viewport */
3396 if (GTK_IS_VIEWPORT (bin_child)) {
3397 bin_child = GTK_BIN (bin_child)->child;
3400 if (gtk_widget_translate_coordinates (child, bin_child, 0, 0, &x, &y))
3401 hildon_pannable_area_scroll_to (area, x, y);
3405 * hildon_pannable_area_jump_to_child:
3406 * @area: A #HildonPannableArea.
3407 * @child: A #GtkWidget, descendant of @area.
3409 * Jumps to make sure @child is visible inside @area. @child must
3410 * be a descendant of @area. If you want to move inside a scrollable
3411 * widget, like, #GtkTreeview, see hildon_pannable_area_scroll_to().
3413 * There is a precondition to this function: the widget must be
3414 * already realized. You can control if the widget is ready with the
3415 * GTK_WIDGET_REALIZED macro. If you want to call this function during
3416 * the initialization process of the widget do it inside a callback to
3417 * the ::realize signal, using g_signal_connect_after() function.
3422 hildon_pannable_area_jump_to_child (HildonPannableArea *area, GtkWidget *child)
3424 GtkWidget *bin_child;
3427 g_return_if_fail (GTK_WIDGET_REALIZED (area));
3428 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3429 g_return_if_fail (GTK_IS_WIDGET (child));
3430 g_return_if_fail (gtk_widget_is_ancestor (child, GTK_WIDGET (area)));
3432 if (gtk_bin_get_child (GTK_BIN (area)) == NULL)
3435 /* We need to get to check the child of the inside the area */
3436 bin_child = gtk_bin_get_child (GTK_BIN (area));
3438 /* we check if we added a viewport */
3439 if (GTK_IS_VIEWPORT (bin_child)) {
3440 bin_child = gtk_bin_get_child (GTK_BIN (bin_child));
3443 if (gtk_widget_translate_coordinates (child, bin_child, 0, 0, &x, &y))
3444 hildon_pannable_area_jump_to (area, x, y);
3448 * hildon_pannable_get_child_widget_at:
3449 * @area: A #HildonPannableArea.
3450 * @x: horizontal coordinate of the point
3451 * @y: vertical coordinate of the point
3453 * Get the widget at the point (x, y) inside the pannable area. In
3454 * case no widget found it returns NULL.
3456 * returns: the #GtkWidget if we find a widget, NULL in any other case
3461 hildon_pannable_get_child_widget_at (HildonPannableArea *area,
3462 gdouble x, gdouble y)
3464 GdkWindow *window = NULL;
3465 GtkWidget *child_widget = NULL;
3467 window = hildon_pannable_area_get_topmost
3468 (gtk_bin_get_child (GTK_BIN (area))->window,
3469 x, y, NULL, NULL, GDK_ALL_EVENTS_MASK);
3471 gdk_window_get_user_data (window, (gpointer) &child_widget);
3473 return child_widget;
3478 * hildon_pannable_area_get_hadjustment:
3479 * @area: A #HildonPannableArea.
3481 * Returns the horizontal adjustment. This adjustment is the internal
3482 * widget adjustment used to control the animations. Do not modify it
3483 * directly to change the position of the pannable, to do that use the
3484 * pannable API. If you modify the object directly it could cause
3485 * artifacts in the animations.
3487 * returns: The horizontal #GtkAdjustment
3492 hildon_pannable_area_get_hadjustment (HildonPannableArea *area)
3495 g_return_val_if_fail (HILDON_IS_PANNABLE_AREA (area), NULL);
3497 return area->priv->hadjust;
3501 * hildon_pannable_area_get_vadjustment:
3502 * @area: A #HildonPannableArea.
3504 * Returns the vertical adjustment. This adjustment is the internal
3505 * widget adjustment used to control the animations. Do not modify it
3506 * directly to change the position of the pannable, to do that use the
3507 * pannable API. If you modify the object directly it could cause
3508 * artifacts in the animations.
3510 * returns: The vertical #GtkAdjustment
3515 hildon_pannable_area_get_vadjustment (HildonPannableArea *area)
3517 g_return_val_if_fail (HILDON_IS_PANNABLE_AREA (area), NULL);
3519 return area->priv->vadjust;
3524 * hildon_pannable_area_get_size_request_policy:
3525 * @area: A #HildonPannableArea.
3527 * This function returns the current size request policy of the
3528 * widget. That policy controls the way the size_request is done in
3529 * the pannable area. Check
3530 * hildon_pannable_area_set_size_request_policy() for a more detailed
3533 * returns: the policy is currently being used in the widget
3534 * #HildonSizeRequestPolicy.
3538 HildonSizeRequestPolicy
3539 hildon_pannable_area_get_size_request_policy (HildonPannableArea *area)
3541 HildonPannableAreaPrivate *priv;
3543 g_return_val_if_fail (HILDON_IS_PANNABLE_AREA (area), FALSE);
3547 return priv->size_request_policy;
3551 * hildon_pannable_area_set_size_request_policy:
3552 * @area: A #HildonPannableArea.
3553 * @size_request_policy: One of the allowed #HildonSizeRequestPolicy
3555 * This function sets the pannable area size request policy. That
3556 * policy controls the way the size_request is done in the pannable
3557 * area. Pannable can use the size request of its children
3558 * (#HILDON_SIZE_REQUEST_CHILDREN) or the minimum size required for
3559 * the area itself (#HILDON_SIZE_REQUEST_MINIMUM), the latter is the
3560 * default. Recall this size depends on the scrolling policy you are
3561 * requesting to the pannable area, if you set #GTK_POLICY_NEVER this
3562 * parameter will not have any effect with
3563 * #HILDON_SIZE_REQUEST_MINIMUM set.
3567 * Deprecated: This method and the policy request is deprecated, DO
3568 * NOT use it in future code, the only policy properly supported in
3569 * gtk+ nowadays is the minimum size. Use #gtk_window_set_default_size
3570 * or #gtk_window_set_geometry_hints with the proper size in your case
3571 * to define the height of your dialogs.
3574 hildon_pannable_area_set_size_request_policy (HildonPannableArea *area,
3575 HildonSizeRequestPolicy size_request_policy)
3577 HildonPannableAreaPrivate *priv;
3579 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3583 if (priv->size_request_policy == size_request_policy)
3586 priv->size_request_policy = size_request_policy;
3588 gtk_widget_queue_resize (GTK_WIDGET (area));
3590 g_object_notify (G_OBJECT (area), "size-request-policy");
3594 * hildon_pannable_area_get_center_on_child_focus
3595 * @area: A #HildonPannableArea
3597 * Gets the @area #HildonPannableArea:center-on-child-focus property
3600 * See #HildonPannableArea:center-on-child-focus for more information.
3602 * Returns: the @area #HildonPannableArea:center-on-child-focus value
3607 hildon_pannable_area_get_center_on_child_focus (HildonPannableArea *area)
3609 g_return_val_if_fail (HILDON_IS_PANNABLE_AREA (area), FALSE);
3611 return area->priv->center_on_child_focus;
3615 * hildon_pannable_area_set_center_on_child_focus
3616 * @area: A #HildonPannableArea
3617 * @value: the new value
3619 * Sets the @area #HildonPannableArea:center-on-child-focus property
3622 * See #HildonPannableArea:center-on-child-focus for more information.
3627 hildon_pannable_area_set_center_on_child_focus (HildonPannableArea *area,
3630 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3632 area->priv->center_on_child_focus = value;