2 * This file is a part of hildon
4 * Copyright (C) 2008 Nokia Corporation, all rights reserved.
6 * Contact: Rodrigo Novo <rodrigo.novo@nokia.com>
8 * This widget is based on MokoFingerScroll from libmokoui
9 * OpenMoko Application Framework UI Library
10 * Authored by Chris Lord <chris@openedhand.com>
11 * Copyright (C) 2006-2007 OpenMoko Inc.
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU Lesser Public License as published by
15 * the Free Software Foundation; version 2 of the license.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU Lesser Public License for more details.
25 * SECTION: hildon-pannable-area
26 * @short_description: A scrolling widget designed for touch screens
27 * @see_also: #GtkScrolledWindow
29 * #HildonPannableArea is a container widget that can be "panned" (scrolled)
30 * up and down using the touchscreen with fingers. The widget has no scrollbars,
31 * but it rather shows small scroll indicators to give an idea of the part of the
32 * content that is visible at a time. The scroll indicators appear when a dragging
33 * motion is started on the pannable area.
35 * The scrolling is "kinetic", meaning the motion can be "flicked" and it will
36 * continue from the initial motion by gradually slowing down to an eventual stop.
37 * The motion can also be stopped immediately by pressing the touchscreen over the
41 #undef HILDON_DISABLE_DEPRECATED
44 #if USE_CAIRO_SCROLLBARS == 1
49 #include "hildon-pannable-area.h"
50 #include "hildon-marshalers.h"
51 #include "hildon-enum-types.h"
53 #define USE_CAIRO_SCROLLBARS 0
55 #define SCROLL_BAR_MIN_SIZE 5
56 #define RATIO_TOLERANCE 0.000001
57 #define SCROLL_FADE_TIMEOUT 100
58 #define MOTION_EVENTS_PER_SECOND 25
59 #define CURSOR_STOPPED_TIMEOUT 80
60 #define MAX_SPEED_THRESHOLD 250
61 #define PANNABLE_MAX_WIDTH 788
62 #define PANNABLE_MAX_HEIGHT 378
64 G_DEFINE_TYPE (HildonPannableArea, hildon_pannable_area, GTK_TYPE_BIN)
66 #define PANNABLE_AREA_PRIVATE(o) \
67 (G_TYPE_INSTANCE_GET_PRIVATE ((o), HILDON_TYPE_PANNABLE_AREA, \
68 HildonPannableAreaPrivate))
70 struct _HildonPannableAreaPrivate {
71 HildonPannableAreaMode mode;
72 HildonMovementMode mov_mode;
73 GdkWindow *event_window;
74 gdouble x; /* Used to store mouse co-ordinates of the first or */
75 gdouble y; /* previous events in a press-motion pair */
76 gdouble ex; /* Used to store mouse co-ordinates of the last */
77 gdouble ey; /* motion event in acceleration mode */
79 gboolean button_pressed;
80 guint32 last_time; /* Last event time, to stop infinite loops */
86 gdouble vmax_overshooting;
93 guint panning_threshold;
94 guint scrollbar_fade_delay;
97 guint direction_error_margin;
103 gint ix; /* Initial click mouse co-ordinates */
105 gint cx; /* Initial click child window mouse co-ordinates */
112 gint overshot_dist_x;
113 gint overshot_dist_y;
116 gdouble scroll_indicator_alpha;
117 gint motion_event_scroll_timeout;
118 gint scroll_indicator_timeout;
119 gint scroll_indicator_event_interrupt;
120 gint scroll_delay_counter;
123 gboolean initial_hint;
124 gboolean initial_effect;
125 gboolean low_friction_mode;
128 gboolean size_request_policy;
129 gboolean hscroll_visible;
130 gboolean vscroll_visible;
131 GdkRectangle hscroll_rect;
132 GdkRectangle vscroll_rect;
133 guint indicator_width;
135 GtkAdjustment *hadjust;
136 GtkAdjustment *vadjust;
140 GtkPolicyType vscrollbar_policy;
141 GtkPolicyType hscrollbar_policy;
143 GdkGC *scrollbars_gc;
144 GdkColor scroll_color;
146 gboolean center_on_child_focus;
147 gboolean center_on_child_focus_pending;
159 static guint pannable_area_signals [LAST_SIGNAL] = { 0 };
167 PROP_VEL_MAX_OVERSHOOTING,
168 PROP_VELOCITY_FAST_FACTOR,
172 PROP_PANNING_THRESHOLD,
173 PROP_SCROLLBAR_FADE_DELAY,
176 PROP_DIRECTION_ERROR_MARGIN,
177 PROP_VSCROLLBAR_POLICY,
178 PROP_HSCROLLBAR_POLICY,
183 PROP_LOW_FRICTION_MODE,
184 PROP_SIZE_REQUEST_POLICY,
187 PROP_CENTER_ON_CHILD_FOCUS,
191 static void hildon_pannable_area_class_init (HildonPannableAreaClass * klass);
192 static void hildon_pannable_area_init (HildonPannableArea * area);
193 static void hildon_pannable_area_get_property (GObject * object,
197 static void hildon_pannable_area_set_property (GObject * object,
199 const GValue * value,
201 static void hildon_pannable_area_remove_timeouts (GtkWidget * widget);
202 static void hildon_pannable_area_dispose (GObject * object);
203 static void hildon_pannable_area_realize (GtkWidget * widget);
204 static void hildon_pannable_area_unrealize (GtkWidget * widget);
205 static void hildon_pannable_area_size_request (GtkWidget * widget,
206 GtkRequisition * requisition);
207 static void hildon_pannable_area_size_allocate (GtkWidget * widget,
208 GtkAllocation * allocation);
209 static void hildon_pannable_area_child_allocate_calculate (GtkWidget * widget,
210 GtkAllocation * allocation,
211 GtkAllocation * child_allocation);
212 static void hildon_pannable_area_style_set (GtkWidget * widget,
213 GtkStyle * previous_style);
214 static void hildon_pannable_area_map (GtkWidget * widget);
215 static void hildon_pannable_area_unmap (GtkWidget * widget);
216 static void hildon_pannable_area_grab_notify (GtkWidget *widget,
217 gboolean was_grabbed,
219 #if USE_CAIRO_SCROLLBARS == 1
220 static void rgb_from_gdkcolor (GdkColor *color, gdouble *r, gdouble *g, gdouble *b);
221 #else /* USE_CAIRO_SCROLLBARS */
222 static void tranparency_color (GdkColor *color,
225 gdouble transparency);
226 #endif /* USE_CAIRO_SCROLLBARS */
227 static void hildon_pannable_draw_vscroll (GtkWidget * widget,
228 GdkColor *back_color,
229 GdkColor *scroll_color);
230 static void hildon_pannable_draw_hscroll (GtkWidget * widget,
231 GdkColor *back_color,
232 GdkColor *scroll_color);
233 static void hildon_pannable_area_initial_effect (GtkWidget * widget);
234 static void hildon_pannable_area_redraw (HildonPannableArea * area);
235 static void hildon_pannable_area_launch_fade_timeout (HildonPannableArea * area,
237 static void hildon_pannable_area_adjust_value_changed (HildonPannableArea * area,
239 static void hildon_pannable_area_adjust_changed (HildonPannableArea * area,
241 static gboolean hildon_pannable_area_scroll_indicator_fade(HildonPannableArea * area);
242 static gboolean hildon_pannable_area_expose_event (GtkWidget * widget,
243 GdkEventExpose * event);
244 static GdkWindow * hildon_pannable_area_get_topmost (GdkWindow * window,
246 gint * tx, gint * ty,
248 static void synth_crossing (GdkWindow * child,
250 gint x_root, gint y_root,
251 guint32 time, gboolean in);
252 static gboolean hildon_pannable_area_button_press_cb (GtkWidget * widget,
253 GdkEventButton * event);
254 static void hildon_pannable_area_refresh (HildonPannableArea * area);
255 static gboolean hildon_pannable_area_check_scrollbars (HildonPannableArea * area);
256 static void hildon_pannable_axis_scroll (HildonPannableArea *area,
257 GtkAdjustment *adjust,
265 static void hildon_pannable_area_scroll (HildonPannableArea *area,
266 gdouble x, gdouble y);
267 static gboolean hildon_pannable_area_timeout (HildonPannableArea * area);
268 static void hildon_pannable_area_calculate_velocity (gdouble *vel,
272 gdouble drag_inertia,
275 static gboolean hildon_pannable_area_motion_event_scroll_timeout (HildonPannableArea *area);
276 static void hildon_pannable_area_motion_event_scroll (HildonPannableArea *area,
277 gdouble x, gdouble y);
278 static void hildon_pannable_area_check_move (HildonPannableArea *area,
279 GdkEventMotion * event,
282 static void hildon_pannable_area_handle_move (HildonPannableArea *area,
283 GdkEventMotion * event,
286 static gboolean hildon_pannable_area_motion_notify_cb (GtkWidget * widget,
287 GdkEventMotion * event);
288 static gboolean hildon_pannable_leave_notify_event (GtkWidget *widget,
289 GdkEventCrossing *event);
290 static gboolean hildon_pannable_area_button_release_cb (GtkWidget * widget,
291 GdkEventButton * event);
292 static gboolean hildon_pannable_area_scroll_cb (GtkWidget *widget,
293 GdkEventScroll *event);
294 static void hildon_pannable_area_child_mapped (GtkWidget *widget,
297 static void hildon_pannable_area_add (GtkContainer *container, GtkWidget *child);
298 static void hildon_pannable_area_remove (GtkContainer *container, GtkWidget *child);
299 static void hildon_pannable_calculate_vel_factor (HildonPannableArea * self);
300 static void hildon_pannable_area_set_focus_child (GtkContainer *container,
302 static void hildon_pannable_area_center_on_child_focus (HildonPannableArea *area);
306 hildon_pannable_area_class_init (HildonPannableAreaClass * klass)
308 GObjectClass *object_class = G_OBJECT_CLASS (klass);
309 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
310 GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
313 g_type_class_add_private (klass, sizeof (HildonPannableAreaPrivate));
315 object_class->get_property = hildon_pannable_area_get_property;
316 object_class->set_property = hildon_pannable_area_set_property;
317 object_class->dispose = hildon_pannable_area_dispose;
319 widget_class->realize = hildon_pannable_area_realize;
320 widget_class->unrealize = hildon_pannable_area_unrealize;
321 widget_class->map = hildon_pannable_area_map;
322 widget_class->unmap = hildon_pannable_area_unmap;
323 widget_class->size_request = hildon_pannable_area_size_request;
324 widget_class->size_allocate = hildon_pannable_area_size_allocate;
325 widget_class->expose_event = hildon_pannable_area_expose_event;
326 widget_class->style_set = hildon_pannable_area_style_set;
327 widget_class->button_press_event = hildon_pannable_area_button_press_cb;
328 widget_class->button_release_event = hildon_pannable_area_button_release_cb;
329 widget_class->motion_notify_event = hildon_pannable_area_motion_notify_cb;
330 widget_class->leave_notify_event = hildon_pannable_leave_notify_event;
331 widget_class->scroll_event = hildon_pannable_area_scroll_cb;
333 container_class->add = hildon_pannable_area_add;
334 container_class->remove = hildon_pannable_area_remove;
335 container_class->set_focus_child = hildon_pannable_area_set_focus_child;
337 klass->horizontal_movement = NULL;
338 klass->vertical_movement = NULL;
340 g_object_class_install_property (object_class,
342 g_param_spec_boolean ("enabled",
344 "Enable or disable finger-scroll.",
349 g_object_class_install_property (object_class,
350 PROP_VSCROLLBAR_POLICY,
351 g_param_spec_enum ("vscrollbar_policy",
353 "Visual policy of the vertical scrollbar",
354 GTK_TYPE_POLICY_TYPE,
355 GTK_POLICY_AUTOMATIC,
359 g_object_class_install_property (object_class,
360 PROP_HSCROLLBAR_POLICY,
361 g_param_spec_enum ("hscrollbar_policy",
363 "Visual policy of the horizontal scrollbar",
364 GTK_TYPE_POLICY_TYPE,
365 GTK_POLICY_AUTOMATIC,
369 g_object_class_install_property (object_class,
371 g_param_spec_enum ("mode",
373 "Change the finger-scrolling mode.",
374 HILDON_TYPE_PANNABLE_AREA_MODE,
375 HILDON_PANNABLE_AREA_MODE_AUTO,
379 g_object_class_install_property (object_class,
381 g_param_spec_flags ("mov_mode",
382 "Scroll movement mode",
383 "Controls if the widget can scroll vertically, horizontally or both",
384 HILDON_TYPE_MOVEMENT_MODE,
385 HILDON_MOVEMENT_MODE_VERT,
389 g_object_class_install_property (object_class,
391 g_param_spec_double ("velocity_min",
392 "Minimum scroll velocity",
393 "Minimum distance the child widget should scroll "
394 "per 'frame', in pixels per frame.",
399 g_object_class_install_property (object_class,
401 g_param_spec_double ("velocity_max",
402 "Maximum scroll velocity",
403 "Maximum distance the child widget should scroll "
404 "per 'frame', in pixels per frame.",
409 g_object_class_install_property (object_class,
410 PROP_VEL_MAX_OVERSHOOTING,
411 g_param_spec_double ("velocity_overshooting_max",
412 "Maximum scroll velocity when overshooting",
413 "Maximum distance the child widget should scroll "
414 "per 'frame', in pixels per frame when it overshoots after hitting the edge.",
419 g_object_class_install_property (object_class,
420 PROP_VELOCITY_FAST_FACTOR,
421 g_param_spec_double ("velocity_fast_factor",
422 "Fast velocity factor",
423 "Minimum velocity that is considered 'fast': "
424 "children widgets won't receive button presses. "
425 "Expressed as a fraction of the maximum velocity.",
430 g_object_class_install_property (object_class,
432 g_param_spec_double ("deceleration",
433 "Deceleration multiplier",
434 "The multiplier used when decelerating when in "
435 "acceleration scrolling mode.",
440 g_object_class_install_property (object_class,
442 g_param_spec_double ("drag_inertia",
443 "Inertia of the cursor dragging",
444 "Percentage of the calculated speed in each moment we are are going to use"
445 "to calculate the launch speed, the other part would be the speed"
446 "calculated previously",
451 g_object_class_install_property (object_class,
453 g_param_spec_uint ("sps",
454 "Scrolls per second",
455 "Amount of scroll events to generate per second.",
460 g_object_class_install_property (object_class,
461 PROP_PANNING_THRESHOLD,
462 g_param_spec_uint ("panning_threshold",
463 "Threshold to consider a motion event an scroll",
464 "Amount of pixels to consider a motion event an scroll, if it is less"
465 "it is a click detected incorrectly by the touch screen.",
470 g_object_class_install_property (object_class,
471 PROP_SCROLLBAR_FADE_DELAY,
472 g_param_spec_uint ("scrollbar_fade_delay",
473 "Time before starting to fade the scrollbar",
474 "Time the scrollbar is going to be visible if the widget is not in"
475 "action in miliseconds",
480 g_object_class_install_property (object_class,
482 g_param_spec_uint ("bounce_steps",
484 "Number of steps that is going to be used to bounce when hitting the"
485 "edge, the rubberband effect depends on it",
490 g_object_class_install_property (object_class,
492 g_param_spec_uint ("force",
493 "Multiplier of the calculated speed",
494 "Force applied to the movement, multiplies the calculated speed of the"
495 "user movement the cursor in the screen",
500 g_object_class_install_property (object_class,
501 PROP_DIRECTION_ERROR_MARGIN,
502 g_param_spec_uint ("direction_error_margin",
503 "Margin in the direction detection",
504 "After detecting the direction of the movement (horizontal or"
505 "vertical), we can add this margin of error to allow the movement in"
506 "the other direction even apparently it is not",
511 g_object_class_install_property (object_class,
513 g_param_spec_int ("vovershoot_max",
514 "Vertical overshoot distance",
515 "Space we allow the widget to pass over its vertical limits when"
516 "hitting the edges, set 0 in order to deactivate overshooting.",
521 g_object_class_install_property (object_class,
523 g_param_spec_int ("hovershoot_max",
524 "Horizontal overshoot distance",
525 "Space we allow the widget to pass over its horizontal limits when"
526 "hitting the edges, set 0 in order to deactivate overshooting.",
531 g_object_class_install_property (object_class,
533 g_param_spec_double ("scroll_time",
534 "Time to scroll to a position",
535 "The time to scroll to a position when calling the hildon_pannable_scroll_to function",
540 g_object_class_install_property (object_class,
542 g_param_spec_boolean ("initial-hint",
544 "Whether to hint the user about the pannability of the container.",
549 g_object_class_install_property (object_class,
550 PROP_LOW_FRICTION_MODE,
551 g_param_spec_boolean ("low-friction-mode",
552 "Do not decelerate the initial velocity",
553 "Avoid decelerating the panning movement, like no friction, the widget"
554 "will stop in the edges or if the user clicks.",
559 g_object_class_install_property (object_class,
560 PROP_SIZE_REQUEST_POLICY,
561 g_param_spec_enum ("size-request-policy",
562 "Size Requisition policy",
563 "Controls the size request policy of the widget",
564 HILDON_TYPE_SIZE_REQUEST_POLICY,
565 HILDON_SIZE_REQUEST_MINIMUM,
569 g_object_class_install_property (object_class,
571 g_param_spec_object ("hadjustment",
572 "Horizontal Adjustment",
573 "The GtkAdjustment for the horizontal position",
576 g_object_class_install_property (object_class,
578 g_param_spec_object ("vadjustment",
579 "Vertical Adjustment",
580 "The GtkAdjustment for the vertical position",
584 g_object_class_install_property (object_class,
585 PROP_CENTER_ON_CHILD_FOCUS,
586 g_param_spec_boolean ("center-on-child-focus",
587 "Center on the child with the focus",
588 "Whether to center the pannable on the child that receives the focus.",
594 gtk_widget_class_install_style_property (widget_class,
597 "Width of the scroll indicators",
598 "Pixel width used to draw the scroll indicators.",
603 * HildonPannableArea::horizontal-movement:
604 * @hildonpannable: the object which received the signal
605 * @direction: the direction of the movement #HILDON_MOVEMENT_LEFT or #HILDON_MOVEMENT_RIGHT
606 * @initial_x: the x coordinate of the point where the user clicked to start the movement
607 * @initial_y: the y coordinate of the point where the user clicked to start the movement
609 * The horizontal-movement signal is emitted when the pannable area
610 * detects a horizontal movement. The detection does not mean the
611 * widget is going to move (i.e. maybe the children are smaller
612 * horizontally than the screen).
616 pannable_area_signals[HORIZONTAL_MOVEMENT] =
617 g_signal_new ("horizontal_movement",
618 G_TYPE_FROM_CLASS (object_class),
619 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
620 G_STRUCT_OFFSET (HildonPannableAreaClass, horizontal_movement),
622 _hildon_marshal_VOID__INT_DOUBLE_DOUBLE,
629 * HildonPannableArea::vertical-movement:
630 * @hildonpannable: the object which received the signal
631 * @direction: the direction of the movement #HILDON_MOVEMENT_UP or #HILDON_MOVEMENT_DOWN
632 * @initial_x: the x coordinate of the point where the user clicked to start the movement
633 * @initial_y: the y coordinate of the point where the user clicked to start the movement
635 * The vertical-movement signal is emitted when the pannable area
636 * detects a vertical movement. The detection does not mean the
637 * widget is going to move (i.e. maybe the children are smaller
638 * vertically than the screen).
642 pannable_area_signals[VERTICAL_MOVEMENT] =
643 g_signal_new ("vertical_movement",
644 G_TYPE_FROM_CLASS (object_class),
645 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
646 G_STRUCT_OFFSET (HildonPannableAreaClass, vertical_movement),
648 _hildon_marshal_VOID__INT_DOUBLE_DOUBLE,
655 * HildonPannableArea::panning-started:
656 * @hildonpannable: the pannable area object that is going to start
659 * This signal is emitted before the panning starts. Applications
660 * can return %TRUE to avoid the panning. The main difference with
661 * the vertical-movement and horizontal-movement signals is those
662 * gesture signals are launched no matter if the widget is going to
663 * move, this signal means the widget is going to start moving. It
664 * could even happen that the widget moves and there was no gesture
665 * (i.e. click meanwhile the pannable is overshooting).
667 * Returns: %TRUE to stop the panning launch. %FALSE to continue
672 pannable_area_signals[PANNING_STARTED] =
673 g_signal_new ("panning-started",
674 G_TYPE_FROM_CLASS (object_class),
678 _hildon_marshal_BOOLEAN__VOID,
682 * HildonPannableArea::panning-finished:
683 * @hildonpannable: the pannable area object that finished the
686 * This signal is emitted after the kinetic panning has
691 pannable_area_signals[PANNING_FINISHED] =
692 g_signal_new ("panning-finished",
693 G_TYPE_FROM_CLASS (object_class),
697 _hildon_marshal_VOID__VOID,
703 hildon_pannable_area_init (HildonPannableArea * area)
705 HildonPannableAreaPrivate *priv = PANNABLE_AREA_PRIVATE (area);
707 GTK_WIDGET_UNSET_FLAGS (area, GTK_NO_WINDOW);
712 priv->button_pressed = FALSE;
715 priv->vscroll_visible = TRUE;
716 priv->hscroll_visible = TRUE;
717 priv->indicator_width = 6;
718 priv->overshot_dist_x = 0;
719 priv->overshot_dist_y = 0;
720 priv->overshooting_y = 0;
721 priv->overshooting_x = 0;
725 priv->scroll_indicator_alpha = 0.0;
726 priv->scroll_indicator_timeout = 0;
727 priv->motion_event_scroll_timeout = 0;
728 priv->scroll_indicator_event_interrupt = 0;
729 priv->scroll_delay_counter = 0;
730 priv->scrollbar_fade_delay = 0;
731 priv->scroll_to_x = -1;
732 priv->scroll_to_y = -1;
733 priv->first_drag = TRUE;
734 priv->initial_effect = TRUE;
735 priv->child_width = 0;
736 priv->child_height = 0;
737 priv->last_in = TRUE;
740 priv->center_on_child_focus_pending = FALSE;
742 gtk_style_lookup_color (GTK_WIDGET (area)->style,
743 "SecondaryTextColor", &priv->scroll_color);
746 GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
748 GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
750 g_object_ref_sink (G_OBJECT (priv->hadjust));
751 g_object_ref_sink (G_OBJECT (priv->vadjust));
753 g_signal_connect_swapped (priv->hadjust, "value-changed",
754 G_CALLBACK (hildon_pannable_area_adjust_value_changed), area);
755 g_signal_connect_swapped (priv->vadjust, "value-changed",
756 G_CALLBACK (hildon_pannable_area_adjust_value_changed), area);
757 g_signal_connect_swapped (priv->hadjust, "changed",
758 G_CALLBACK (hildon_pannable_area_adjust_changed), area);
759 g_signal_connect_swapped (priv->vadjust, "changed",
760 G_CALLBACK (hildon_pannable_area_adjust_changed), area);
761 g_signal_connect (area, "grab-notify",
762 G_CALLBACK (hildon_pannable_area_grab_notify), NULL);
766 hildon_pannable_area_get_property (GObject * object,
771 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (object)->priv;
773 switch (property_id) {
775 g_value_set_boolean (value, priv->enabled);
778 g_value_set_enum (value, priv->mode);
780 case PROP_MOVEMENT_MODE:
781 g_value_set_flags (value, priv->mov_mode);
783 case PROP_VELOCITY_MIN:
784 g_value_set_double (value, priv->vmin);
786 case PROP_VELOCITY_MAX:
787 g_value_set_double (value, priv->vmax);
789 case PROP_VEL_MAX_OVERSHOOTING:
790 g_value_set_double (value, priv->vmax_overshooting);
792 case PROP_VELOCITY_FAST_FACTOR:
793 g_value_set_double (value, priv->vfast_factor);
795 case PROP_DECELERATION:
796 g_value_set_double (value, priv->decel);
798 case PROP_DRAG_INERTIA:
799 g_value_set_double (value, priv->drag_inertia);
802 g_value_set_uint (value, priv->sps);
804 case PROP_PANNING_THRESHOLD:
805 g_value_set_uint (value, priv->panning_threshold);
807 case PROP_SCROLLBAR_FADE_DELAY:
808 /* convert to miliseconds */
809 g_value_set_uint (value, priv->scrollbar_fade_delay * SCROLL_FADE_TIMEOUT);
811 case PROP_BOUNCE_STEPS:
812 g_value_set_uint (value, priv->bounce_steps);
815 g_value_set_uint (value, priv->force);
817 case PROP_DIRECTION_ERROR_MARGIN:
818 g_value_set_uint (value, priv->direction_error_margin);
820 case PROP_VSCROLLBAR_POLICY:
821 g_value_set_enum (value, priv->vscrollbar_policy);
823 case PROP_HSCROLLBAR_POLICY:
824 g_value_set_enum (value, priv->hscrollbar_policy);
826 case PROP_VOVERSHOOT_MAX:
827 g_value_set_int (value, priv->vovershoot_max);
829 case PROP_HOVERSHOOT_MAX:
830 g_value_set_int (value, priv->hovershoot_max);
832 case PROP_SCROLL_TIME:
833 g_value_set_double (value, priv->scroll_time);
835 case PROP_INITIAL_HINT:
836 g_value_set_boolean (value, priv->initial_hint);
838 case PROP_LOW_FRICTION_MODE:
839 g_value_set_boolean (value, priv->low_friction_mode);
841 case PROP_SIZE_REQUEST_POLICY:
842 g_value_set_enum (value, priv->size_request_policy);
844 case PROP_HADJUSTMENT:
845 g_value_set_object (value,
846 hildon_pannable_area_get_hadjustment
847 (HILDON_PANNABLE_AREA (object)));
849 case PROP_VADJUSTMENT:
850 g_value_set_object (value,
851 hildon_pannable_area_get_vadjustment
852 (HILDON_PANNABLE_AREA (object)));
854 case PROP_CENTER_ON_CHILD_FOCUS:
855 g_value_set_boolean (value, priv->center_on_child_focus);
858 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
863 hildon_pannable_area_set_property (GObject * object,
865 const GValue * value,
868 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (object)->priv;
871 switch (property_id) {
873 enabled = g_value_get_boolean (value);
875 if ((priv->enabled != enabled) && (GTK_WIDGET_REALIZED (object))) {
877 gdk_window_raise (priv->event_window);
879 gdk_window_lower (priv->event_window);
882 priv->enabled = enabled;
885 priv->mode = g_value_get_enum (value);
887 case PROP_MOVEMENT_MODE:
888 priv->mov_mode = g_value_get_flags (value);
890 case PROP_VELOCITY_MIN:
891 priv->vmin = g_value_get_double (value);
893 case PROP_VELOCITY_MAX:
894 priv->vmax = g_value_get_double (value);
896 case PROP_VEL_MAX_OVERSHOOTING:
897 priv->vmax_overshooting = g_value_get_double (value);
899 case PROP_VELOCITY_FAST_FACTOR:
900 priv->vfast_factor = g_value_get_double (value);
902 case PROP_DECELERATION:
903 priv->decel = g_value_get_double (value);
904 hildon_pannable_calculate_vel_factor (HILDON_PANNABLE_AREA (object));
906 case PROP_DRAG_INERTIA:
907 priv->drag_inertia = g_value_get_double (value);
910 priv->sps = g_value_get_uint (value);
912 case PROP_PANNING_THRESHOLD:
914 GtkSettings *settings = gtk_settings_get_default ();
915 GtkSettingsValue svalue = { NULL, { 0, }, };
917 priv->panning_threshold = g_value_get_uint (value);
919 /* insure gtk dnd is the same we are using, not allowed
920 different thresholds in the same application */
921 svalue.origin = "panning_threshold";
922 g_value_init (&svalue.value, G_TYPE_LONG);
923 g_value_set_long (&svalue.value, priv->panning_threshold);
924 gtk_settings_set_property_value (settings, "gtk-dnd-drag-threshold", &svalue);
925 g_value_unset (&svalue.value);
928 case PROP_SCROLLBAR_FADE_DELAY:
929 /* convert to miliseconds */
930 priv->scrollbar_fade_delay = g_value_get_uint (value)/(SCROLL_FADE_TIMEOUT);
932 case PROP_BOUNCE_STEPS:
933 priv->bounce_steps = g_value_get_uint (value);
936 priv->force = g_value_get_uint (value);
938 case PROP_DIRECTION_ERROR_MARGIN:
939 priv->direction_error_margin = g_value_get_uint (value);
941 case PROP_VSCROLLBAR_POLICY:
942 priv->vscrollbar_policy = g_value_get_enum (value);
944 gtk_widget_queue_resize (GTK_WIDGET (object));
946 case PROP_HSCROLLBAR_POLICY:
947 priv->hscrollbar_policy = g_value_get_enum (value);
949 gtk_widget_queue_resize (GTK_WIDGET (object));
951 case PROP_VOVERSHOOT_MAX:
952 priv->vovershoot_max = g_value_get_int (value);
954 case PROP_HOVERSHOOT_MAX:
955 priv->hovershoot_max = g_value_get_int (value);
957 case PROP_SCROLL_TIME:
958 priv->scroll_time = g_value_get_double (value);
960 hildon_pannable_calculate_vel_factor (HILDON_PANNABLE_AREA (object));
962 case PROP_INITIAL_HINT:
963 priv->initial_hint = g_value_get_boolean (value);
965 case PROP_LOW_FRICTION_MODE:
966 priv->low_friction_mode = g_value_get_boolean (value);
968 case PROP_SIZE_REQUEST_POLICY:
969 hildon_pannable_area_set_size_request_policy (HILDON_PANNABLE_AREA (object),
970 g_value_get_enum (value));
972 case PROP_CENTER_ON_CHILD_FOCUS:
973 priv->center_on_child_focus = g_value_get_boolean (value);
977 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
982 hildon_pannable_area_dispose (GObject * object)
984 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (object)->priv;
985 GtkWidget *child = gtk_bin_get_child (GTK_BIN (object));
987 hildon_pannable_area_remove_timeouts (GTK_WIDGET (object));
990 g_signal_handlers_disconnect_by_func (child,
991 hildon_pannable_area_child_mapped,
995 g_signal_handlers_disconnect_by_func (object,
996 hildon_pannable_area_grab_notify,
1000 g_signal_handlers_disconnect_by_func (priv->hadjust,
1001 hildon_pannable_area_adjust_value_changed,
1003 g_signal_handlers_disconnect_by_func (priv->hadjust,
1004 hildon_pannable_area_adjust_changed,
1006 g_object_unref (priv->hadjust);
1007 priv->hadjust = NULL;
1010 if (priv->vadjust) {
1011 g_signal_handlers_disconnect_by_func (priv->vadjust,
1012 hildon_pannable_area_adjust_value_changed,
1014 g_signal_handlers_disconnect_by_func (priv->vadjust,
1015 hildon_pannable_area_adjust_changed,
1017 g_object_unref (priv->vadjust);
1018 priv->vadjust = NULL;
1021 if (G_OBJECT_CLASS (hildon_pannable_area_parent_class)->dispose)
1022 G_OBJECT_CLASS (hildon_pannable_area_parent_class)->dispose (object);
1026 hildon_pannable_area_realize (GtkWidget * widget)
1028 GdkWindowAttr attributes;
1029 gint attributes_mask;
1031 HildonPannableAreaPrivate *priv;
1033 priv = HILDON_PANNABLE_AREA (widget)->priv;
1035 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
1037 border_width = GTK_CONTAINER (widget)->border_width;
1039 attributes.x = widget->allocation.x + border_width;
1040 attributes.y = widget->allocation.y + border_width;
1041 attributes.width = MAX (widget->allocation.width - 2 * border_width, 0);
1042 attributes.height = MAX (widget->allocation.height - 2 * border_width, 0);
1043 attributes.window_type = GDK_WINDOW_CHILD;
1045 /* avoid using the hildon_window */
1046 attributes.visual = gtk_widget_get_visual (widget);
1047 attributes.colormap = gtk_widget_get_colormap (widget);
1048 attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK;
1049 attributes.wclass = GDK_INPUT_OUTPUT;
1051 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1053 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
1054 &attributes, attributes_mask);
1055 gdk_window_set_user_data (widget->window, widget);
1057 /* create the events window */
1060 attributes.event_mask = gtk_widget_get_events (widget)
1061 | GDK_BUTTON_MOTION_MASK
1062 | GDK_BUTTON_PRESS_MASK
1063 | GDK_BUTTON_RELEASE_MASK
1065 | GDK_POINTER_MOTION_HINT_MASK
1066 | GDK_EXPOSURE_MASK | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK;
1067 attributes.wclass = GDK_INPUT_ONLY;
1069 attributes_mask = GDK_WA_X | GDK_WA_Y;
1071 priv->event_window = gdk_window_new (widget->window,
1072 &attributes, attributes_mask);
1073 gdk_window_set_user_data (priv->event_window, widget);
1075 widget->style = gtk_style_attach (widget->style, widget->window);
1076 gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
1078 priv->scrollbars_gc = gdk_gc_new (GDK_DRAWABLE (widget->window));
1079 gdk_gc_copy (priv->scrollbars_gc, widget->style->fg_gc[GTK_STATE_INSENSITIVE]);
1084 hildon_pannable_area_remove_timeouts (GtkWidget * widget)
1086 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1088 if (priv->idle_id) {
1089 g_signal_emit (widget, pannable_area_signals[PANNING_FINISHED], 0);
1090 g_source_remove (priv->idle_id);
1094 if (priv->scroll_indicator_timeout){
1095 g_source_remove (priv->scroll_indicator_timeout);
1096 priv->scroll_indicator_timeout = 0;
1099 if (priv->motion_event_scroll_timeout){
1100 g_source_remove (priv->motion_event_scroll_timeout);
1101 priv->motion_event_scroll_timeout = 0;
1106 hildon_pannable_area_unrealize (GtkWidget * widget)
1108 HildonPannableAreaPrivate *priv;
1110 priv = HILDON_PANNABLE_AREA (widget)->priv;
1112 hildon_pannable_area_remove_timeouts (widget);
1114 if (priv->event_window != NULL) {
1115 gdk_window_set_user_data (priv->event_window, NULL);
1116 gdk_window_destroy (priv->event_window);
1117 priv->event_window = NULL;
1120 gdk_gc_unref (priv->scrollbars_gc);
1122 if (GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->unrealize)
1123 (*GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->unrealize)(widget);
1127 hildon_pannable_area_size_request (GtkWidget * widget,
1128 GtkRequisition * requisition)
1130 GtkRequisition child_requisition = {0};
1131 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1132 GtkWidget *child = gtk_bin_get_child (GTK_BIN (widget));
1134 if (child && GTK_WIDGET_VISIBLE (child))
1136 gtk_widget_size_request (child, &child_requisition);
1139 if (priv->hscrollbar_policy == GTK_POLICY_NEVER) {
1140 requisition->width = child_requisition.width;
1142 switch (priv->size_request_policy) {
1143 case HILDON_SIZE_REQUEST_CHILDREN:
1144 requisition->width = MIN (PANNABLE_MAX_WIDTH,
1145 child_requisition.width);
1147 case HILDON_SIZE_REQUEST_MINIMUM:
1149 requisition->width = priv->indicator_width;
1153 if (priv->vscrollbar_policy == GTK_POLICY_NEVER) {
1154 requisition->height = child_requisition.height;
1156 switch (priv->size_request_policy) {
1157 case HILDON_SIZE_REQUEST_CHILDREN:
1158 requisition->height = MIN (PANNABLE_MAX_HEIGHT,
1159 child_requisition.height);
1161 case HILDON_SIZE_REQUEST_MINIMUM:
1163 requisition->height = priv->indicator_width;
1167 requisition->width += 2 * GTK_CONTAINER (widget)->border_width;
1168 requisition->height += 2 * GTK_CONTAINER (widget)->border_width;
1172 hildon_pannable_area_child_allocate_calculate (GtkWidget * widget,
1173 GtkAllocation * allocation,
1174 GtkAllocation * child_allocation)
1177 HildonPannableAreaPrivate *priv;
1179 border_width = GTK_CONTAINER (widget)->border_width;
1181 priv = HILDON_PANNABLE_AREA (widget)->priv;
1183 child_allocation->x = 0;
1184 child_allocation->y = 0;
1185 child_allocation->width = MAX (allocation->width - 2 * border_width -
1186 (priv->vscroll_visible ? priv->vscroll_rect.width : 0), 0);
1187 child_allocation->height = MAX (allocation->height - 2 * border_width -
1188 (priv->hscroll_visible ? priv->hscroll_rect.height : 0), 0);
1190 if (priv->overshot_dist_y > 0) {
1191 child_allocation->y = MIN (child_allocation->y + priv->overshot_dist_y,
1192 child_allocation->height);
1193 child_allocation->height = MAX (child_allocation->height - priv->overshot_dist_y, 0);
1194 } else if (priv->overshot_dist_y < 0) {
1195 child_allocation->height = MAX (child_allocation->height + priv->overshot_dist_y, 0);
1198 if (priv->overshot_dist_x > 0) {
1199 child_allocation->x = MIN (child_allocation->x + priv->overshot_dist_x,
1200 child_allocation->width);
1201 child_allocation->width = MAX (child_allocation->width - priv->overshot_dist_x, 0);
1202 } else if (priv->overshot_dist_x < 0) {
1203 child_allocation->width = MAX (child_allocation->width + priv->overshot_dist_x, 0);
1208 hildon_pannable_area_size_allocate (GtkWidget * widget,
1209 GtkAllocation * allocation)
1211 GtkAllocation child_allocation;
1212 HildonPannableAreaPrivate *priv;
1213 GtkWidget *child = gtk_bin_get_child (GTK_BIN (widget));
1217 border_width = GTK_CONTAINER (widget)->border_width;
1219 widget->allocation = *allocation;
1221 priv = HILDON_PANNABLE_AREA (widget)->priv;
1223 if (GTK_WIDGET_REALIZED (widget)) {
1224 gdk_window_move_resize (widget->window,
1225 allocation->x + border_width,
1226 allocation->y + border_width,
1227 allocation->width - border_width * 2,
1228 allocation->height - border_width * 2);
1229 gdk_window_move_resize (priv->event_window,
1232 allocation->width - border_width * 2,
1233 allocation->height - border_width * 2);
1236 if (child && GTK_WIDGET_VISIBLE (child)) {
1238 hildon_pannable_area_check_scrollbars (HILDON_PANNABLE_AREA (widget));
1240 hildon_pannable_area_child_allocate_calculate (widget,
1244 gtk_widget_size_allocate (child, &child_allocation);
1246 if (hildon_pannable_area_check_scrollbars (HILDON_PANNABLE_AREA (widget))) {
1247 hildon_pannable_area_child_allocate_calculate (widget,
1251 gtk_widget_size_allocate (child, &child_allocation);
1254 hv = priv->hadjust->value;
1255 vv = priv->vadjust->value;
1257 /* we have to do this after child size_allocate because page_size is
1258 * changed when we allocate the size of the children */
1259 if (priv->overshot_dist_y < 0) {
1260 priv->vadjust->value = priv->vadjust->upper - priv->vadjust->page_size;
1263 if (priv->overshot_dist_x < 0) {
1264 priv->hadjust->value = priv->hadjust->upper - priv->hadjust->page_size;
1267 if (hv != priv->hadjust->value)
1268 gtk_adjustment_value_changed (priv->hadjust);
1270 if (vv != priv->vadjust->value)
1271 gtk_adjustment_value_changed (priv->vadjust);
1274 hildon_pannable_area_check_scrollbars (HILDON_PANNABLE_AREA (widget));
1279 hildon_pannable_area_style_set (GtkWidget * widget,
1280 GtkStyle * previous_style)
1282 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1284 GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->
1285 style_set (widget, previous_style);
1287 gtk_style_lookup_color (widget->style, "SecondaryTextColor", &priv->scroll_color);
1288 gtk_widget_style_get (widget, "indicator-width", &priv->indicator_width, NULL);
1292 hildon_pannable_area_map (GtkWidget * widget)
1294 HildonPannableAreaPrivate *priv;
1296 priv = HILDON_PANNABLE_AREA (widget)->priv;
1298 gdk_window_show (widget->window);
1300 if (priv->event_window != NULL && !priv->enabled)
1301 gdk_window_show (priv->event_window);
1303 (*GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->map) (widget);
1305 if (priv->event_window != NULL && priv->enabled)
1306 gdk_window_show (priv->event_window);
1310 hildon_pannable_area_unmap (GtkWidget * widget)
1312 HildonPannableAreaPrivate *priv;
1314 priv = HILDON_PANNABLE_AREA (widget)->priv;
1316 if (priv->event_window != NULL)
1317 gdk_window_hide (priv->event_window);
1319 gdk_window_hide (widget->window);
1321 (*GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->unmap) (widget);
1325 hildon_pannable_area_grab_notify (GtkWidget *widget,
1326 gboolean was_grabbed,
1329 /* an internal widget has grabbed the focus and now has returned it,
1330 we have to do some release actions */
1332 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1334 priv->scroll_indicator_event_interrupt = 0;
1336 if ((!priv->scroll_indicator_timeout)&&(priv->scroll_indicator_alpha)>0.1) {
1337 priv->scroll_delay_counter = priv->scrollbar_fade_delay;
1339 hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget),
1340 priv->scroll_indicator_alpha);
1343 priv->last_type = 3;
1344 priv->moved = FALSE;
1348 #if USE_CAIRO_SCROLLBARS == 1
1351 rgb_from_gdkcolor (GdkColor *color, gdouble *r, gdouble *g, gdouble *b)
1353 *r = (color->red >> 8) / 255.0;
1354 *g = (color->green >> 8) / 255.0;
1355 *b = (color->blue >> 8) / 255.0;
1359 hildon_pannable_draw_vscroll (GtkWidget * widget,
1360 GdkColor *back_color,
1361 GdkColor *scroll_color)
1363 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1366 cairo_pattern_t *pattern;
1368 gint radius = (priv->vscroll_rect.width/2) - 1;
1370 cr = gdk_cairo_create(widget->window);
1372 /* Draw the background */
1373 rgb_from_gdkcolor (back_color, &r, &g, &b);
1374 cairo_set_source_rgb (cr, r, g, b);
1375 cairo_rectangle(cr, priv->vscroll_rect.x, priv->vscroll_rect.y,
1376 priv->vscroll_rect.width,
1377 priv->vscroll_rect.height);
1378 cairo_fill_preserve (cr);
1381 /* Calculate the scroll bar height and position */
1382 y = ((priv->vadjust->value - priv->vadjust->lower) / (priv->vadjust->upper - priv->vadjust->lower)) *
1383 (widget->allocation.height -
1384 (priv->hscroll_visible ? priv->indicator_width : 0));
1385 height = ((((priv->vadjust->value - priv->vadjust->lower) +
1386 priv->vadjust->page_size) /
1387 (priv->vadjust->upper - priv->vadjust->lower)) *
1388 (widget->allocation.height -
1389 (priv->hscroll_visible ? priv->indicator_width : 0))) - y;
1391 /* Set a minimum height */
1392 height = MAX (SCROLL_BAR_MIN_SIZE, height);
1394 /* Check the max y position */
1395 y = MIN (y, widget->allocation.height -
1396 (priv->hscroll_visible ? priv->hscroll_rect.height : 0) -
1399 /* Draw the scrollbar */
1400 rgb_from_gdkcolor (scroll_color, &r, &g, &b);
1402 pattern = cairo_pattern_create_linear(radius+1, y, radius+1,y + height);
1403 cairo_pattern_add_color_stop_rgb(pattern, 0, r, g, b);
1404 cairo_pattern_add_color_stop_rgb(pattern, 1, r/2, g/2, b/2);
1405 cairo_set_source(cr, pattern);
1407 cairo_pattern_destroy(pattern);
1409 cairo_arc(cr, priv->vscroll_rect.x + radius + 1, y + radius + 1, radius, G_PI, 0);
1410 cairo_line_to(cr, priv->vscroll_rect.x + (radius * 2) + 1, y + height - radius);
1411 cairo_arc(cr, priv->vscroll_rect.x + radius + 1, y + height - radius, radius, 0, G_PI);
1412 cairo_line_to(cr, priv->vscroll_rect.x + 1, y + height - radius);
1415 cairo_paint_with_alpha(cr, priv->scroll_indicator_alpha);
1421 hildon_pannable_draw_hscroll (GtkWidget * widget,
1422 GdkColor *back_color,
1423 GdkColor *scroll_color)
1425 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1428 cairo_pattern_t *pattern;
1430 gint radius = (priv->hscroll_rect.height/2) - 1;
1432 cr = gdk_cairo_create(widget->window);
1434 /* Draw the background */
1435 rgb_from_gdkcolor (back_color, &r, &g, &b);
1436 cairo_set_source_rgb (cr, r, g, b);
1437 cairo_rectangle(cr, priv->hscroll_rect.x, priv->hscroll_rect.y,
1438 priv->hscroll_rect.width,
1439 priv->hscroll_rect.height);
1440 cairo_fill_preserve (cr);
1443 /* calculate the scrollbar width and position */
1444 x = ((priv->hadjust->value - priv->hadjust->lower) / (priv->hadjust->upper - priv->hadjust->lower)) *
1445 (widget->allocation.width - (priv->vscroll_visible ? priv->indicator_width : 0));
1446 width =((((priv->hadjust->value - priv->hadjust->lower) +
1447 priv->hadjust->page_size) / (priv->hadjust->upper - priv->hadjust->lower)) *
1448 (widget->allocation.width -
1449 (priv->vscroll_visible ? priv->indicator_width : 0))) - x;
1451 /* Set a minimum width */
1452 width = MAX (SCROLL_BAR_MIN_SIZE, width);
1454 /* Check the max x position */
1455 x = MIN (x, widget->allocation.width -
1456 (priv->vscroll_visible ? priv->vscroll_rect.width : 0) -
1459 /* Draw the scrollbar */
1460 rgb_from_gdkcolor (scroll_color, &r, &g, &b);
1462 pattern = cairo_pattern_create_linear(x, radius+1, x+width, radius+1);
1463 cairo_pattern_add_color_stop_rgb(pattern, 0, r, g, b);
1464 cairo_pattern_add_color_stop_rgb(pattern, 1, r/2, g/2, b/2);
1465 cairo_set_source(cr, pattern);
1467 cairo_pattern_destroy(pattern);
1469 cairo_arc_negative(cr, x + radius + 1, priv->hscroll_rect.y + radius + 1, radius, 3*G_PI_2, G_PI_2);
1470 cairo_line_to(cr, x + width - radius, priv->hscroll_rect.y + (radius * 2) + 1);
1471 cairo_arc_negative(cr, x + width - radius, priv->hscroll_rect.y + radius + 1, radius, G_PI_2, 3*G_PI_2);
1472 cairo_line_to(cr, x + width - radius, priv->hscroll_rect.y + 1);
1475 cairo_paint_with_alpha(cr, priv->scroll_indicator_alpha);
1480 #else /* USE_CAIRO_SCROLLBARS */
1483 tranparency_color (GdkColor *color,
1486 gdouble transparency)
1490 diff = colora.red - colorb.red;
1491 color->red = colora.red-diff*transparency;
1493 diff = colora.green - colorb.green;
1494 color->green = colora.green-diff*transparency;
1496 diff = colora.blue - colorb.blue;
1497 color->blue = colora.blue-diff*transparency;
1501 hildon_pannable_draw_vscroll (GtkWidget *widget,
1502 GdkColor *back_color,
1503 GdkColor *scroll_color)
1505 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1507 GdkColor transp_color;
1508 GdkGC *gc = priv->scrollbars_gc;
1510 gdk_draw_rectangle (widget->window,
1511 widget->style->bg_gc[GTK_STATE_NORMAL],
1513 priv->vscroll_rect.x, priv->vscroll_rect.y,
1514 priv->vscroll_rect.width,
1515 priv->vscroll_rect.height);
1517 y = ((priv->vadjust->value - priv->vadjust->lower) / (priv->vadjust->upper - priv->vadjust->lower)) *
1518 (widget->allocation.height - (priv->hscroll_visible ? priv->indicator_width : 0));
1519 height = ((((priv->vadjust->value - priv->vadjust->lower) + priv->vadjust->page_size) /
1520 (priv->vadjust->upper - priv->vadjust->lower)) *
1521 (widget->allocation.height -
1522 (priv->hscroll_visible ? priv->indicator_width : 0))) - y;
1524 /* Set a minimum height */
1525 height = MAX (SCROLL_BAR_MIN_SIZE, height);
1527 /* Check the max y position */
1528 y = MIN (y, widget->allocation.height -
1529 (priv->hscroll_visible ? priv->hscroll_rect.height : 0) -
1532 if (priv->scroll_indicator_alpha == 1.0) {
1533 transp_color = priv->scroll_color;
1534 } else if (priv->scroll_indicator_alpha < 1.0) {
1535 tranparency_color (&transp_color, *back_color, *scroll_color,
1536 priv->scroll_indicator_alpha);
1538 gdk_gc_set_rgb_fg_color (gc, &transp_color);
1540 gdk_draw_rectangle (widget->window, gc,
1541 TRUE, priv->vscroll_rect.x, y,
1542 priv->vscroll_rect.width, height);
1546 hildon_pannable_draw_hscroll (GtkWidget *widget,
1547 GdkColor *back_color,
1548 GdkColor *scroll_color)
1550 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1552 GdkColor transp_color;
1553 GdkGC *gc = priv->scrollbars_gc;
1555 gdk_draw_rectangle (widget->window,
1556 widget->style->bg_gc[GTK_STATE_INSENSITIVE],
1558 priv->hscroll_rect.x, priv->hscroll_rect.y,
1559 priv->hscroll_rect.width,
1560 priv->hscroll_rect.height);
1562 /* calculate the scrollbar width and position */
1563 x = ((priv->hadjust->value - priv->hadjust->lower) / (priv->hadjust->upper - priv->hadjust->lower)) *
1564 (widget->allocation.width - (priv->vscroll_visible ? priv->indicator_width : 0));
1565 width =((((priv->hadjust->value - priv->hadjust->lower) +
1566 priv->hadjust->page_size) / (priv->hadjust->upper - priv->hadjust->lower)) *
1567 (widget->allocation.width -
1568 (priv->vscroll_visible ? priv->indicator_width : 0))) - x;
1570 /* Set a minimum width */
1571 width = MAX (SCROLL_BAR_MIN_SIZE, width);
1573 /* Check the max x position */
1574 x = MIN (x, widget->allocation.width -
1575 (priv->vscroll_visible ? priv->vscroll_rect.width : 0) -
1578 if (priv->scroll_indicator_alpha == 1.0) {
1579 transp_color = priv->scroll_color;
1580 } else if (priv->scroll_indicator_alpha < 1.0) {
1581 tranparency_color (&transp_color, *back_color, *scroll_color,
1582 priv->scroll_indicator_alpha);
1584 gdk_gc_set_rgb_fg_color (gc, &transp_color);
1586 gdk_draw_rectangle (widget->window, gc,
1587 TRUE, x, priv->hscroll_rect.y, width,
1588 priv->hscroll_rect.height);
1591 #endif /* USE_CAIRO_SCROLLBARS */
1594 hildon_pannable_area_initial_effect (GtkWidget * widget)
1596 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1598 if (priv->initial_hint) {
1599 if (priv->vscroll_visible || priv->hscroll_visible) {
1601 priv->scroll_indicator_event_interrupt = 0;
1602 priv->scroll_delay_counter = priv->scrollbar_fade_delay;
1604 hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget), 1.0);
1610 hildon_pannable_area_launch_fade_timeout (HildonPannableArea * area,
1613 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (area)->priv;
1615 priv->scroll_indicator_alpha = alpha;
1617 if (!priv->scroll_indicator_timeout)
1618 priv->scroll_indicator_timeout =
1619 gdk_threads_add_timeout (SCROLL_FADE_TIMEOUT,
1620 (GSourceFunc) hildon_pannable_area_scroll_indicator_fade,
1625 hildon_pannable_area_adjust_changed (HildonPannableArea * area,
1628 if (GTK_WIDGET_REALIZED (area))
1629 hildon_pannable_area_refresh (area);
1633 hildon_pannable_area_adjust_value_changed (HildonPannableArea * area,
1636 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (area)->priv;
1638 gint x = priv->x_offset;
1639 gint y = priv->y_offset;
1641 priv->x_offset = priv->hadjust->value;
1642 xdiff = x - priv->x_offset;
1643 priv->y_offset = priv->vadjust->value;
1644 ydiff = y - priv->y_offset;
1646 if ((xdiff || ydiff) && GTK_WIDGET_DRAWABLE (area)) {
1647 hildon_pannable_area_redraw (area);
1649 if ((priv->vscroll_visible) || (priv->hscroll_visible)) {
1650 priv->scroll_indicator_event_interrupt = 0;
1651 priv->scroll_delay_counter = priv->scrollbar_fade_delay;
1653 hildon_pannable_area_launch_fade_timeout (area, 1.0);
1659 hildon_pannable_area_redraw (HildonPannableArea * area)
1661 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (area)->priv;
1663 /* Redraw scroll indicators */
1664 if (GTK_WIDGET_DRAWABLE (area)) {
1665 if (priv->hscroll_visible) {
1666 gdk_window_invalidate_rect (GTK_WIDGET (area)->window,
1667 &priv->hscroll_rect, FALSE);
1670 if (priv->vscroll_visible) {
1671 gdk_window_invalidate_rect (GTK_WIDGET (area)->window,
1672 &priv->vscroll_rect, FALSE);
1678 hildon_pannable_area_scroll_indicator_fade(HildonPannableArea * area)
1680 HildonPannableAreaPrivate *priv = area->priv;
1682 /* if moving do not fade out */
1683 if (((ABS (priv->vel_y)>priv->vmin)||
1684 (ABS (priv->vel_x)>priv->vmin))&&(!priv->button_pressed)) {
1689 if (priv->scroll_indicator_event_interrupt) {
1690 /* Stop a fade out, and fade back in */
1691 if (priv->scroll_indicator_alpha > 0.9) {
1692 priv->scroll_indicator_alpha = 1.0;
1693 priv->scroll_indicator_timeout = 0;
1697 priv->scroll_indicator_alpha += 0.2;
1698 hildon_pannable_area_redraw (area);
1704 if ((priv->scroll_indicator_alpha > 0.9) &&
1705 (priv->scroll_delay_counter > 0)) {
1706 priv->scroll_delay_counter--;
1711 if (!priv->scroll_indicator_event_interrupt) {
1712 /* Continue fade out */
1713 if (priv->scroll_indicator_alpha < 0.1) {
1714 priv->scroll_indicator_timeout = 0;
1715 priv->scroll_indicator_alpha = 0.0;
1719 priv->scroll_indicator_alpha -= 0.2;
1720 hildon_pannable_area_redraw (area);
1730 hildon_pannable_area_expose_event (GtkWidget * widget,
1731 GdkEventExpose * event)
1734 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1735 #if USE_CAIRO_SCROLLBARS == 1
1736 GdkColor back_color = widget->style->bg[GTK_STATE_NORMAL];
1737 GdkColor scroll_color = widget->style->base[GTK_STATE_SELECTED];
1738 #else /* USE_CAIRO_SCROLLBARS */
1739 GdkColor back_color = widget->style->bg[GTK_STATE_NORMAL];
1740 GdkColor scroll_color = priv->scroll_color;
1743 if (G_UNLIKELY (priv->initial_effect)) {
1744 hildon_pannable_area_initial_effect (widget);
1746 priv->initial_effect = FALSE;
1749 if (gtk_bin_get_child (GTK_BIN (widget))) {
1751 if (priv->scroll_indicator_alpha > 0.1) {
1752 if (priv->vscroll_visible) {
1753 hildon_pannable_draw_vscroll (widget, &back_color, &scroll_color);
1755 if (priv->hscroll_visible) {
1756 hildon_pannable_draw_hscroll (widget, &back_color, &scroll_color);
1760 /* draw overshooting rectangles */
1761 if (priv->overshot_dist_y > 0) {
1762 gint overshot_height;
1764 overshot_height = MIN (priv->overshot_dist_y, widget->allocation.height -
1765 (priv->hscroll_visible ? priv->hscroll_rect.height : 0));
1767 gdk_draw_rectangle (widget->window,
1768 widget->style->bg_gc[GTK_STATE_NORMAL],
1772 widget->allocation.width -
1773 (priv->vscroll_visible ? priv->vscroll_rect.width : 0),
1775 } else if (priv->overshot_dist_y < 0) {
1776 gint overshot_height;
1780 MAX (priv->overshot_dist_y,
1781 -(widget->allocation.height -
1782 (priv->hscroll_visible ? priv->hscroll_rect.height : 0)));
1784 overshot_y = MAX (widget->allocation.height +
1786 (priv->hscroll_visible ? priv->hscroll_rect.height : 0), 0);
1788 gdk_draw_rectangle (widget->window,
1789 widget->style->bg_gc[GTK_STATE_NORMAL],
1793 widget->allocation.width -
1794 priv->vscroll_rect.width,
1798 if (priv->overshot_dist_x > 0) {
1799 gint overshot_width;
1801 overshot_width = MIN (priv->overshot_dist_x, widget->allocation.width -
1802 (priv->vscroll_visible ? priv->vscroll_rect.width : 0));
1804 gdk_draw_rectangle (widget->window,
1805 widget->style->bg_gc[GTK_STATE_NORMAL],
1810 widget->allocation.height -
1811 (priv->hscroll_visible ? priv->hscroll_rect.height : 0));
1812 } else if (priv->overshot_dist_x < 0) {
1813 gint overshot_width;
1817 MAX (priv->overshot_dist_x,
1818 -(widget->allocation.width -
1819 (priv->vscroll_visible ? priv->vscroll_rect.width : 0)));
1821 overshot_x = MAX (widget->allocation.width +
1823 (priv->vscroll_visible ? priv->vscroll_rect.width : 0), 0);
1825 gdk_draw_rectangle (widget->window,
1826 widget->style->bg_gc[GTK_STATE_NORMAL],
1831 widget->allocation.height -
1832 priv->hscroll_rect.height);
1837 return GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->expose_event (widget, event);
1841 hildon_pannable_area_get_topmost (GdkWindow * window,
1843 gint * tx, gint * ty,
1846 /* Find the GdkWindow at the given point, by recursing from a given
1847 * parent GdkWindow. Optionally return the co-ordinates transformed
1848 * relative to the child window.
1851 GList *c, *children;
1852 GdkWindow *selected_window = NULL;
1854 gdk_drawable_get_size (GDK_DRAWABLE (window), &width, &height);
1855 if ((x < 0) || (x >= width) || (y < 0) || (y >= height))
1858 children = gdk_window_peek_children (window);
1865 selected_window = window;
1868 for (c = children; c; c = c->next) {
1869 GdkWindow *child = (GdkWindow *) c->data;
1872 gdk_drawable_get_size (GDK_DRAWABLE (child), &width, &height);
1873 gdk_window_get_position (child, &wx, &wy);
1875 if ((x >= wx) && (x < (wx + width)) && (y >= wy) && (y < (wy + height)) &&
1876 (gdk_window_is_visible (child))) {
1878 if (gdk_window_peek_children (child)) {
1879 selected_window = hildon_pannable_area_get_topmost (child, x-wx, y-wy,
1881 if (!selected_window) {
1886 selected_window = child;
1889 if ((gdk_window_get_events (child)&mask)) {
1894 selected_window = child;
1900 return selected_window;
1904 synth_crossing (GdkWindow * child,
1906 gint x_root, gint y_root,
1907 guint32 time, gboolean in)
1909 GdkEventCrossing *crossing_event;
1910 GdkEventType type = in ? GDK_ENTER_NOTIFY : GDK_LEAVE_NOTIFY;
1912 /* Send synthetic enter event */
1913 crossing_event = (GdkEventCrossing *) gdk_event_new (type);
1914 ((GdkEventAny *) crossing_event)->type = type;
1915 ((GdkEventAny *) crossing_event)->window = g_object_ref (child);
1916 ((GdkEventAny *) crossing_event)->send_event = FALSE;
1917 crossing_event->subwindow = g_object_ref (child);
1918 crossing_event->time = time;
1919 crossing_event->x = x;
1920 crossing_event->y = y;
1921 crossing_event->x_root = x_root;
1922 crossing_event->y_root = y_root;
1923 crossing_event->mode = GDK_CROSSING_NORMAL;
1924 crossing_event->detail = GDK_NOTIFY_UNKNOWN;
1925 crossing_event->focus = FALSE;
1926 crossing_event->state = 0;
1927 gdk_event_put ((GdkEvent *) crossing_event);
1928 gdk_event_free ((GdkEvent *) crossing_event);
1932 hildon_pannable_area_button_press_cb (GtkWidget * widget,
1933 GdkEventButton * event)
1936 HildonPannableArea *area = HILDON_PANNABLE_AREA (widget);
1937 HildonPannableAreaPrivate *priv = area->priv;
1939 if ((!priv->enabled) || (event->button != 1) ||
1940 ((event->time == priv->last_time) &&
1941 (priv->last_type == 1)) || (gtk_bin_get_child (GTK_BIN (widget)) == NULL))
1944 priv->scroll_indicator_event_interrupt = 1;
1946 hildon_pannable_area_launch_fade_timeout (area,
1947 priv->scroll_indicator_alpha);
1949 priv->last_time = event->time;
1950 priv->last_type = 1;
1952 priv->scroll_to_x = -1;
1953 priv->scroll_to_y = -1;
1955 if (priv->button_pressed && priv->child) {
1956 /* Widget stole focus on last click, send crossing-out event */
1957 synth_crossing (priv->child, 0, 0, event->x_root, event->y_root,
1958 event->time, FALSE);
1966 /* Don't allow a click if we're still moving fast */
1967 if ((ABS (priv->vel_x) <= (priv->vmax * priv->vfast_factor)) &&
1968 (ABS (priv->vel_y) <= (priv->vmax * priv->vfast_factor)))
1970 hildon_pannable_area_get_topmost (gtk_bin_get_child (GTK_BIN (widget))->window,
1971 event->x, event->y, &x, &y, GDK_BUTTON_PRESS_MASK);
1975 priv->button_pressed = TRUE;
1977 /* Stop scrolling on mouse-down (so you can flick, then hold to stop) */
1980 if (priv->idle_id) {
1981 g_source_remove (priv->idle_id);
1983 g_signal_emit (area, pannable_area_signals[PANNING_FINISHED], 0);
1988 gdk_drawable_get_size (priv->child, &priv->child_width,
1989 &priv->child_height);
1990 priv->last_in = TRUE;
1992 g_object_add_weak_pointer ((GObject *) priv->child,
1993 (gpointer) & priv->child);
1995 event = (GdkEventButton *) gdk_event_copy ((GdkEvent *) event);
1996 /* remove the reference we added with the copy */
1997 g_object_unref (priv->event_window);
2003 synth_crossing (priv->child, x, y, event->x_root,
2004 event->y_root, event->time, TRUE);
2006 /* Send synthetic click (button press/release) event */
2007 ((GdkEventAny *) event)->window = g_object_ref (priv->child);
2009 gdk_event_put ((GdkEvent *) event);
2010 gdk_event_free ((GdkEvent *) event);
2018 hildon_pannable_area_check_scrollbars (HildonPannableArea * area)
2020 HildonPannableAreaPrivate *priv = area->priv;
2021 gboolean prev_hscroll_visible, prev_vscroll_visible;
2023 prev_hscroll_visible = priv->hscroll_visible;
2024 prev_vscroll_visible = priv->vscroll_visible;
2026 if (!gtk_bin_get_child (GTK_BIN (area))) {
2027 priv->vscroll_visible = FALSE;
2028 priv->hscroll_visible = FALSE;
2030 switch (priv->hscrollbar_policy) {
2031 case GTK_POLICY_ALWAYS:
2032 priv->hscroll_visible = TRUE;
2034 case GTK_POLICY_NEVER:
2035 priv->hscroll_visible = FALSE;
2038 priv->hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
2039 priv->hadjust->page_size);
2042 switch (priv->vscrollbar_policy) {
2043 case GTK_POLICY_ALWAYS:
2044 priv->vscroll_visible = TRUE;
2046 case GTK_POLICY_NEVER:
2047 priv->vscroll_visible = FALSE;
2050 priv->vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
2051 priv->vadjust->page_size);
2054 /* Store the vscroll/hscroll areas for redrawing */
2055 if (priv->vscroll_visible) {
2056 GtkAllocation *allocation = >K_WIDGET (area)->allocation;
2057 priv->vscroll_rect.x = allocation->width - priv->indicator_width;
2058 priv->vscroll_rect.y = 0;
2059 priv->vscroll_rect.width = priv->indicator_width;
2060 priv->vscroll_rect.height = allocation->height -
2061 (priv->hscroll_visible ? priv->indicator_width : 0);
2063 if (priv->hscroll_visible) {
2064 GtkAllocation *allocation = >K_WIDGET (area)->allocation;
2065 priv->hscroll_rect.y = allocation->height - priv->indicator_width;
2066 priv->hscroll_rect.x = 0;
2067 priv->hscroll_rect.height = priv->indicator_width;
2068 priv->hscroll_rect.width = allocation->width -
2069 (priv->vscroll_visible ? priv->indicator_width : 0);
2073 return ((priv->hscroll_visible != prev_hscroll_visible) ||
2074 (priv->vscroll_visible != prev_vscroll_visible));
2078 hildon_pannable_area_refresh (HildonPannableArea * area)
2080 if (GTK_WIDGET_DRAWABLE (area) &&
2081 hildon_pannable_area_check_scrollbars (area)) {
2082 HildonPannableAreaPrivate *priv = area->priv;
2084 gtk_widget_queue_resize (GTK_WIDGET (area));
2086 if ((priv->vscroll_visible) || (priv->hscroll_visible)) {
2087 priv->scroll_indicator_event_interrupt = 0;
2088 priv->scroll_delay_counter = area->priv->scrollbar_fade_delay;
2090 hildon_pannable_area_launch_fade_timeout (area, 1.0);
2093 hildon_pannable_area_redraw (area);
2097 /* Scroll by a particular amount (in pixels). Optionally, return if
2098 * the scroll on a particular axis was successful.
2101 hildon_pannable_axis_scroll (HildonPannableArea *area,
2102 GtkAdjustment *adjust,
2106 gint *overshot_dist,
2112 HildonPannableAreaPrivate *priv = area->priv;
2114 dist = gtk_adjustment_get_value (adjust) - inc;
2117 * We use overshot_dist to define the distance of the current overshoot,
2118 * and overshooting to define the direction/whether or not we are overshot
2120 if (!(*overshooting)) {
2122 /* Initiation of the overshoot happens when the finger is released
2123 * and the current position of the pannable contents are out of range
2125 if (dist < adjust->lower) {
2128 dist = adjust->lower;
2130 if (overshoot_max!=0) {
2133 *overshot_dist = CLAMP (*overshot_dist + *vel, 0, overshoot_max);
2134 *vel = MIN (priv->vmax_overshooting, *vel);
2135 gtk_widget_queue_resize (GTK_WIDGET (area));
2139 } else if (dist > adjust->upper - adjust->page_size) {
2142 dist = adjust->upper - adjust->page_size;
2144 if (overshoot_max!=0) {
2147 *overshot_dist = CLAMP (*overshot_dist + *vel, -overshoot_max, 0);
2148 *vel = MAX (-priv->vmax_overshooting, *vel);
2149 gtk_widget_queue_resize (GTK_WIDGET (area));
2154 if ((*scroll_to) != -1) {
2155 if (((inc < 0)&&(*scroll_to <= dist))||
2156 ((inc > 0)&&(*scroll_to >= dist))) {
2164 adjust->value = dist;
2166 if (!priv->button_pressed) {
2168 /* When the overshoot has started we continue for
2169 * PROP_BOUNCE_STEPS more steps into the overshoot before we
2170 * reverse direction. The deceleration factor is calculated
2171 * based on the percentage distance from the first item with
2172 * each iteration, therefore always returning us to the
2173 * top/bottom most element
2175 if (*overshot_dist > 0) {
2177 if ((*overshooting < priv->bounce_steps) && (*vel > 0)) {
2179 *vel = (((gdouble)*overshot_dist)/overshoot_max) * (*vel);
2180 } else if ((*overshooting >= priv->bounce_steps) && (*vel > 0)) {
2182 } else if ((*overshooting > 1) && (*vel < 0)) {
2183 /* we add the MIN in order to avoid very small speeds */
2184 *vel = MIN (((((gdouble)*overshot_dist)*0.8) * -1), -10.0);
2187 *overshot_dist = CLAMP (*overshot_dist + *vel, 0, overshoot_max);
2189 gtk_widget_queue_resize (GTK_WIDGET (area));
2191 } else if (*overshot_dist < 0) {
2193 if ((*overshooting < priv->bounce_steps) && (*vel < 0)) {
2195 *vel = (((gdouble)*overshot_dist)/overshoot_max) * (*vel) * -1;
2196 } else if ((*overshooting >= priv->bounce_steps) && (*vel < 0)) {
2198 } else if ((*overshooting > 1) && (*vel > 0)) {
2199 /* we add the MAX in order to avoid very small speeds */
2200 *vel = MAX (((((gdouble)*overshot_dist)*0.8) * -1), 10.0);
2203 *overshot_dist = CLAMP (*overshot_dist + (*vel), -overshoot_max, 0);
2205 gtk_widget_queue_resize (GTK_WIDGET (area));
2210 gtk_widget_queue_resize (GTK_WIDGET (area));
2214 gint overshot_dist_old = *overshot_dist;
2216 if (*overshot_dist > 0) {
2217 *overshot_dist = CLAMP ((*overshot_dist) + inc, 0, overshoot_max);
2218 } else if (*overshot_dist < 0) {
2219 *overshot_dist = CLAMP ((*overshot_dist) + inc, -1 * overshoot_max, 0);
2222 adjust->value = CLAMP (dist,
2228 if (*overshot_dist != overshot_dist_old)
2229 gtk_widget_queue_resize (GTK_WIDGET (area));
2235 hildon_pannable_area_scroll (HildonPannableArea *area,
2236 gdouble x, gdouble y)
2239 HildonPannableAreaPrivate *priv = area->priv;
2240 gboolean hscroll_visible, vscroll_visible;
2243 if (gtk_bin_get_child (GTK_BIN (area)) == NULL)
2246 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
2247 priv->vadjust->page_size);
2248 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
2249 priv->hadjust->page_size);
2254 hv = priv->hadjust->value;
2255 vv = priv->vadjust->value;
2257 if (vscroll_visible) {
2258 hildon_pannable_axis_scroll (area, priv->vadjust, &priv->vel_y, y,
2259 &priv->overshooting_y, &priv->overshot_dist_y,
2260 &priv->scroll_to_y, priv->vovershoot_max, &sy);
2265 if (hscroll_visible) {
2266 hildon_pannable_axis_scroll (area, priv->hadjust, &priv->vel_x, x,
2267 &priv->overshooting_x, &priv->overshot_dist_x,
2268 &priv->scroll_to_x, priv->hovershoot_max, &sx);
2273 if (hv != priv->hadjust->value)
2274 gtk_adjustment_value_changed (priv->hadjust);
2276 if (vv != priv->vadjust->value)
2277 gtk_adjustment_value_changed (priv->vadjust);
2279 /* If the scroll on a particular axis wasn't succesful, reset the
2280 * initial scroll position to the new mouse co-ordinate. This means
2281 * when you get to the top of the page, dragging down works immediately.
2283 if (priv->mode == HILDON_PANNABLE_AREA_MODE_ACCEL) {
2295 hildon_pannable_area_timeout (HildonPannableArea * area)
2297 HildonPannableAreaPrivate *priv = area->priv;
2299 if ((!priv->enabled) || (priv->mode == HILDON_PANNABLE_AREA_MODE_PUSH)) {
2301 g_signal_emit (area, pannable_area_signals[PANNING_FINISHED], 0);
2306 if (!priv->button_pressed) {
2307 /* Decelerate gradually when pointer is raised */
2308 if ((!priv->overshot_dist_y) &&
2309 (!priv->overshot_dist_x)) {
2311 /* in case we move to a specific point do not decelerate when arriving */
2312 if ((priv->scroll_to_x != -1)||(priv->scroll_to_y != -1)) {
2314 if (ABS (priv->vel_x) >= 1.5) {
2315 priv->vel_x *= priv->decel;
2318 if (ABS (priv->vel_y) >= 1.5) {
2319 priv->vel_y *= priv->decel;
2323 if ((!priv->low_friction_mode) ||
2324 ((priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ) &&
2325 (ABS (priv->vel_x) < 0.8*priv->vmax)))
2326 priv->vel_x *= priv->decel;
2328 if ((!priv->low_friction_mode) ||
2329 ((priv->mov_mode&HILDON_MOVEMENT_MODE_VERT) &&
2330 (ABS (priv->vel_y) < 0.8*priv->vmax)))
2331 priv->vel_y *= priv->decel;
2333 if ((ABS (priv->vel_x) < 1.0) && (ABS (priv->vel_y) < 1.0)) {
2338 g_signal_emit (area, pannable_area_signals[PANNING_FINISHED], 0);
2344 } else if (priv->mode == HILDON_PANNABLE_AREA_MODE_AUTO) {
2350 hildon_pannable_area_scroll (area, priv->vel_x, priv->vel_y);
2352 gdk_window_process_updates (GTK_WIDGET (area)->window, FALSE);
2358 hildon_pannable_area_calculate_velocity (gdouble *vel,
2362 gdouble drag_inertia,
2368 if (ABS (dist) >= RATIO_TOLERANCE) {
2369 rawvel = (dist / ABS (delta)) * force;
2370 *vel = *vel * (1 - drag_inertia) +
2371 rawvel * drag_inertia;
2372 *vel = *vel > 0 ? MIN (*vel, vmax)
2373 : MAX (*vel, -1 * vmax);
2378 hildon_pannable_area_motion_event_scroll_timeout (HildonPannableArea *area)
2380 HildonPannableAreaPrivate *priv = area->priv;
2382 if ((priv->motion_x != 0)||(priv->motion_y != 0))
2383 hildon_pannable_area_scroll (area, priv->motion_x, priv->motion_y);
2385 priv->motion_event_scroll_timeout = 0;
2391 hildon_pannable_area_motion_event_scroll (HildonPannableArea *area,
2392 gdouble x, gdouble y)
2394 HildonPannableAreaPrivate *priv = area->priv;
2396 if (priv->motion_event_scroll_timeout) {
2398 priv->motion_x += x;
2399 priv->motion_y += y;
2403 /* we do not delay the first event but the next ones */
2404 hildon_pannable_area_scroll (area, x, y);
2409 priv->motion_event_scroll_timeout = gdk_threads_add_timeout
2410 ((gint) (1000.0 / (gdouble) MOTION_EVENTS_PER_SECOND),
2411 (GSourceFunc) hildon_pannable_area_motion_event_scroll_timeout, area);
2416 hildon_pannable_area_check_move (HildonPannableArea *area,
2417 GdkEventMotion * event,
2421 HildonPannableAreaPrivate *priv = area->priv;
2423 if (priv->first_drag && (!priv->moved) &&
2424 ((ABS (*x) > (priv->panning_threshold))
2425 || (ABS (*y) > (priv->panning_threshold)))) {
2430 if (priv->first_drag) {
2431 gboolean vscroll_visible;
2432 gboolean hscroll_visible;
2434 if (ABS (priv->iy - event->y) >=
2435 ABS (priv->ix - event->x)) {
2437 g_signal_emit (area,
2438 pannable_area_signals[VERTICAL_MOVEMENT],
2439 0, (priv->iy > event->y) ?
2440 HILDON_MOVEMENT_UP :
2441 HILDON_MOVEMENT_DOWN,
2442 (gdouble)priv->ix, (gdouble)priv->iy);
2444 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
2445 priv->vadjust->page_size);
2447 if (!((vscroll_visible)&&
2448 (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT))) {
2450 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
2451 priv->hadjust->page_size);
2453 /* even in case we do not have to move we check if this
2454 could be a fake horizontal movement */
2455 if (!((hscroll_visible)&&
2456 (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ)) ||
2457 (ABS (priv->iy - event->y) -
2458 ABS (priv->ix - event->x) >= priv->direction_error_margin))
2459 priv->moved = FALSE;
2463 g_signal_emit (area,
2464 pannable_area_signals[HORIZONTAL_MOVEMENT],
2465 0, (priv->ix > event->x) ?
2466 HILDON_MOVEMENT_LEFT :
2467 HILDON_MOVEMENT_RIGHT,
2468 (gdouble)priv->ix, (gdouble)priv->iy);
2470 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
2471 priv->hadjust->page_size);
2473 if (!((hscroll_visible)&&
2474 (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ))) {
2476 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
2477 priv->vadjust->page_size);
2479 /* even in case we do not have to move we check if this
2480 could be a fake vertical movement */
2481 if (!((vscroll_visible) &&
2482 (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT)) ||
2483 (ABS (priv->ix - event->x) -
2484 ABS (priv->iy - event->y) >= priv->direction_error_margin))
2485 priv->moved = FALSE;
2489 if ((priv->moved)&&(priv->child)) {
2492 pos_x = priv->cx + (event->x - priv->ix);
2493 pos_y = priv->cy + (event->y - priv->iy);
2495 synth_crossing (priv->child, pos_x, pos_y, event->x_root,
2496 event->y_root, event->time, FALSE);
2500 gboolean result_val;
2502 g_signal_emit (area,
2503 pannable_area_signals[PANNING_STARTED],
2506 priv->moved = !result_val;
2510 priv->first_drag = FALSE;
2512 if ((priv->mode != HILDON_PANNABLE_AREA_MODE_PUSH) &&
2513 (priv->mode != HILDON_PANNABLE_AREA_MODE_AUTO)) {
2516 priv->idle_id = gdk_threads_add_timeout ((gint)
2517 (1000.0 / (gdouble) priv->sps),
2519 hildon_pannable_area_timeout, area);
2525 hildon_pannable_area_handle_move (HildonPannableArea *area,
2526 GdkEventMotion * event,
2530 HildonPannableAreaPrivate *priv = area->priv;
2533 switch (priv->mode) {
2534 case HILDON_PANNABLE_AREA_MODE_PUSH:
2535 /* Scroll by the amount of pixels the cursor has moved
2536 * since the last motion event.
2538 hildon_pannable_area_motion_event_scroll (area, *x, *y);
2542 case HILDON_PANNABLE_AREA_MODE_ACCEL:
2543 /* Set acceleration relative to the initial click */
2544 priv->ex = event->x;
2545 priv->ey = event->y;
2546 priv->vel_x = ((*x > 0) ? 1 : -1) *
2548 (gdouble) GTK_WIDGET (area)->allocation.width) *
2549 (priv->vmax - priv->vmin)) + priv->vmin);
2550 priv->vel_y = ((*y > 0) ? 1 : -1) *
2552 (gdouble) GTK_WIDGET (area)->allocation.height) *
2553 (priv->vmax - priv->vmin)) + priv->vmin);
2555 case HILDON_PANNABLE_AREA_MODE_AUTO:
2557 delta = event->time - priv->last_time;
2559 if (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT) {
2560 gdouble dist = event->y - priv->y;
2562 hildon_pannable_area_calculate_velocity (&priv->vel_y,
2574 if (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ) {
2575 gdouble dist = event->x - priv->x;
2577 hildon_pannable_area_calculate_velocity (&priv->vel_x,
2589 hildon_pannable_area_motion_event_scroll (area, *x, *y);
2591 if (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ)
2593 if (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT)
2603 hildon_pannable_area_motion_notify_cb (GtkWidget * widget,
2604 GdkEventMotion * event)
2606 HildonPannableArea *area = HILDON_PANNABLE_AREA (widget);
2607 HildonPannableAreaPrivate *priv = area->priv;
2610 if (gtk_bin_get_child (GTK_BIN (widget)) == NULL)
2613 if ((!priv->enabled) || (!priv->button_pressed) ||
2614 ((event->time == priv->last_time) && (priv->last_type == 2))) {
2615 gdk_window_get_pointer (widget->window, NULL, NULL, 0);
2619 if (priv->last_type == 1) {
2620 priv->first_drag = TRUE;
2623 x = event->x - priv->x;
2624 y = event->y - priv->y;
2627 hildon_pannable_area_check_move (area, event, &x, &y);
2631 hildon_pannable_area_handle_move (area, event, &x, &y);
2632 } else if (priv->child) {
2636 pos_x = priv->cx + (event->x - priv->ix);
2637 pos_y = priv->cy + (event->y - priv->iy);
2639 in = (((0 <= pos_x)&&(priv->child_width >= pos_x)) &&
2640 ((0 <= pos_y)&&(priv->child_height >= pos_y)));
2642 if (((!priv->last_in)&&in)||((priv->last_in)&&(!in))) {
2644 synth_crossing (priv->child, pos_x, pos_y, event->x_root,
2645 event->y_root, event->time, in);
2651 priv->last_time = event->time;
2652 priv->last_type = 2;
2655 /* Send motion notify to child */
2656 event = (GdkEventMotion *) gdk_event_copy ((GdkEvent *) event);
2657 /* remove the reference we added with the copy */
2658 g_object_unref (priv->event_window);
2659 event->x = priv->cx + (event->x - priv->ix);
2660 event->y = priv->cy + (event->y - priv->iy);
2661 event->window = g_object_ref (priv->child);
2662 gdk_event_put ((GdkEvent *) event);
2663 gdk_event_free ((GdkEvent *) event);
2666 gdk_window_get_pointer (widget->window, NULL, NULL, 0);
2672 hildon_pannable_leave_notify_event (GtkWidget *widget,
2673 GdkEventCrossing *event)
2675 HildonPannableArea *area = HILDON_PANNABLE_AREA (widget);
2676 HildonPannableAreaPrivate *priv = area->priv;
2678 if ((priv->child)&&(priv->last_in)) {
2679 priv->last_in = FALSE;
2681 synth_crossing (priv->child, 0, 0, event->x_root,
2682 event->y_root, event->time, FALSE);
2689 hildon_pannable_area_button_release_cb (GtkWidget * widget,
2690 GdkEventButton * event)
2692 HildonPannableArea *area = HILDON_PANNABLE_AREA (widget);
2693 HildonPannableAreaPrivate *priv = area->priv;
2698 if (((event->time == priv->last_time) && (priv->last_type == 3))
2699 || (gtk_bin_get_child (GTK_BIN (widget)) == NULL)
2700 || (!priv->button_pressed) || (!priv->enabled) || (event->button != 1))
2703 /* if last event was a motion-notify we have to check the movement
2704 and launch the animation */
2705 if (priv->last_type == 2) {
2707 dx = event->x - priv->x;
2708 dy = event->y - priv->y;
2710 hildon_pannable_area_check_move (area, (GdkEventMotion *) event, &dx, &dy);
2713 gdouble delta = event->time - priv->last_time;
2715 hildon_pannable_area_handle_move (area, (GdkEventMotion *) event, &dx, &dy);
2717 /* move all the way to the last position now */
2718 if (priv->motion_event_scroll_timeout) {
2719 g_source_remove (priv->motion_event_scroll_timeout);
2720 hildon_pannable_area_motion_event_scroll_timeout (HILDON_PANNABLE_AREA (widget));
2725 if ((ABS (dx) < 4.0) && (delta >= CURSOR_STOPPED_TIMEOUT))
2728 if ((ABS (dy) < 4.0) && (delta >= CURSOR_STOPPED_TIMEOUT))
2733 /* If overshoot has been initiated with a finger down, on release set max speed */
2734 if (priv->overshot_dist_y != 0) {
2735 priv->overshooting_y = priv->bounce_steps; /* Hack to stop a bounce in the finger down case */
2736 priv->vel_y = priv->overshot_dist_y * 0.9;
2739 if (priv->overshot_dist_x != 0) {
2740 priv->overshooting_x = priv->bounce_steps; /* Hack to stop a bounce in the finger down case */
2741 priv->vel_x = priv->overshot_dist_x * 0.9;
2744 priv->button_pressed = FALSE;
2746 if ((ABS (priv->vel_y) >= priv->vmin) ||
2747 (ABS (priv->vel_x) >= priv->vmin)) {
2749 /* we have to move because we are in overshooting position*/
2751 gboolean result_val;
2753 g_signal_emit (area,
2754 pannable_area_signals[PANNING_STARTED],
2758 priv->scroll_indicator_alpha = 1.0;
2760 if (ABS (priv->vel_x) > MAX_SPEED_THRESHOLD)
2761 priv->vel_x = (priv->vel_x > 0) ? priv->vmax : -priv->vmax;
2763 if (ABS (priv->vel_y) > MAX_SPEED_THRESHOLD)
2764 priv->vel_y = (priv->vel_y > 0) ? priv->vmax : -priv->vmax;
2767 priv->idle_id = gdk_threads_add_timeout ((gint) (1000.0 / (gdouble) priv->sps),
2769 hildon_pannable_area_timeout, widget);
2771 if (priv->center_on_child_focus_pending) {
2772 hildon_pannable_area_center_on_child_focus (area);
2776 g_signal_emit (widget, pannable_area_signals[PANNING_FINISHED], 0);
2779 area->priv->center_on_child_focus_pending = FALSE;
2781 priv->scroll_indicator_event_interrupt = 0;
2782 priv->scroll_delay_counter = priv->scrollbar_fade_delay;
2784 hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget),
2785 priv->scroll_indicator_alpha);
2787 priv->last_time = event->time;
2788 priv->last_type = 3;
2791 priv->moved = FALSE;
2796 hildon_pannable_area_get_topmost (gtk_bin_get_child (GTK_BIN (widget))->window,
2797 event->x, event->y, &x, &y, GDK_BUTTON_RELEASE_MASK);
2799 event = (GdkEventButton *) gdk_event_copy ((GdkEvent *) event);
2800 /* remove the reference we added with the copy */
2801 g_object_unref (priv->event_window);
2805 /* Leave the widget if we've moved - This doesn't break selection,
2806 * but stops buttons from being clicked.
2808 if ((child != priv->child) || (priv->moved)) {
2809 /* Send synthetic leave event */
2810 synth_crossing (priv->child, x, y, event->x_root,
2811 event->y_root, event->time, FALSE);
2812 /* insure no click will happen for widgets that do not handle
2816 /* Send synthetic button release event */
2817 ((GdkEventAny *) event)->window = g_object_ref (priv->child);
2818 gdk_event_put ((GdkEvent *) event);
2820 /* Send synthetic button release event */
2821 ((GdkEventAny *) event)->window = g_object_ref (child);
2822 gdk_event_put ((GdkEvent *) event);
2823 /* Send synthetic leave event */
2824 synth_crossing (priv->child, x, y, event->x_root,
2825 event->y_root, event->time, FALSE);
2827 g_object_remove_weak_pointer ((GObject *) priv->child,
2828 (gpointer) & priv->child);
2830 priv->moved = FALSE;
2831 gdk_event_free ((GdkEvent *) event);
2836 /* utility event handler */
2838 hildon_pannable_area_scroll_cb (GtkWidget *widget,
2839 GdkEventScroll *event)
2841 GtkAdjustment *adj = NULL;
2842 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
2844 if ((!priv->enabled) ||
2845 (gtk_bin_get_child (GTK_BIN (widget)) == NULL))
2848 priv->scroll_indicator_event_interrupt = 0;
2849 priv->scroll_delay_counter = priv->scrollbar_fade_delay + 20;
2851 hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget), 1.0);
2853 /* Stop inertial scrolling */
2854 if (priv->idle_id) {
2857 priv->overshooting_x = 0;
2858 priv->overshooting_y = 0;
2860 if ((priv->overshot_dist_x>0)||(priv->overshot_dist_y>0)) {
2861 priv->overshot_dist_x = 0;
2862 priv->overshot_dist_y = 0;
2864 gtk_widget_queue_resize (GTK_WIDGET (widget));
2867 g_signal_emit (widget, pannable_area_signals[PANNING_FINISHED], 0);
2869 g_source_remove (priv->idle_id);
2873 if (event->direction == GDK_SCROLL_UP || event->direction == GDK_SCROLL_DOWN)
2874 adj = priv->vadjust;
2876 adj = priv->hadjust;
2880 gdouble delta, new_value;
2882 /* from gtkrange.c calculate delta*/
2883 delta = pow (adj->page_size, 2.0 / 3.0);
2885 if (event->direction == GDK_SCROLL_UP ||
2886 event->direction == GDK_SCROLL_LEFT)
2889 new_value = CLAMP (adj->value + delta, adj->lower, adj->upper - adj->page_size);
2891 gtk_adjustment_set_value (adj, new_value);
2898 hildon_pannable_area_child_mapped (GtkWidget *widget,
2902 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (user_data)->priv;
2904 if (priv->event_window != NULL && priv->enabled)
2905 gdk_window_raise (priv->event_window);
2909 hildon_pannable_area_add (GtkContainer *container, GtkWidget *child)
2911 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (container)->priv;
2913 g_return_if_fail (gtk_bin_get_child (GTK_BIN (container)) == NULL);
2915 gtk_widget_set_parent (child, GTK_WIDGET (container));
2916 GTK_BIN (container)->child = child;
2918 g_signal_connect_after (child, "map-event",
2919 G_CALLBACK (hildon_pannable_area_child_mapped),
2922 if (!gtk_widget_set_scroll_adjustments (child, priv->hadjust, priv->vadjust)) {
2923 g_warning ("%s: cannot add non scrollable widget, "
2924 "wrap it in a viewport", __FUNCTION__);
2928 /* call this function if you are not panning */
2930 hildon_pannable_area_center_on_child_focus (HildonPannableArea *area)
2932 GtkWidget *focused_child = NULL;
2933 GtkWidget *window = NULL;
2935 window = gtk_widget_get_toplevel (GTK_WIDGET (area));
2937 if (GTK_WIDGET_TOPLEVEL (window)) {
2938 focused_child = gtk_window_get_focus (GTK_WINDOW (window));
2941 if (focused_child) {
2942 hildon_pannable_area_scroll_to_child (area, focused_child);
2947 hildon_pannable_area_set_focus_child (GtkContainer *container,
2950 HildonPannableArea *area = HILDON_PANNABLE_AREA (container);
2952 if (!area->priv->center_on_child_focus) {
2956 if (GTK_IS_WIDGET (child)) {
2957 area->priv->center_on_child_focus_pending = TRUE;
2962 hildon_pannable_area_remove (GtkContainer *container, GtkWidget *child)
2964 g_return_if_fail (HILDON_IS_PANNABLE_AREA (container));
2965 g_return_if_fail (child != NULL);
2966 g_return_if_fail (gtk_bin_get_child (GTK_BIN (container)) == child);
2968 gtk_widget_set_scroll_adjustments (child, NULL, NULL);
2970 g_signal_handlers_disconnect_by_func (child,
2971 hildon_pannable_area_child_mapped,
2974 /* chain parent class handler to remove child */
2975 GTK_CONTAINER_CLASS (hildon_pannable_area_parent_class)->remove (container, child);
2979 * This method calculates a factor necessary to determine the initial distance
2980 * to jump in hildon_pannable_area_scroll_to(). For fixed time and frames per
2981 * second, we know in how many frames 'n' we need to reach the destination
2982 * point. We know that, for a distance d,
2984 * d = d_0 + d_1 + ... + d_n
2986 * where d_i is the distance travelled in the i-th frame and decel_factor is
2987 * the deceleration factor. This can be rewritten as
2989 * d = d_0 + (d_0 * decel_factor) + ... + (d_n-1 * decel_factor),
2991 * since the distance travelled on each frame is the distance travelled in the
2992 * previous frame reduced by the deceleration factor. Reducing this and
2993 * factoring d_0 out, we get
2995 * d = d_0 (1 + decel_factor + ... + decel_factor^(n-1)).
2997 * Since the sum is independent of the distance to be travelled, we can define
3000 * vel_factor = 1 + decel_factor + ... + decel_factor^(n-1).
3002 * That's the gem we calculate in this method.
3005 hildon_pannable_calculate_vel_factor (HildonPannableArea * self)
3007 HildonPannableAreaPrivate *priv = self->priv;
3012 n = ceil (priv->sps * priv->scroll_time);
3014 for (i = 1; i < n && fct_i >= RATIO_TOLERANCE; i++) {
3015 fct_i *= priv->decel;
3019 priv->vel_factor = fct;
3023 * hildon_pannable_area_new:
3025 * Create a new pannable area widget
3027 * Returns: the newly created #HildonPannableArea
3033 hildon_pannable_area_new (void)
3035 return g_object_new (HILDON_TYPE_PANNABLE_AREA, NULL);
3039 * hildon_pannable_area_new_full:
3040 * @mode: #HildonPannableAreaMode
3041 * @enabled: Value for the enabled property
3042 * @vel_min: Value for the velocity-min property
3043 * @vel_max: Value for the velocity-max property
3044 * @decel: Value for the deceleration property
3045 * @sps: Value for the sps property
3047 * Create a new #HildonPannableArea widget and set various properties
3049 * returns: the newly create #HildonPannableArea
3055 hildon_pannable_area_new_full (gint mode, gboolean enabled,
3056 gdouble vel_min, gdouble vel_max,
3057 gdouble decel, guint sps)
3059 return g_object_new (HILDON_TYPE_PANNABLE_AREA,
3062 "velocity_min", vel_min,
3063 "velocity_max", vel_max,
3064 "deceleration", decel, "sps", sps, NULL);
3068 * hildon_pannable_area_add_with_viewport:
3069 * @area: A #HildonPannableArea
3070 * @child: Child widget to add to the viewport
3072 * Convenience function used to add a child to a #GtkViewport, and add the
3073 * viewport to the scrolled window.
3079 hildon_pannable_area_add_with_viewport (HildonPannableArea * area,
3083 GtkWidget *viewport;
3085 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3086 g_return_if_fail (GTK_IS_WIDGET (child));
3087 g_return_if_fail (child->parent == NULL);
3089 bin = GTK_BIN (area);
3091 if (bin->child != NULL)
3093 g_return_if_fail (GTK_IS_VIEWPORT (bin->child));
3094 g_return_if_fail (GTK_BIN (bin->child)->child == NULL);
3096 viewport = bin->child;
3100 HildonPannableAreaPrivate *priv = area->priv;
3102 viewport = gtk_viewport_new (priv->hadjust,
3104 gtk_viewport_set_shadow_type (GTK_VIEWPORT (viewport), GTK_SHADOW_NONE);
3105 gtk_container_add (GTK_CONTAINER (area), viewport);
3108 gtk_widget_show (viewport);
3109 gtk_container_add (GTK_CONTAINER (viewport), child);
3113 * hildon_pannable_area_scroll_to:
3114 * @area: A #HildonPannableArea.
3115 * @x: The x coordinate of the destination point or -1 to ignore this axis.
3116 * @y: The y coordinate of the destination point or -1 to ignore this axis.
3118 * Smoothly scrolls @area to ensure that (@x, @y) is a visible point
3119 * on the widget. To move in only one coordinate, you must set the other one
3120 * to -1. Notice that, in %HILDON_PANNABLE_AREA_MODE_PUSH mode, this function
3121 * works just like hildon_pannable_area_jump_to().
3123 * This function is useful if you need to present the user with a particular
3124 * element inside a scrollable widget, like #GtkTreeView. For instance,
3125 * the following example shows how to scroll inside a #GtkTreeView to
3126 * make visible an item, indicated by the #GtkTreeIter @iter.
3130 * GtkTreePath *path;
3131 * GdkRectangle *rect;
3133 * path = gtk_tree_model_get_path (model, &iter);
3134 * gtk_tree_view_get_background_area (GTK_TREE_VIEW (treeview),
3135 * path, NULL, &rect);
3136 * gtk_tree_view_convert_bin_window_to_tree_coords (GTK_TREE_VIEW (treeview),
3137 * 0, rect.y, NULL, &y);
3138 * hildon_pannable_area_scroll_to (panarea, -1, y);
3139 * gtk_tree_path_free (path);
3143 * If you want to present a child widget in simpler scenarios,
3144 * use hildon_pannable_area_scroll_to_child() instead.
3146 * There is a precondition to this function: the widget must be
3147 * already realized. Check the hildon_pannable_area_jump_to_child() for
3148 * more tips regarding how to call this function during
3154 hildon_pannable_area_scroll_to (HildonPannableArea *area,
3155 const gint x, const gint y)
3157 HildonPannableAreaPrivate *priv;
3159 gint dist_x, dist_y;
3160 gboolean hscroll_visible, vscroll_visible;
3162 g_return_if_fail (GTK_WIDGET_REALIZED (area));
3163 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3167 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
3168 priv->vadjust->page_size);
3169 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
3170 priv->hadjust->page_size);
3172 if (((!vscroll_visible)&&(!hscroll_visible)) ||
3173 (x == -1 && y == -1)) {
3177 if (priv->mode == HILDON_PANNABLE_AREA_MODE_PUSH)
3178 hildon_pannable_area_jump_to (area, x, y);
3180 width = priv->hadjust->upper - priv->hadjust->lower;
3181 height = priv->vadjust->upper - priv->vadjust->lower;
3183 g_return_if_fail (x < width || y < height);
3185 if ((x > -1)&&(hscroll_visible)) {
3186 priv->scroll_to_x = CLAMP (x - priv->hadjust->page_size/2,
3187 priv->hadjust->lower,
3188 priv->hadjust->upper - priv->hadjust->page_size);
3189 dist_x = priv->scroll_to_x - priv->hadjust->value;
3191 priv->scroll_to_x = -1;
3193 priv->vel_x = - dist_x/priv->vel_factor;
3196 priv->scroll_to_x = -1;
3199 if ((y > -1)&&(vscroll_visible)) {
3200 priv->scroll_to_y = CLAMP (y - priv->vadjust->page_size/2,
3201 priv->vadjust->lower,
3202 priv->vadjust->upper - priv->vadjust->page_size);
3203 dist_y = priv->scroll_to_y - priv->vadjust->value;
3205 priv->scroll_to_y = -1;
3207 priv->vel_y = - dist_y/priv->vel_factor;
3210 priv->scroll_to_y = y;
3213 if ((priv->scroll_to_y == -1) && (priv->scroll_to_x == -1)) {
3217 hildon_pannable_area_launch_fade_timeout (area, 1.0);
3220 priv->idle_id = gdk_threads_add_timeout ((gint) (1000.0 / (gdouble) priv->sps),
3222 hildon_pannable_area_timeout, area);
3226 * hildon_pannable_area_jump_to:
3227 * @area: A #HildonPannableArea.
3228 * @x: The x coordinate of the destination point or -1 to ignore this axis.
3229 * @y: The y coordinate of the destination point or -1 to ignore this axis.
3231 * Jumps the position of @area to ensure that (@x, @y) is a visible
3232 * point in the widget. In order to move in only one coordinate, you
3233 * must set the other one to -1. See hildon_pannable_area_scroll_to()
3234 * function for an example of how to calculate the position of
3235 * children in scrollable widgets like #GtkTreeview.
3237 * There is a precondition to this function: the widget must be
3238 * already realized. Check the hildon_pannable_area_jump_to_child() for
3239 * more tips regarding how to call this function during
3245 hildon_pannable_area_jump_to (HildonPannableArea *area,
3246 const gint x, const gint y)
3248 HildonPannableAreaPrivate *priv;
3252 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3253 g_return_if_fail (GTK_WIDGET_REALIZED (area));
3254 g_return_if_fail (x >= -1 && y >= -1);
3256 if (x == -1 && y == -1) {
3262 width = priv->hadjust->upper - priv->hadjust->lower;
3263 height = priv->vadjust->upper - priv->vadjust->lower;
3265 g_return_if_fail (x < width || y < height);
3267 hv = priv->hadjust->value;
3268 vv = priv->vadjust->value;
3271 gdouble jump_to = x - priv->hadjust->page_size/2;
3273 priv->hadjust->value = CLAMP (jump_to,
3274 priv->hadjust->lower,
3275 priv->hadjust->upper -
3276 priv->hadjust->page_size);
3280 gdouble jump_to = y - priv->vadjust->page_size/2;
3282 priv->vadjust->value = CLAMP (jump_to,
3283 priv->vadjust->lower,
3284 priv->vadjust->upper -
3285 priv->vadjust->page_size);
3288 if (hv != priv->hadjust->value)
3289 gtk_adjustment_value_changed (priv->hadjust);
3291 if (vv != priv->vadjust->value)
3292 gtk_adjustment_value_changed (priv->vadjust);
3294 priv->scroll_indicator_alpha = 1.0;
3296 if (priv->scroll_indicator_timeout) {
3297 g_source_remove (priv->scroll_indicator_timeout);
3298 priv->scroll_indicator_timeout = 0;
3301 if (priv->idle_id) {
3304 priv->overshooting_x = 0;
3305 priv->overshooting_y = 0;
3307 if ((priv->overshot_dist_x>0)||(priv->overshot_dist_y>0)) {
3308 priv->overshot_dist_x = 0;
3309 priv->overshot_dist_y = 0;
3311 gtk_widget_queue_resize (GTK_WIDGET (area));
3314 g_signal_emit (area, pannable_area_signals[PANNING_FINISHED], 0);
3315 g_source_remove (priv->idle_id);
3321 * hildon_pannable_area_scroll_to_child:
3322 * @area: A #HildonPannableArea.
3323 * @child: A #GtkWidget, descendant of @area.
3325 * Smoothly scrolls until @child is visible inside @area. @child must
3326 * be a descendant of @area. If you need to scroll inside a scrollable
3327 * widget, e.g., #GtkTreeview, see hildon_pannable_area_scroll_to().
3329 * There is a precondition to this function: the widget must be
3330 * already realized. Check the hildon_pannable_area_jump_to_child() for
3331 * more tips regarding how to call this function during
3337 hildon_pannable_area_scroll_to_child (HildonPannableArea *area, GtkWidget *child)
3339 GtkWidget *bin_child;
3342 g_return_if_fail (GTK_WIDGET_REALIZED (area));
3343 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3344 g_return_if_fail (GTK_IS_WIDGET (child));
3345 g_return_if_fail (gtk_widget_is_ancestor (child, GTK_WIDGET (area)));
3347 if (GTK_BIN (area)->child == NULL)
3350 /* We need to get to check the child of the inside the area */
3351 bin_child = GTK_BIN (area)->child;
3353 /* we check if we added a viewport */
3354 if (GTK_IS_VIEWPORT (bin_child)) {
3355 bin_child = GTK_BIN (bin_child)->child;
3358 if (gtk_widget_translate_coordinates (child, bin_child, 0, 0, &x, &y))
3359 hildon_pannable_area_scroll_to (area, x, y);
3363 * hildon_pannable_area_jump_to_child:
3364 * @area: A #HildonPannableArea.
3365 * @child: A #GtkWidget, descendant of @area.
3367 * Jumps to make sure @child is visible inside @area. @child must
3368 * be a descendant of @area. If you want to move inside a scrollable
3369 * widget, like, #GtkTreeview, see hildon_pannable_area_scroll_to().
3371 * There is a precondition to this function: the widget must be
3372 * already realized. You can control if the widget is ready with the
3373 * GTK_WIDGET_REALIZED macro. If you want to call this function during
3374 * the initialization process of the widget do it inside a callback to
3375 * the ::realize signal, using g_signal_connect_after() function.
3380 hildon_pannable_area_jump_to_child (HildonPannableArea *area, GtkWidget *child)
3382 GtkWidget *bin_child;
3385 g_return_if_fail (GTK_WIDGET_REALIZED (area));
3386 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3387 g_return_if_fail (GTK_IS_WIDGET (child));
3388 g_return_if_fail (gtk_widget_is_ancestor (child, GTK_WIDGET (area)));
3390 if (gtk_bin_get_child (GTK_BIN (area)) == NULL)
3393 /* We need to get to check the child of the inside the area */
3394 bin_child = gtk_bin_get_child (GTK_BIN (area));
3396 /* we check if we added a viewport */
3397 if (GTK_IS_VIEWPORT (bin_child)) {
3398 bin_child = gtk_bin_get_child (GTK_BIN (bin_child));
3401 if (gtk_widget_translate_coordinates (child, bin_child, 0, 0, &x, &y))
3402 hildon_pannable_area_jump_to (area, x, y);
3406 * hildon_pannable_get_child_widget_at:
3407 * @area: A #HildonPannableArea.
3408 * @x: horizontal coordinate of the point
3409 * @y: vertical coordinate of the point
3411 * Get the widget at the point (x, y) inside the pannable area. In
3412 * case no widget found it returns NULL.
3414 * returns: the #GtkWidget if we find a widget, NULL in any other case
3419 hildon_pannable_get_child_widget_at (HildonPannableArea *area,
3420 gdouble x, gdouble y)
3422 GdkWindow *window = NULL;
3423 GtkWidget *child_widget = NULL;
3425 window = hildon_pannable_area_get_topmost
3426 (gtk_bin_get_child (GTK_BIN (area))->window,
3427 x, y, NULL, NULL, GDK_ALL_EVENTS_MASK);
3429 gdk_window_get_user_data (window, (gpointer) &child_widget);
3431 return child_widget;
3436 * hildon_pannable_area_get_hadjustment:
3437 * @area: A #HildonPannableArea.
3439 * Returns the horizontal adjustment. This adjustment is the internal
3440 * widget adjustment used to control the animations. Do not modify it
3441 * directly to change the position of the pannable, to do that use the
3442 * pannable API. If you modify the object directly it could cause
3443 * artifacts in the animations.
3445 * returns: The horizontal #GtkAdjustment
3450 hildon_pannable_area_get_hadjustment (HildonPannableArea *area)
3453 g_return_val_if_fail (HILDON_IS_PANNABLE_AREA (area), NULL);
3455 return area->priv->hadjust;
3459 * hildon_pannable_area_get_vadjustment:
3460 * @area: A #HildonPannableArea.
3462 * Returns the vertical adjustment. This adjustment is the internal
3463 * widget adjustment used to control the animations. Do not modify it
3464 * directly to change the position of the pannable, to do that use the
3465 * pannable API. If you modify the object directly it could cause
3466 * artifacts in the animations.
3468 * returns: The vertical #GtkAdjustment
3473 hildon_pannable_area_get_vadjustment (HildonPannableArea *area)
3475 g_return_val_if_fail (HILDON_IS_PANNABLE_AREA (area), NULL);
3477 return area->priv->vadjust;
3482 * hildon_pannable_area_get_size_request_policy:
3483 * @area: A #HildonPannableArea.
3485 * This function returns the current size request policy of the
3486 * widget. That policy controls the way the size_request is done in
3487 * the pannable area. Check
3488 * hildon_pannable_area_set_size_request_policy() for a more detailed
3491 * returns: the policy is currently being used in the widget
3492 * #HildonSizeRequestPolicy.
3496 HildonSizeRequestPolicy
3497 hildon_pannable_area_get_size_request_policy (HildonPannableArea *area)
3499 HildonPannableAreaPrivate *priv;
3501 g_return_val_if_fail (HILDON_IS_PANNABLE_AREA (area), FALSE);
3505 return priv->size_request_policy;
3509 * hildon_pannable_area_set_size_request_policy:
3510 * @area: A #HildonPannableArea.
3511 * @size_request_policy: One of the allowed #HildonSizeRequestPolicy
3513 * This function sets the pannable area size request policy. That
3514 * policy controls the way the size_request is done in the pannable
3515 * area. Pannable can use the size request of its children
3516 * (#HILDON_SIZE_REQUEST_CHILDREN) or the minimum size required for
3517 * the area itself (#HILDON_SIZE_REQUEST_MINIMUM), the latter is the
3518 * default. Recall this size depends on the scrolling policy you are
3519 * requesting to the pannable area, if you set #GTK_POLICY_NEVER this
3520 * parameter will not have any effect with
3521 * #HILDON_SIZE_REQUEST_MINIMUM set.
3525 * Deprecated: This method and the policy request is deprecated, DO
3526 * NOT use it in future code, the only policy properly supported in
3527 * gtk+ nowadays is the minimum size. Use #gtk_window_set_default_size
3528 * or #gtk_window_set_geometry_hints with the proper size in your case
3529 * to define the height of your dialogs.
3532 hildon_pannable_area_set_size_request_policy (HildonPannableArea *area,
3533 HildonSizeRequestPolicy size_request_policy)
3535 HildonPannableAreaPrivate *priv;
3537 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3541 if (priv->size_request_policy == size_request_policy)
3544 priv->size_request_policy = size_request_policy;
3546 gtk_widget_queue_resize (GTK_WIDGET (area));
3548 g_object_notify (G_OBJECT (area), "size-request-policy");
3552 * hildon_pannable_area_get_center_on_child_focus
3553 * @area: A #HildonPannableArea
3555 * Gets the @area #HildonPannableArea:center-on-child-focus property
3558 * See #HildonPannableArea:center-on-child-focus for more information.
3560 * Returns: the @area #HildonPannableArea:center-on-child-focus value
3565 hildon_pannable_area_get_center_on_child_focus (HildonPannableArea *area)
3567 g_return_val_if_fail (HILDON_IS_PANNABLE_AREA (area), FALSE);
3569 return area->priv->center_on_child_focus;
3573 * hildon_pannable_area_set_center_on_child_focus
3574 * @area: A #HildonPannableArea
3575 * @value: the new value
3577 * Sets the @area #HildonPannableArea:center-on-child-focus property
3580 * See #HildonPannableArea:center-on-child-focus for more information.
3585 hildon_pannable_area_set_center_on_child_focus (HildonPannableArea *area,
3588 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3590 area->priv->center_on_child_focus = value;