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_full (G_PRIORITY_HIGH_IDLE + 20,
1620 SCROLL_FADE_TIMEOUT,
1621 (GSourceFunc) hildon_pannable_area_scroll_indicator_fade,
1627 hildon_pannable_area_adjust_changed (HildonPannableArea * area,
1630 if (GTK_WIDGET_REALIZED (area))
1631 hildon_pannable_area_refresh (area);
1635 hildon_pannable_area_adjust_value_changed (HildonPannableArea * area,
1638 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (area)->priv;
1640 gint x = priv->x_offset;
1641 gint y = priv->y_offset;
1643 priv->x_offset = priv->hadjust->value;
1644 xdiff = x - priv->x_offset;
1645 priv->y_offset = priv->vadjust->value;
1646 ydiff = y - priv->y_offset;
1648 if ((xdiff || ydiff) && GTK_WIDGET_DRAWABLE (area)) {
1649 hildon_pannable_area_redraw (area);
1651 if ((priv->vscroll_visible) || (priv->hscroll_visible)) {
1652 priv->scroll_indicator_event_interrupt = 0;
1653 priv->scroll_delay_counter = priv->scrollbar_fade_delay;
1655 hildon_pannable_area_launch_fade_timeout (area, 1.0);
1661 hildon_pannable_area_redraw (HildonPannableArea * area)
1663 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (area)->priv;
1665 /* Redraw scroll indicators */
1666 if (GTK_WIDGET_DRAWABLE (area)) {
1667 if (priv->hscroll_visible) {
1668 gdk_window_invalidate_rect (GTK_WIDGET (area)->window,
1669 &priv->hscroll_rect, FALSE);
1672 if (priv->vscroll_visible) {
1673 gdk_window_invalidate_rect (GTK_WIDGET (area)->window,
1674 &priv->vscroll_rect, FALSE);
1680 hildon_pannable_area_scroll_indicator_fade(HildonPannableArea * area)
1682 HildonPannableAreaPrivate *priv = area->priv;
1684 /* if moving do not fade out */
1685 if (((ABS (priv->vel_y)>priv->vmin)||
1686 (ABS (priv->vel_x)>priv->vmin))&&(!priv->button_pressed)) {
1691 if (priv->scroll_indicator_event_interrupt) {
1692 /* Stop a fade out, and fade back in */
1693 if (priv->scroll_indicator_alpha > 0.9) {
1694 priv->scroll_indicator_alpha = 1.0;
1695 priv->scroll_indicator_timeout = 0;
1699 priv->scroll_indicator_alpha += 0.2;
1700 hildon_pannable_area_redraw (area);
1706 if ((priv->scroll_indicator_alpha > 0.9) &&
1707 (priv->scroll_delay_counter > 0)) {
1708 priv->scroll_delay_counter--;
1713 if (!priv->scroll_indicator_event_interrupt) {
1714 /* Continue fade out */
1715 if (priv->scroll_indicator_alpha < 0.1) {
1716 priv->scroll_indicator_timeout = 0;
1717 priv->scroll_indicator_alpha = 0.0;
1721 priv->scroll_indicator_alpha -= 0.2;
1722 hildon_pannable_area_redraw (area);
1732 hildon_pannable_area_expose_event (GtkWidget * widget,
1733 GdkEventExpose * event)
1736 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1737 #if USE_CAIRO_SCROLLBARS == 1
1738 GdkColor back_color = widget->style->bg[GTK_STATE_NORMAL];
1739 GdkColor scroll_color = widget->style->base[GTK_STATE_SELECTED];
1740 #else /* USE_CAIRO_SCROLLBARS */
1741 GdkColor back_color = widget->style->bg[GTK_STATE_NORMAL];
1742 GdkColor scroll_color = priv->scroll_color;
1745 if (G_UNLIKELY (priv->initial_effect)) {
1746 hildon_pannable_area_initial_effect (widget);
1748 priv->initial_effect = FALSE;
1751 if (gtk_bin_get_child (GTK_BIN (widget))) {
1753 if (priv->scroll_indicator_alpha > 0.1) {
1754 if (priv->vscroll_visible) {
1755 hildon_pannable_draw_vscroll (widget, &back_color, &scroll_color);
1757 if (priv->hscroll_visible) {
1758 hildon_pannable_draw_hscroll (widget, &back_color, &scroll_color);
1762 /* draw overshooting rectangles */
1763 if (priv->overshot_dist_y > 0) {
1764 gint overshot_height;
1766 overshot_height = MIN (priv->overshot_dist_y, widget->allocation.height -
1767 (priv->hscroll_visible ? priv->hscroll_rect.height : 0));
1769 gdk_draw_rectangle (widget->window,
1770 widget->style->bg_gc[GTK_STATE_NORMAL],
1774 widget->allocation.width -
1775 (priv->vscroll_visible ? priv->vscroll_rect.width : 0),
1777 } else if (priv->overshot_dist_y < 0) {
1778 gint overshot_height;
1782 MAX (priv->overshot_dist_y,
1783 -(widget->allocation.height -
1784 (priv->hscroll_visible ? priv->hscroll_rect.height : 0)));
1786 overshot_y = MAX (widget->allocation.height +
1788 (priv->hscroll_visible ? priv->hscroll_rect.height : 0), 0);
1790 gdk_draw_rectangle (widget->window,
1791 widget->style->bg_gc[GTK_STATE_NORMAL],
1795 widget->allocation.width -
1796 priv->vscroll_rect.width,
1800 if (priv->overshot_dist_x > 0) {
1801 gint overshot_width;
1803 overshot_width = MIN (priv->overshot_dist_x, widget->allocation.width -
1804 (priv->vscroll_visible ? priv->vscroll_rect.width : 0));
1806 gdk_draw_rectangle (widget->window,
1807 widget->style->bg_gc[GTK_STATE_NORMAL],
1812 widget->allocation.height -
1813 (priv->hscroll_visible ? priv->hscroll_rect.height : 0));
1814 } else if (priv->overshot_dist_x < 0) {
1815 gint overshot_width;
1819 MAX (priv->overshot_dist_x,
1820 -(widget->allocation.width -
1821 (priv->vscroll_visible ? priv->vscroll_rect.width : 0)));
1823 overshot_x = MAX (widget->allocation.width +
1825 (priv->vscroll_visible ? priv->vscroll_rect.width : 0), 0);
1827 gdk_draw_rectangle (widget->window,
1828 widget->style->bg_gc[GTK_STATE_NORMAL],
1833 widget->allocation.height -
1834 priv->hscroll_rect.height);
1839 return GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->expose_event (widget, event);
1843 hildon_pannable_area_get_topmost (GdkWindow * window,
1845 gint * tx, gint * ty,
1848 /* Find the GdkWindow at the given point, by recursing from a given
1849 * parent GdkWindow. Optionally return the co-ordinates transformed
1850 * relative to the child window.
1853 GList *c, *children;
1854 GdkWindow *selected_window = NULL;
1856 gdk_drawable_get_size (GDK_DRAWABLE (window), &width, &height);
1857 if ((x < 0) || (x >= width) || (y < 0) || (y >= height))
1860 children = gdk_window_peek_children (window);
1867 selected_window = window;
1870 for (c = children; c; c = c->next) {
1871 GdkWindow *child = (GdkWindow *) c->data;
1874 gdk_drawable_get_size (GDK_DRAWABLE (child), &width, &height);
1875 gdk_window_get_position (child, &wx, &wy);
1877 if ((x >= wx) && (x < (wx + width)) && (y >= wy) && (y < (wy + height)) &&
1878 (gdk_window_is_visible (child))) {
1880 if (gdk_window_peek_children (child)) {
1881 selected_window = hildon_pannable_area_get_topmost (child, x-wx, y-wy,
1883 if (!selected_window) {
1888 selected_window = child;
1891 if ((gdk_window_get_events (child)&mask)) {
1896 selected_window = child;
1902 return selected_window;
1906 synth_crossing (GdkWindow * child,
1908 gint x_root, gint y_root,
1909 guint32 time, gboolean in)
1911 GdkEventCrossing *crossing_event;
1912 GdkEventType type = in ? GDK_ENTER_NOTIFY : GDK_LEAVE_NOTIFY;
1914 /* Send synthetic enter event */
1915 crossing_event = (GdkEventCrossing *) gdk_event_new (type);
1916 ((GdkEventAny *) crossing_event)->type = type;
1917 ((GdkEventAny *) crossing_event)->window = g_object_ref (child);
1918 ((GdkEventAny *) crossing_event)->send_event = FALSE;
1919 crossing_event->subwindow = g_object_ref (child);
1920 crossing_event->time = time;
1921 crossing_event->x = x;
1922 crossing_event->y = y;
1923 crossing_event->x_root = x_root;
1924 crossing_event->y_root = y_root;
1925 crossing_event->mode = GDK_CROSSING_NORMAL;
1926 crossing_event->detail = GDK_NOTIFY_UNKNOWN;
1927 crossing_event->focus = FALSE;
1928 crossing_event->state = 0;
1929 gdk_event_put ((GdkEvent *) crossing_event);
1930 gdk_event_free ((GdkEvent *) crossing_event);
1934 hildon_pannable_area_button_press_cb (GtkWidget * widget,
1935 GdkEventButton * event)
1938 HildonPannableArea *area = HILDON_PANNABLE_AREA (widget);
1939 HildonPannableAreaPrivate *priv = area->priv;
1941 if ((!priv->enabled) || (event->button != 1) ||
1942 ((event->time == priv->last_time) &&
1943 (priv->last_type == 1)) || (gtk_bin_get_child (GTK_BIN (widget)) == NULL))
1946 priv->scroll_indicator_event_interrupt = 1;
1948 hildon_pannable_area_launch_fade_timeout (area,
1949 priv->scroll_indicator_alpha);
1951 priv->last_time = event->time;
1952 priv->last_type = 1;
1954 priv->scroll_to_x = -1;
1955 priv->scroll_to_y = -1;
1957 if (priv->button_pressed && priv->child) {
1958 /* Widget stole focus on last click, send crossing-out event */
1959 synth_crossing (priv->child, 0, 0, event->x_root, event->y_root,
1960 event->time, FALSE);
1968 /* Don't allow a click if we're still moving fast */
1969 if ((ABS (priv->vel_x) <= (priv->vmax * priv->vfast_factor)) &&
1970 (ABS (priv->vel_y) <= (priv->vmax * priv->vfast_factor)))
1972 hildon_pannable_area_get_topmost (gtk_bin_get_child (GTK_BIN (widget))->window,
1973 event->x, event->y, &x, &y, GDK_BUTTON_PRESS_MASK);
1977 priv->button_pressed = TRUE;
1979 /* Stop scrolling on mouse-down (so you can flick, then hold to stop) */
1982 if (priv->idle_id) {
1983 g_source_remove (priv->idle_id);
1985 g_signal_emit (area, pannable_area_signals[PANNING_FINISHED], 0);
1990 gdk_drawable_get_size (priv->child, &priv->child_width,
1991 &priv->child_height);
1992 priv->last_in = TRUE;
1994 g_object_add_weak_pointer ((GObject *) priv->child,
1995 (gpointer) & priv->child);
1997 event = (GdkEventButton *) gdk_event_copy ((GdkEvent *) event);
1998 /* remove the reference we added with the copy */
1999 g_object_unref (priv->event_window);
2005 synth_crossing (priv->child, x, y, event->x_root,
2006 event->y_root, event->time, TRUE);
2008 /* Send synthetic click (button press/release) event */
2009 ((GdkEventAny *) event)->window = g_object_ref (priv->child);
2011 gdk_event_put ((GdkEvent *) event);
2012 gdk_event_free ((GdkEvent *) event);
2020 hildon_pannable_area_check_scrollbars (HildonPannableArea * area)
2022 HildonPannableAreaPrivate *priv = area->priv;
2023 gboolean prev_hscroll_visible, prev_vscroll_visible;
2025 prev_hscroll_visible = priv->hscroll_visible;
2026 prev_vscroll_visible = priv->vscroll_visible;
2028 if (!gtk_bin_get_child (GTK_BIN (area))) {
2029 priv->vscroll_visible = FALSE;
2030 priv->hscroll_visible = FALSE;
2032 switch (priv->hscrollbar_policy) {
2033 case GTK_POLICY_ALWAYS:
2034 priv->hscroll_visible = TRUE;
2036 case GTK_POLICY_NEVER:
2037 priv->hscroll_visible = FALSE;
2040 priv->hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
2041 priv->hadjust->page_size);
2044 switch (priv->vscrollbar_policy) {
2045 case GTK_POLICY_ALWAYS:
2046 priv->vscroll_visible = TRUE;
2048 case GTK_POLICY_NEVER:
2049 priv->vscroll_visible = FALSE;
2052 priv->vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
2053 priv->vadjust->page_size);
2056 /* Store the vscroll/hscroll areas for redrawing */
2057 if (priv->vscroll_visible) {
2058 GtkAllocation *allocation = >K_WIDGET (area)->allocation;
2059 priv->vscroll_rect.x = allocation->width - priv->indicator_width;
2060 priv->vscroll_rect.y = 0;
2061 priv->vscroll_rect.width = priv->indicator_width;
2062 priv->vscroll_rect.height = allocation->height -
2063 (priv->hscroll_visible ? priv->indicator_width : 0);
2065 if (priv->hscroll_visible) {
2066 GtkAllocation *allocation = >K_WIDGET (area)->allocation;
2067 priv->hscroll_rect.y = allocation->height - priv->indicator_width;
2068 priv->hscroll_rect.x = 0;
2069 priv->hscroll_rect.height = priv->indicator_width;
2070 priv->hscroll_rect.width = allocation->width -
2071 (priv->vscroll_visible ? priv->indicator_width : 0);
2075 return ((priv->hscroll_visible != prev_hscroll_visible) ||
2076 (priv->vscroll_visible != prev_vscroll_visible));
2080 hildon_pannable_area_refresh (HildonPannableArea * area)
2082 if (GTK_WIDGET_DRAWABLE (area) &&
2083 hildon_pannable_area_check_scrollbars (area)) {
2084 HildonPannableAreaPrivate *priv = area->priv;
2086 gtk_widget_queue_resize (GTK_WIDGET (area));
2088 if ((priv->vscroll_visible) || (priv->hscroll_visible)) {
2089 priv->scroll_indicator_event_interrupt = 0;
2090 priv->scroll_delay_counter = area->priv->scrollbar_fade_delay;
2092 hildon_pannable_area_launch_fade_timeout (area, 1.0);
2095 hildon_pannable_area_redraw (area);
2099 /* Scroll by a particular amount (in pixels). Optionally, return if
2100 * the scroll on a particular axis was successful.
2103 hildon_pannable_axis_scroll (HildonPannableArea *area,
2104 GtkAdjustment *adjust,
2108 gint *overshot_dist,
2114 HildonPannableAreaPrivate *priv = area->priv;
2116 dist = gtk_adjustment_get_value (adjust) - inc;
2119 * We use overshot_dist to define the distance of the current overshoot,
2120 * and overshooting to define the direction/whether or not we are overshot
2122 if (!(*overshooting)) {
2124 /* Initiation of the overshoot happens when the finger is released
2125 * and the current position of the pannable contents are out of range
2127 if (dist < adjust->lower) {
2130 dist = adjust->lower;
2132 if (overshoot_max!=0) {
2135 *overshot_dist = CLAMP (*overshot_dist + *vel, 0, overshoot_max);
2136 *vel = MIN (priv->vmax_overshooting, *vel);
2137 gtk_widget_queue_resize (GTK_WIDGET (area));
2141 } else if (dist > adjust->upper - adjust->page_size) {
2144 dist = adjust->upper - adjust->page_size;
2146 if (overshoot_max!=0) {
2149 *overshot_dist = CLAMP (*overshot_dist + *vel, -overshoot_max, 0);
2150 *vel = MAX (-priv->vmax_overshooting, *vel);
2151 gtk_widget_queue_resize (GTK_WIDGET (area));
2156 if ((*scroll_to) != -1) {
2157 if (((inc < 0)&&(*scroll_to <= dist))||
2158 ((inc > 0)&&(*scroll_to >= dist))) {
2166 adjust->value = dist;
2168 if (!priv->button_pressed) {
2170 /* When the overshoot has started we continue for
2171 * PROP_BOUNCE_STEPS more steps into the overshoot before we
2172 * reverse direction. The deceleration factor is calculated
2173 * based on the percentage distance from the first item with
2174 * each iteration, therefore always returning us to the
2175 * top/bottom most element
2177 if (*overshot_dist > 0) {
2179 if ((*overshooting < priv->bounce_steps) && (*vel > 0)) {
2181 *vel = (((gdouble)*overshot_dist)/overshoot_max) * (*vel);
2182 } else if ((*overshooting >= priv->bounce_steps) && (*vel > 0)) {
2184 } else if ((*overshooting > 1) && (*vel < 0)) {
2185 /* we add the MIN in order to avoid very small speeds */
2186 *vel = MIN (((((gdouble)*overshot_dist)*0.8) * -1), -10.0);
2189 *overshot_dist = CLAMP (*overshot_dist + *vel, 0, overshoot_max);
2191 gtk_widget_queue_resize (GTK_WIDGET (area));
2193 } else if (*overshot_dist < 0) {
2195 if ((*overshooting < priv->bounce_steps) && (*vel < 0)) {
2197 *vel = (((gdouble)*overshot_dist)/overshoot_max) * (*vel) * -1;
2198 } else if ((*overshooting >= priv->bounce_steps) && (*vel < 0)) {
2200 } else if ((*overshooting > 1) && (*vel > 0)) {
2201 /* we add the MAX in order to avoid very small speeds */
2202 *vel = MAX (((((gdouble)*overshot_dist)*0.8) * -1), 10.0);
2205 *overshot_dist = CLAMP (*overshot_dist + (*vel), -overshoot_max, 0);
2207 gtk_widget_queue_resize (GTK_WIDGET (area));
2212 gtk_widget_queue_resize (GTK_WIDGET (area));
2216 gint overshot_dist_old = *overshot_dist;
2218 if (*overshot_dist > 0) {
2219 *overshot_dist = CLAMP ((*overshot_dist) + inc, 0, overshoot_max);
2220 } else if (*overshot_dist < 0) {
2221 *overshot_dist = CLAMP ((*overshot_dist) + inc, -1 * overshoot_max, 0);
2224 adjust->value = CLAMP (dist,
2230 if (*overshot_dist != overshot_dist_old)
2231 gtk_widget_queue_resize (GTK_WIDGET (area));
2237 hildon_pannable_area_scroll (HildonPannableArea *area,
2238 gdouble x, gdouble y)
2241 HildonPannableAreaPrivate *priv = area->priv;
2242 gboolean hscroll_visible, vscroll_visible;
2245 if (gtk_bin_get_child (GTK_BIN (area)) == NULL)
2248 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
2249 priv->vadjust->page_size);
2250 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
2251 priv->hadjust->page_size);
2256 hv = priv->hadjust->value;
2257 vv = priv->vadjust->value;
2259 if (vscroll_visible) {
2260 hildon_pannable_axis_scroll (area, priv->vadjust, &priv->vel_y, y,
2261 &priv->overshooting_y, &priv->overshot_dist_y,
2262 &priv->scroll_to_y, priv->vovershoot_max, &sy);
2267 if (hscroll_visible) {
2268 hildon_pannable_axis_scroll (area, priv->hadjust, &priv->vel_x, x,
2269 &priv->overshooting_x, &priv->overshot_dist_x,
2270 &priv->scroll_to_x, priv->hovershoot_max, &sx);
2275 if (hv != priv->hadjust->value)
2276 gtk_adjustment_value_changed (priv->hadjust);
2278 if (vv != priv->vadjust->value)
2279 gtk_adjustment_value_changed (priv->vadjust);
2281 /* If the scroll on a particular axis wasn't succesful, reset the
2282 * initial scroll position to the new mouse co-ordinate. This means
2283 * when you get to the top of the page, dragging down works immediately.
2285 if (priv->mode == HILDON_PANNABLE_AREA_MODE_ACCEL) {
2297 hildon_pannable_area_timeout (HildonPannableArea * area)
2299 HildonPannableAreaPrivate *priv = area->priv;
2301 if ((!priv->enabled) || (priv->mode == HILDON_PANNABLE_AREA_MODE_PUSH)) {
2303 g_signal_emit (area, pannable_area_signals[PANNING_FINISHED], 0);
2308 if (!priv->button_pressed) {
2309 /* Decelerate gradually when pointer is raised */
2310 if ((!priv->overshot_dist_y) &&
2311 (!priv->overshot_dist_x)) {
2313 /* in case we move to a specific point do not decelerate when arriving */
2314 if ((priv->scroll_to_x != -1)||(priv->scroll_to_y != -1)) {
2316 if (ABS (priv->vel_x) >= 1.5) {
2317 priv->vel_x *= priv->decel;
2320 if (ABS (priv->vel_y) >= 1.5) {
2321 priv->vel_y *= priv->decel;
2325 if ((!priv->low_friction_mode) ||
2326 ((priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ) &&
2327 (ABS (priv->vel_x) < 0.8*priv->vmax)))
2328 priv->vel_x *= priv->decel;
2330 if ((!priv->low_friction_mode) ||
2331 ((priv->mov_mode&HILDON_MOVEMENT_MODE_VERT) &&
2332 (ABS (priv->vel_y) < 0.8*priv->vmax)))
2333 priv->vel_y *= priv->decel;
2335 if ((ABS (priv->vel_x) < 1.0) && (ABS (priv->vel_y) < 1.0)) {
2340 g_signal_emit (area, pannable_area_signals[PANNING_FINISHED], 0);
2346 } else if (priv->mode == HILDON_PANNABLE_AREA_MODE_AUTO) {
2352 hildon_pannable_area_scroll (area, priv->vel_x, priv->vel_y);
2354 gdk_window_process_updates (GTK_WIDGET (area)->window, FALSE);
2360 hildon_pannable_area_calculate_velocity (gdouble *vel,
2364 gdouble drag_inertia,
2370 if (ABS (dist) >= RATIO_TOLERANCE) {
2371 rawvel = (dist / ABS (delta)) * force;
2372 *vel = *vel * (1 - drag_inertia) +
2373 rawvel * drag_inertia;
2374 *vel = *vel > 0 ? MIN (*vel, vmax)
2375 : MAX (*vel, -1 * vmax);
2380 hildon_pannable_area_motion_event_scroll_timeout (HildonPannableArea *area)
2382 HildonPannableAreaPrivate *priv = area->priv;
2384 if ((priv->motion_x != 0)||(priv->motion_y != 0))
2385 hildon_pannable_area_scroll (area, priv->motion_x, priv->motion_y);
2387 priv->motion_event_scroll_timeout = 0;
2393 hildon_pannable_area_motion_event_scroll (HildonPannableArea *area,
2394 gdouble x, gdouble y)
2396 HildonPannableAreaPrivate *priv = area->priv;
2398 if (priv->motion_event_scroll_timeout) {
2400 priv->motion_x += x;
2401 priv->motion_y += y;
2405 /* we do not delay the first event but the next ones */
2406 hildon_pannable_area_scroll (area, x, y);
2411 priv->motion_event_scroll_timeout = gdk_threads_add_timeout_full
2412 (G_PRIORITY_HIGH_IDLE + 20,
2413 (gint) (1000.0 / (gdouble) MOTION_EVENTS_PER_SECOND),
2414 (GSourceFunc) hildon_pannable_area_motion_event_scroll_timeout, area, NULL);
2419 hildon_pannable_area_check_move (HildonPannableArea *area,
2420 GdkEventMotion * event,
2424 HildonPannableAreaPrivate *priv = area->priv;
2426 if (priv->first_drag && (!priv->moved) &&
2427 ((ABS (*x) > (priv->panning_threshold))
2428 || (ABS (*y) > (priv->panning_threshold)))) {
2433 if (priv->first_drag) {
2434 gboolean vscroll_visible;
2435 gboolean hscroll_visible;
2437 if (ABS (priv->iy - event->y) >=
2438 ABS (priv->ix - event->x)) {
2440 g_signal_emit (area,
2441 pannable_area_signals[VERTICAL_MOVEMENT],
2442 0, (priv->iy > event->y) ?
2443 HILDON_MOVEMENT_UP :
2444 HILDON_MOVEMENT_DOWN,
2445 (gdouble)priv->ix, (gdouble)priv->iy);
2447 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
2448 priv->vadjust->page_size);
2450 if (!((vscroll_visible)&&
2451 (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT))) {
2453 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
2454 priv->hadjust->page_size);
2456 /* even in case we do not have to move we check if this
2457 could be a fake horizontal movement */
2458 if (!((hscroll_visible)&&
2459 (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ)) ||
2460 (ABS (priv->iy - event->y) -
2461 ABS (priv->ix - event->x) >= priv->direction_error_margin))
2462 priv->moved = FALSE;
2466 g_signal_emit (area,
2467 pannable_area_signals[HORIZONTAL_MOVEMENT],
2468 0, (priv->ix > event->x) ?
2469 HILDON_MOVEMENT_LEFT :
2470 HILDON_MOVEMENT_RIGHT,
2471 (gdouble)priv->ix, (gdouble)priv->iy);
2473 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
2474 priv->hadjust->page_size);
2476 if (!((hscroll_visible)&&
2477 (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ))) {
2479 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
2480 priv->vadjust->page_size);
2482 /* even in case we do not have to move we check if this
2483 could be a fake vertical movement */
2484 if (!((vscroll_visible) &&
2485 (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT)) ||
2486 (ABS (priv->ix - event->x) -
2487 ABS (priv->iy - event->y) >= priv->direction_error_margin))
2488 priv->moved = FALSE;
2492 if ((priv->moved)&&(priv->child)) {
2495 pos_x = priv->cx + (event->x - priv->ix);
2496 pos_y = priv->cy + (event->y - priv->iy);
2498 synth_crossing (priv->child, pos_x, pos_y, event->x_root,
2499 event->y_root, event->time, FALSE);
2503 gboolean result_val;
2505 g_signal_emit (area,
2506 pannable_area_signals[PANNING_STARTED],
2509 priv->moved = !result_val;
2513 priv->first_drag = FALSE;
2515 if ((priv->mode != HILDON_PANNABLE_AREA_MODE_PUSH) &&
2516 (priv->mode != HILDON_PANNABLE_AREA_MODE_AUTO)) {
2519 priv->idle_id = gdk_threads_add_timeout_full
2520 (G_PRIORITY_HIGH_IDLE + 20,
2521 (gint)(1000.0 / (gdouble) priv->sps),
2523 hildon_pannable_area_timeout, area, NULL);
2529 hildon_pannable_area_handle_move (HildonPannableArea *area,
2530 GdkEventMotion * event,
2534 HildonPannableAreaPrivate *priv = area->priv;
2537 switch (priv->mode) {
2538 case HILDON_PANNABLE_AREA_MODE_PUSH:
2539 /* Scroll by the amount of pixels the cursor has moved
2540 * since the last motion event.
2542 hildon_pannable_area_motion_event_scroll (area, *x, *y);
2546 case HILDON_PANNABLE_AREA_MODE_ACCEL:
2547 /* Set acceleration relative to the initial click */
2548 priv->ex = event->x;
2549 priv->ey = event->y;
2550 priv->vel_x = ((*x > 0) ? 1 : -1) *
2552 (gdouble) GTK_WIDGET (area)->allocation.width) *
2553 (priv->vmax - priv->vmin)) + priv->vmin);
2554 priv->vel_y = ((*y > 0) ? 1 : -1) *
2556 (gdouble) GTK_WIDGET (area)->allocation.height) *
2557 (priv->vmax - priv->vmin)) + priv->vmin);
2559 case HILDON_PANNABLE_AREA_MODE_AUTO:
2561 delta = event->time - priv->last_time;
2563 if (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT) {
2564 gdouble dist = event->y - priv->y;
2566 hildon_pannable_area_calculate_velocity (&priv->vel_y,
2578 if (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ) {
2579 gdouble dist = event->x - priv->x;
2581 hildon_pannable_area_calculate_velocity (&priv->vel_x,
2593 hildon_pannable_area_motion_event_scroll (area, *x, *y);
2595 if (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ)
2597 if (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT)
2607 hildon_pannable_area_motion_notify_cb (GtkWidget * widget,
2608 GdkEventMotion * event)
2610 HildonPannableArea *area = HILDON_PANNABLE_AREA (widget);
2611 HildonPannableAreaPrivate *priv = area->priv;
2614 if (gtk_bin_get_child (GTK_BIN (widget)) == NULL)
2617 if ((!priv->enabled) || (!priv->button_pressed) ||
2618 ((event->time == priv->last_time) && (priv->last_type == 2))) {
2619 gdk_window_get_pointer (widget->window, NULL, NULL, 0);
2623 if (priv->last_type == 1) {
2624 priv->first_drag = TRUE;
2627 x = event->x - priv->x;
2628 y = event->y - priv->y;
2631 hildon_pannable_area_check_move (area, event, &x, &y);
2635 hildon_pannable_area_handle_move (area, event, &x, &y);
2636 } else if (priv->child) {
2640 pos_x = priv->cx + (event->x - priv->ix);
2641 pos_y = priv->cy + (event->y - priv->iy);
2643 in = (((0 <= pos_x)&&(priv->child_width >= pos_x)) &&
2644 ((0 <= pos_y)&&(priv->child_height >= pos_y)));
2646 if (((!priv->last_in)&&in)||((priv->last_in)&&(!in))) {
2648 synth_crossing (priv->child, pos_x, pos_y, event->x_root,
2649 event->y_root, event->time, in);
2655 priv->last_time = event->time;
2656 priv->last_type = 2;
2659 /* Send motion notify to child */
2660 event = (GdkEventMotion *) gdk_event_copy ((GdkEvent *) event);
2661 /* remove the reference we added with the copy */
2662 g_object_unref (priv->event_window);
2663 event->x = priv->cx + (event->x - priv->ix);
2664 event->y = priv->cy + (event->y - priv->iy);
2665 event->window = g_object_ref (priv->child);
2666 gdk_event_put ((GdkEvent *) event);
2667 gdk_event_free ((GdkEvent *) event);
2670 gdk_window_get_pointer (widget->window, NULL, NULL, 0);
2676 hildon_pannable_leave_notify_event (GtkWidget *widget,
2677 GdkEventCrossing *event)
2679 HildonPannableArea *area = HILDON_PANNABLE_AREA (widget);
2680 HildonPannableAreaPrivate *priv = area->priv;
2682 if ((priv->child)&&(priv->last_in)) {
2683 priv->last_in = FALSE;
2685 synth_crossing (priv->child, 0, 0, event->x_root,
2686 event->y_root, event->time, FALSE);
2693 hildon_pannable_area_button_release_cb (GtkWidget * widget,
2694 GdkEventButton * event)
2696 HildonPannableArea *area = HILDON_PANNABLE_AREA (widget);
2697 HildonPannableAreaPrivate *priv = area->priv;
2702 if (((event->time == priv->last_time) && (priv->last_type == 3))
2703 || (gtk_bin_get_child (GTK_BIN (widget)) == NULL)
2704 || (!priv->button_pressed) || (!priv->enabled) || (event->button != 1))
2707 /* if last event was a motion-notify we have to check the movement
2708 and launch the animation */
2709 if (priv->last_type == 2) {
2711 dx = event->x - priv->x;
2712 dy = event->y - priv->y;
2714 hildon_pannable_area_check_move (area, (GdkEventMotion *) event, &dx, &dy);
2717 gdouble delta = event->time - priv->last_time;
2719 hildon_pannable_area_handle_move (area, (GdkEventMotion *) event, &dx, &dy);
2721 /* move all the way to the last position now */
2722 if (priv->motion_event_scroll_timeout) {
2723 g_source_remove (priv->motion_event_scroll_timeout);
2724 hildon_pannable_area_motion_event_scroll_timeout (HILDON_PANNABLE_AREA (widget));
2729 if ((ABS (dx) < 4.0) && (delta >= CURSOR_STOPPED_TIMEOUT))
2732 if ((ABS (dy) < 4.0) && (delta >= CURSOR_STOPPED_TIMEOUT))
2737 /* If overshoot has been initiated with a finger down, on release set max speed */
2738 if (priv->overshot_dist_y != 0) {
2739 priv->overshooting_y = priv->bounce_steps; /* Hack to stop a bounce in the finger down case */
2740 priv->vel_y = priv->overshot_dist_y * 0.9;
2743 if (priv->overshot_dist_x != 0) {
2744 priv->overshooting_x = priv->bounce_steps; /* Hack to stop a bounce in the finger down case */
2745 priv->vel_x = priv->overshot_dist_x * 0.9;
2748 priv->button_pressed = FALSE;
2750 if ((ABS (priv->vel_y) >= priv->vmin) ||
2751 (ABS (priv->vel_x) >= priv->vmin)) {
2753 /* we have to move because we are in overshooting position*/
2755 gboolean result_val;
2757 g_signal_emit (area,
2758 pannable_area_signals[PANNING_STARTED],
2762 priv->scroll_indicator_alpha = 1.0;
2764 if (ABS (priv->vel_x) > MAX_SPEED_THRESHOLD)
2765 priv->vel_x = (priv->vel_x > 0) ? priv->vmax : -priv->vmax;
2767 if (ABS (priv->vel_y) > MAX_SPEED_THRESHOLD)
2768 priv->vel_y = (priv->vel_y > 0) ? priv->vmax : -priv->vmax;
2771 priv->idle_id = gdk_threads_add_timeout_full (G_PRIORITY_HIGH_IDLE + 20,
2772 (gint) (1000.0 / (gdouble) priv->sps),
2773 (GSourceFunc) hildon_pannable_area_timeout,
2776 if (priv->center_on_child_focus_pending) {
2777 hildon_pannable_area_center_on_child_focus (area);
2781 g_signal_emit (widget, pannable_area_signals[PANNING_FINISHED], 0);
2784 area->priv->center_on_child_focus_pending = FALSE;
2786 priv->scroll_indicator_event_interrupt = 0;
2787 priv->scroll_delay_counter = priv->scrollbar_fade_delay;
2789 hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget),
2790 priv->scroll_indicator_alpha);
2792 priv->last_time = event->time;
2793 priv->last_type = 3;
2796 priv->moved = FALSE;
2801 hildon_pannable_area_get_topmost (gtk_bin_get_child (GTK_BIN (widget))->window,
2802 event->x, event->y, &x, &y, GDK_BUTTON_RELEASE_MASK);
2804 event = (GdkEventButton *) gdk_event_copy ((GdkEvent *) event);
2805 /* remove the reference we added with the copy */
2806 g_object_unref (priv->event_window);
2810 /* Leave the widget if we've moved - This doesn't break selection,
2811 * but stops buttons from being clicked.
2813 if ((child != priv->child) || (priv->moved)) {
2814 /* Send synthetic leave event */
2815 synth_crossing (priv->child, x, y, event->x_root,
2816 event->y_root, event->time, FALSE);
2817 /* insure no click will happen for widgets that do not handle
2821 /* Send synthetic button release event */
2822 ((GdkEventAny *) event)->window = g_object_ref (priv->child);
2823 gdk_event_put ((GdkEvent *) event);
2825 /* Send synthetic button release event */
2826 ((GdkEventAny *) event)->window = g_object_ref (child);
2827 gdk_event_put ((GdkEvent *) event);
2828 /* Send synthetic leave event */
2829 synth_crossing (priv->child, x, y, event->x_root,
2830 event->y_root, event->time, FALSE);
2832 g_object_remove_weak_pointer ((GObject *) priv->child,
2833 (gpointer) & priv->child);
2835 priv->moved = FALSE;
2836 gdk_event_free ((GdkEvent *) event);
2841 /* utility event handler */
2843 hildon_pannable_area_scroll_cb (GtkWidget *widget,
2844 GdkEventScroll *event)
2846 GtkAdjustment *adj = NULL;
2847 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
2849 if ((!priv->enabled) ||
2850 (gtk_bin_get_child (GTK_BIN (widget)) == NULL))
2853 priv->scroll_indicator_event_interrupt = 0;
2854 priv->scroll_delay_counter = priv->scrollbar_fade_delay + 20;
2856 hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget), 1.0);
2858 /* Stop inertial scrolling */
2859 if (priv->idle_id) {
2862 priv->overshooting_x = 0;
2863 priv->overshooting_y = 0;
2865 if ((priv->overshot_dist_x>0)||(priv->overshot_dist_y>0)) {
2866 priv->overshot_dist_x = 0;
2867 priv->overshot_dist_y = 0;
2869 gtk_widget_queue_resize (GTK_WIDGET (widget));
2872 g_signal_emit (widget, pannable_area_signals[PANNING_FINISHED], 0);
2874 g_source_remove (priv->idle_id);
2878 if (event->direction == GDK_SCROLL_UP || event->direction == GDK_SCROLL_DOWN)
2879 adj = priv->vadjust;
2881 adj = priv->hadjust;
2885 gdouble delta, new_value;
2887 /* from gtkrange.c calculate delta*/
2888 delta = pow (adj->page_size, 2.0 / 3.0);
2890 if (event->direction == GDK_SCROLL_UP ||
2891 event->direction == GDK_SCROLL_LEFT)
2894 new_value = CLAMP (adj->value + delta, adj->lower, adj->upper - adj->page_size);
2896 gtk_adjustment_set_value (adj, new_value);
2903 hildon_pannable_area_child_mapped (GtkWidget *widget,
2907 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (user_data)->priv;
2909 if (priv->event_window != NULL && priv->enabled)
2910 gdk_window_raise (priv->event_window);
2914 hildon_pannable_area_add (GtkContainer *container, GtkWidget *child)
2916 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (container)->priv;
2918 g_return_if_fail (gtk_bin_get_child (GTK_BIN (container)) == NULL);
2920 gtk_widget_set_parent (child, GTK_WIDGET (container));
2921 GTK_BIN (container)->child = child;
2923 g_signal_connect_after (child, "map-event",
2924 G_CALLBACK (hildon_pannable_area_child_mapped),
2927 if (!gtk_widget_set_scroll_adjustments (child, priv->hadjust, priv->vadjust)) {
2928 g_warning ("%s: cannot add non scrollable widget, "
2929 "wrap it in a viewport", __FUNCTION__);
2933 /* call this function if you are not panning */
2935 hildon_pannable_area_center_on_child_focus (HildonPannableArea *area)
2937 GtkWidget *focused_child = NULL;
2938 GtkWidget *window = NULL;
2940 window = gtk_widget_get_toplevel (GTK_WIDGET (area));
2942 if (GTK_WIDGET_TOPLEVEL (window)) {
2943 focused_child = gtk_window_get_focus (GTK_WINDOW (window));
2946 if (focused_child) {
2947 hildon_pannable_area_scroll_to_child (area, focused_child);
2952 hildon_pannable_area_set_focus_child (GtkContainer *container,
2955 HildonPannableArea *area = HILDON_PANNABLE_AREA (container);
2957 if (!area->priv->center_on_child_focus) {
2961 if (GTK_IS_WIDGET (child)) {
2962 area->priv->center_on_child_focus_pending = TRUE;
2967 hildon_pannable_area_remove (GtkContainer *container, GtkWidget *child)
2969 g_return_if_fail (HILDON_IS_PANNABLE_AREA (container));
2970 g_return_if_fail (child != NULL);
2971 g_return_if_fail (gtk_bin_get_child (GTK_BIN (container)) == child);
2973 gtk_widget_set_scroll_adjustments (child, NULL, NULL);
2975 g_signal_handlers_disconnect_by_func (child,
2976 hildon_pannable_area_child_mapped,
2979 /* chain parent class handler to remove child */
2980 GTK_CONTAINER_CLASS (hildon_pannable_area_parent_class)->remove (container, child);
2984 * This method calculates a factor necessary to determine the initial distance
2985 * to jump in hildon_pannable_area_scroll_to(). For fixed time and frames per
2986 * second, we know in how many frames 'n' we need to reach the destination
2987 * point. We know that, for a distance d,
2989 * d = d_0 + d_1 + ... + d_n
2991 * where d_i is the distance travelled in the i-th frame and decel_factor is
2992 * the deceleration factor. This can be rewritten as
2994 * d = d_0 + (d_0 * decel_factor) + ... + (d_n-1 * decel_factor),
2996 * since the distance travelled on each frame is the distance travelled in the
2997 * previous frame reduced by the deceleration factor. Reducing this and
2998 * factoring d_0 out, we get
3000 * d = d_0 (1 + decel_factor + ... + decel_factor^(n-1)).
3002 * Since the sum is independent of the distance to be travelled, we can define
3005 * vel_factor = 1 + decel_factor + ... + decel_factor^(n-1).
3007 * That's the gem we calculate in this method.
3010 hildon_pannable_calculate_vel_factor (HildonPannableArea * self)
3012 HildonPannableAreaPrivate *priv = self->priv;
3017 n = ceil (priv->sps * priv->scroll_time);
3019 for (i = 1; i < n && fct_i >= RATIO_TOLERANCE; i++) {
3020 fct_i *= priv->decel;
3024 priv->vel_factor = fct;
3028 * hildon_pannable_area_new:
3030 * Create a new pannable area widget
3032 * Returns: the newly created #HildonPannableArea
3038 hildon_pannable_area_new (void)
3040 return g_object_new (HILDON_TYPE_PANNABLE_AREA, NULL);
3044 * hildon_pannable_area_new_full:
3045 * @mode: #HildonPannableAreaMode
3046 * @enabled: Value for the enabled property
3047 * @vel_min: Value for the velocity-min property
3048 * @vel_max: Value for the velocity-max property
3049 * @decel: Value for the deceleration property
3050 * @sps: Value for the sps property
3052 * Create a new #HildonPannableArea widget and set various properties
3054 * returns: the newly create #HildonPannableArea
3060 hildon_pannable_area_new_full (gint mode, gboolean enabled,
3061 gdouble vel_min, gdouble vel_max,
3062 gdouble decel, guint sps)
3064 return g_object_new (HILDON_TYPE_PANNABLE_AREA,
3067 "velocity_min", vel_min,
3068 "velocity_max", vel_max,
3069 "deceleration", decel, "sps", sps, NULL);
3073 * hildon_pannable_area_add_with_viewport:
3074 * @area: A #HildonPannableArea
3075 * @child: Child widget to add to the viewport
3077 * Convenience function used to add a child to a #GtkViewport, and add the
3078 * viewport to the scrolled window.
3084 hildon_pannable_area_add_with_viewport (HildonPannableArea * area,
3088 GtkWidget *viewport;
3090 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3091 g_return_if_fail (GTK_IS_WIDGET (child));
3092 g_return_if_fail (child->parent == NULL);
3094 bin = GTK_BIN (area);
3096 if (bin->child != NULL)
3098 g_return_if_fail (GTK_IS_VIEWPORT (bin->child));
3099 g_return_if_fail (GTK_BIN (bin->child)->child == NULL);
3101 viewport = bin->child;
3105 HildonPannableAreaPrivate *priv = area->priv;
3107 viewport = gtk_viewport_new (priv->hadjust,
3109 gtk_viewport_set_shadow_type (GTK_VIEWPORT (viewport), GTK_SHADOW_NONE);
3110 gtk_container_add (GTK_CONTAINER (area), viewport);
3113 gtk_widget_show (viewport);
3114 gtk_container_add (GTK_CONTAINER (viewport), child);
3118 * hildon_pannable_area_scroll_to:
3119 * @area: A #HildonPannableArea.
3120 * @x: The x coordinate of the destination point or -1 to ignore this axis.
3121 * @y: The y coordinate of the destination point or -1 to ignore this axis.
3123 * Smoothly scrolls @area to ensure that (@x, @y) is a visible point
3124 * on the widget. To move in only one coordinate, you must set the other one
3125 * to -1. Notice that, in %HILDON_PANNABLE_AREA_MODE_PUSH mode, this function
3126 * works just like hildon_pannable_area_jump_to().
3128 * This function is useful if you need to present the user with a particular
3129 * element inside a scrollable widget, like #GtkTreeView. For instance,
3130 * the following example shows how to scroll inside a #GtkTreeView to
3131 * make visible an item, indicated by the #GtkTreeIter @iter.
3135 * GtkTreePath *path;
3136 * GdkRectangle *rect;
3138 * path = gtk_tree_model_get_path (model, &iter);
3139 * gtk_tree_view_get_background_area (GTK_TREE_VIEW (treeview),
3140 * path, NULL, &rect);
3141 * gtk_tree_view_convert_bin_window_to_tree_coords (GTK_TREE_VIEW (treeview),
3142 * 0, rect.y, NULL, &y);
3143 * hildon_pannable_area_scroll_to (panarea, -1, y);
3144 * gtk_tree_path_free (path);
3148 * If you want to present a child widget in simpler scenarios,
3149 * use hildon_pannable_area_scroll_to_child() instead.
3151 * There is a precondition to this function: the widget must be
3152 * already realized. Check the hildon_pannable_area_jump_to_child() for
3153 * more tips regarding how to call this function during
3159 hildon_pannable_area_scroll_to (HildonPannableArea *area,
3160 const gint x, const gint y)
3162 HildonPannableAreaPrivate *priv;
3164 gint dist_x, dist_y;
3165 gboolean hscroll_visible, vscroll_visible;
3167 g_return_if_fail (GTK_WIDGET_REALIZED (area));
3168 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3172 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
3173 priv->vadjust->page_size);
3174 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
3175 priv->hadjust->page_size);
3177 if (((!vscroll_visible)&&(!hscroll_visible)) ||
3178 (x == -1 && y == -1)) {
3182 if (priv->mode == HILDON_PANNABLE_AREA_MODE_PUSH)
3183 hildon_pannable_area_jump_to (area, x, y);
3185 width = priv->hadjust->upper - priv->hadjust->lower;
3186 height = priv->vadjust->upper - priv->vadjust->lower;
3188 g_return_if_fail (x < width || y < height);
3190 if ((x > -1)&&(hscroll_visible)) {
3191 priv->scroll_to_x = CLAMP (x - priv->hadjust->page_size/2,
3192 priv->hadjust->lower,
3193 priv->hadjust->upper - priv->hadjust->page_size);
3194 dist_x = priv->scroll_to_x - priv->hadjust->value;
3196 priv->scroll_to_x = -1;
3198 priv->vel_x = - dist_x/priv->vel_factor;
3201 priv->scroll_to_x = -1;
3204 if ((y > -1)&&(vscroll_visible)) {
3205 priv->scroll_to_y = CLAMP (y - priv->vadjust->page_size/2,
3206 priv->vadjust->lower,
3207 priv->vadjust->upper - priv->vadjust->page_size);
3208 dist_y = priv->scroll_to_y - priv->vadjust->value;
3210 priv->scroll_to_y = -1;
3212 priv->vel_y = - dist_y/priv->vel_factor;
3215 priv->scroll_to_y = y;
3218 if ((priv->scroll_to_y == -1) && (priv->scroll_to_x == -1)) {
3222 hildon_pannable_area_launch_fade_timeout (area, 1.0);
3225 priv->idle_id = gdk_threads_add_timeout_full (G_PRIORITY_HIGH_IDLE + 20,
3226 (gint) (1000.0 / (gdouble) priv->sps),
3227 (GSourceFunc) hildon_pannable_area_timeout,
3232 * hildon_pannable_area_jump_to:
3233 * @area: A #HildonPannableArea.
3234 * @x: The x coordinate of the destination point or -1 to ignore this axis.
3235 * @y: The y coordinate of the destination point or -1 to ignore this axis.
3237 * Jumps the position of @area to ensure that (@x, @y) is a visible
3238 * point in the widget. In order to move in only one coordinate, you
3239 * must set the other one to -1. See hildon_pannable_area_scroll_to()
3240 * function for an example of how to calculate the position of
3241 * children in scrollable widgets like #GtkTreeview.
3243 * There is a precondition to this function: the widget must be
3244 * already realized. Check the hildon_pannable_area_jump_to_child() for
3245 * more tips regarding how to call this function during
3251 hildon_pannable_area_jump_to (HildonPannableArea *area,
3252 const gint x, const gint y)
3254 HildonPannableAreaPrivate *priv;
3258 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3259 g_return_if_fail (GTK_WIDGET_REALIZED (area));
3260 g_return_if_fail (x >= -1 && y >= -1);
3262 if (x == -1 && y == -1) {
3268 width = priv->hadjust->upper - priv->hadjust->lower;
3269 height = priv->vadjust->upper - priv->vadjust->lower;
3271 g_return_if_fail (x < width || y < height);
3273 hv = priv->hadjust->value;
3274 vv = priv->vadjust->value;
3277 gdouble jump_to = x - priv->hadjust->page_size/2;
3279 priv->hadjust->value = CLAMP (jump_to,
3280 priv->hadjust->lower,
3281 priv->hadjust->upper -
3282 priv->hadjust->page_size);
3286 gdouble jump_to = y - priv->vadjust->page_size/2;
3288 priv->vadjust->value = CLAMP (jump_to,
3289 priv->vadjust->lower,
3290 priv->vadjust->upper -
3291 priv->vadjust->page_size);
3294 if (hv != priv->hadjust->value)
3295 gtk_adjustment_value_changed (priv->hadjust);
3297 if (vv != priv->vadjust->value)
3298 gtk_adjustment_value_changed (priv->vadjust);
3300 priv->scroll_indicator_alpha = 1.0;
3302 if (priv->scroll_indicator_timeout) {
3303 g_source_remove (priv->scroll_indicator_timeout);
3304 priv->scroll_indicator_timeout = 0;
3307 if (priv->idle_id) {
3310 priv->overshooting_x = 0;
3311 priv->overshooting_y = 0;
3313 if ((priv->overshot_dist_x>0)||(priv->overshot_dist_y>0)) {
3314 priv->overshot_dist_x = 0;
3315 priv->overshot_dist_y = 0;
3317 gtk_widget_queue_resize (GTK_WIDGET (area));
3320 g_signal_emit (area, pannable_area_signals[PANNING_FINISHED], 0);
3321 g_source_remove (priv->idle_id);
3327 * hildon_pannable_area_scroll_to_child:
3328 * @area: A #HildonPannableArea.
3329 * @child: A #GtkWidget, descendant of @area.
3331 * Smoothly scrolls until @child is visible inside @area. @child must
3332 * be a descendant of @area. If you need to scroll inside a scrollable
3333 * widget, e.g., #GtkTreeview, see hildon_pannable_area_scroll_to().
3335 * There is a precondition to this function: the widget must be
3336 * already realized. Check the hildon_pannable_area_jump_to_child() for
3337 * more tips regarding how to call this function during
3343 hildon_pannable_area_scroll_to_child (HildonPannableArea *area, GtkWidget *child)
3345 GtkWidget *bin_child;
3348 g_return_if_fail (GTK_WIDGET_REALIZED (area));
3349 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3350 g_return_if_fail (GTK_IS_WIDGET (child));
3351 g_return_if_fail (gtk_widget_is_ancestor (child, GTK_WIDGET (area)));
3353 if (GTK_BIN (area)->child == NULL)
3356 /* We need to get to check the child of the inside the area */
3357 bin_child = GTK_BIN (area)->child;
3359 /* we check if we added a viewport */
3360 if (GTK_IS_VIEWPORT (bin_child)) {
3361 bin_child = GTK_BIN (bin_child)->child;
3364 if (gtk_widget_translate_coordinates (child, bin_child, 0, 0, &x, &y))
3365 hildon_pannable_area_scroll_to (area, x, y);
3369 * hildon_pannable_area_jump_to_child:
3370 * @area: A #HildonPannableArea.
3371 * @child: A #GtkWidget, descendant of @area.
3373 * Jumps to make sure @child is visible inside @area. @child must
3374 * be a descendant of @area. If you want to move inside a scrollable
3375 * widget, like, #GtkTreeview, see hildon_pannable_area_scroll_to().
3377 * There is a precondition to this function: the widget must be
3378 * already realized. You can control if the widget is ready with the
3379 * GTK_WIDGET_REALIZED macro. If you want to call this function during
3380 * the initialization process of the widget do it inside a callback to
3381 * the ::realize signal, using g_signal_connect_after() function.
3386 hildon_pannable_area_jump_to_child (HildonPannableArea *area, GtkWidget *child)
3388 GtkWidget *bin_child;
3391 g_return_if_fail (GTK_WIDGET_REALIZED (area));
3392 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3393 g_return_if_fail (GTK_IS_WIDGET (child));
3394 g_return_if_fail (gtk_widget_is_ancestor (child, GTK_WIDGET (area)));
3396 if (gtk_bin_get_child (GTK_BIN (area)) == NULL)
3399 /* We need to get to check the child of the inside the area */
3400 bin_child = gtk_bin_get_child (GTK_BIN (area));
3402 /* we check if we added a viewport */
3403 if (GTK_IS_VIEWPORT (bin_child)) {
3404 bin_child = gtk_bin_get_child (GTK_BIN (bin_child));
3407 if (gtk_widget_translate_coordinates (child, bin_child, 0, 0, &x, &y))
3408 hildon_pannable_area_jump_to (area, x, y);
3412 * hildon_pannable_get_child_widget_at:
3413 * @area: A #HildonPannableArea.
3414 * @x: horizontal coordinate of the point
3415 * @y: vertical coordinate of the point
3417 * Get the widget at the point (x, y) inside the pannable area. In
3418 * case no widget found it returns NULL.
3420 * returns: the #GtkWidget if we find a widget, NULL in any other case
3425 hildon_pannable_get_child_widget_at (HildonPannableArea *area,
3426 gdouble x, gdouble y)
3428 GdkWindow *window = NULL;
3429 GtkWidget *child_widget = NULL;
3431 window = hildon_pannable_area_get_topmost
3432 (gtk_bin_get_child (GTK_BIN (area))->window,
3433 x, y, NULL, NULL, GDK_ALL_EVENTS_MASK);
3435 gdk_window_get_user_data (window, (gpointer) &child_widget);
3437 return child_widget;
3442 * hildon_pannable_area_get_hadjustment:
3443 * @area: A #HildonPannableArea.
3445 * Returns the horizontal adjustment. This adjustment is the internal
3446 * widget adjustment used to control the animations. Do not modify it
3447 * directly to change the position of the pannable, to do that use the
3448 * pannable API. If you modify the object directly it could cause
3449 * artifacts in the animations.
3451 * returns: The horizontal #GtkAdjustment
3456 hildon_pannable_area_get_hadjustment (HildonPannableArea *area)
3459 g_return_val_if_fail (HILDON_IS_PANNABLE_AREA (area), NULL);
3461 return area->priv->hadjust;
3465 * hildon_pannable_area_get_vadjustment:
3466 * @area: A #HildonPannableArea.
3468 * Returns the vertical adjustment. This adjustment is the internal
3469 * widget adjustment used to control the animations. Do not modify it
3470 * directly to change the position of the pannable, to do that use the
3471 * pannable API. If you modify the object directly it could cause
3472 * artifacts in the animations.
3474 * returns: The vertical #GtkAdjustment
3479 hildon_pannable_area_get_vadjustment (HildonPannableArea *area)
3481 g_return_val_if_fail (HILDON_IS_PANNABLE_AREA (area), NULL);
3483 return area->priv->vadjust;
3488 * hildon_pannable_area_get_size_request_policy:
3489 * @area: A #HildonPannableArea.
3491 * This function returns the current size request policy of the
3492 * widget. That policy controls the way the size_request is done in
3493 * the pannable area. Check
3494 * hildon_pannable_area_set_size_request_policy() for a more detailed
3497 * returns: the policy is currently being used in the widget
3498 * #HildonSizeRequestPolicy.
3502 HildonSizeRequestPolicy
3503 hildon_pannable_area_get_size_request_policy (HildonPannableArea *area)
3505 HildonPannableAreaPrivate *priv;
3507 g_return_val_if_fail (HILDON_IS_PANNABLE_AREA (area), FALSE);
3511 return priv->size_request_policy;
3515 * hildon_pannable_area_set_size_request_policy:
3516 * @area: A #HildonPannableArea.
3517 * @size_request_policy: One of the allowed #HildonSizeRequestPolicy
3519 * This function sets the pannable area size request policy. That
3520 * policy controls the way the size_request is done in the pannable
3521 * area. Pannable can use the size request of its children
3522 * (#HILDON_SIZE_REQUEST_CHILDREN) or the minimum size required for
3523 * the area itself (#HILDON_SIZE_REQUEST_MINIMUM), the latter is the
3524 * default. Recall this size depends on the scrolling policy you are
3525 * requesting to the pannable area, if you set #GTK_POLICY_NEVER this
3526 * parameter will not have any effect with
3527 * #HILDON_SIZE_REQUEST_MINIMUM set.
3531 * Deprecated: This method and the policy request is deprecated, DO
3532 * NOT use it in future code, the only policy properly supported in
3533 * gtk+ nowadays is the minimum size. Use #gtk_window_set_default_size
3534 * or #gtk_window_set_geometry_hints with the proper size in your case
3535 * to define the height of your dialogs.
3538 hildon_pannable_area_set_size_request_policy (HildonPannableArea *area,
3539 HildonSizeRequestPolicy size_request_policy)
3541 HildonPannableAreaPrivate *priv;
3543 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3547 if (priv->size_request_policy == size_request_policy)
3550 priv->size_request_policy = size_request_policy;
3552 gtk_widget_queue_resize (GTK_WIDGET (area));
3554 g_object_notify (G_OBJECT (area), "size-request-policy");
3558 * hildon_pannable_area_get_center_on_child_focus
3559 * @area: A #HildonPannableArea
3561 * Gets the @area #HildonPannableArea:center-on-child-focus property
3564 * See #HildonPannableArea:center-on-child-focus for more information.
3566 * Returns: the @area #HildonPannableArea:center-on-child-focus value
3571 hildon_pannable_area_get_center_on_child_focus (HildonPannableArea *area)
3573 g_return_val_if_fail (HILDON_IS_PANNABLE_AREA (area), FALSE);
3575 return area->priv->center_on_child_focus;
3579 * hildon_pannable_area_set_center_on_child_focus
3580 * @area: A #HildonPannableArea
3581 * @value: the new value
3583 * Sets the @area #HildonPannableArea:center-on-child-focus property
3586 * See #HildonPannableArea:center-on-child-focus for more information.
3591 hildon_pannable_area_set_center_on_child_focus (HildonPannableArea *area,
3594 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3596 area->priv->center_on_child_focus = value;