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_dispose (GObject * object);
202 static void hildon_pannable_area_realize (GtkWidget * widget);
203 static void hildon_pannable_area_unrealize (GtkWidget * widget);
204 static void hildon_pannable_area_size_request (GtkWidget * widget,
205 GtkRequisition * requisition);
206 static void hildon_pannable_area_size_allocate (GtkWidget * widget,
207 GtkAllocation * allocation);
208 static void hildon_pannable_area_child_allocate_calculate (GtkWidget * widget,
209 GtkAllocation * allocation,
210 GtkAllocation * child_allocation);
211 static void hildon_pannable_area_style_set (GtkWidget * widget,
212 GtkStyle * previous_style);
213 static void hildon_pannable_area_map (GtkWidget * widget);
214 static void hildon_pannable_area_unmap (GtkWidget * widget);
215 static void hildon_pannable_area_grab_notify (GtkWidget *widget,
216 gboolean was_grabbed,
218 #if USE_CAIRO_SCROLLBARS == 1
219 static void rgb_from_gdkcolor (GdkColor *color, gdouble *r, gdouble *g, gdouble *b);
220 #else /* USE_CAIRO_SCROLLBARS */
221 static void tranparency_color (GdkColor *color,
224 gdouble transparency);
225 #endif /* USE_CAIRO_SCROLLBARS */
226 static void hildon_pannable_draw_vscroll (GtkWidget * widget,
227 GdkColor *back_color,
228 GdkColor *scroll_color);
229 static void hildon_pannable_draw_hscroll (GtkWidget * widget,
230 GdkColor *back_color,
231 GdkColor *scroll_color);
232 static void hildon_pannable_area_initial_effect (GtkWidget * widget);
233 static void hildon_pannable_area_redraw (HildonPannableArea * area);
234 static void hildon_pannable_area_launch_fade_timeout (HildonPannableArea * area,
236 static void hildon_pannable_area_adjust_value_changed (HildonPannableArea * area,
238 static void hildon_pannable_area_adjust_changed (HildonPannableArea * area,
240 static gboolean hildon_pannable_area_scroll_indicator_fade(HildonPannableArea * area);
241 static gboolean hildon_pannable_area_expose_event (GtkWidget * widget,
242 GdkEventExpose * event);
243 static GdkWindow * hildon_pannable_area_get_topmost (GdkWindow * window,
245 gint * tx, gint * ty,
247 static void synth_crossing (GdkWindow * child,
249 gint x_root, gint y_root,
250 guint32 time, gboolean in);
251 static gboolean hildon_pannable_area_button_press_cb (GtkWidget * widget,
252 GdkEventButton * event);
253 static void hildon_pannable_area_refresh (HildonPannableArea * area);
254 static gboolean hildon_pannable_area_check_scrollbars (HildonPannableArea * area);
255 static void hildon_pannable_axis_scroll (HildonPannableArea *area,
256 GtkAdjustment *adjust,
264 static void hildon_pannable_area_scroll (HildonPannableArea *area,
265 gdouble x, gdouble y);
266 static gboolean hildon_pannable_area_timeout (HildonPannableArea * area);
267 static void hildon_pannable_area_calculate_velocity (gdouble *vel,
271 gdouble drag_inertia,
274 static gboolean hildon_pannable_area_motion_event_scroll_timeout (HildonPannableArea *area);
275 static void hildon_pannable_area_motion_event_scroll (HildonPannableArea *area,
276 gdouble x, gdouble y);
277 static void hildon_pannable_area_check_move (HildonPannableArea *area,
278 GdkEventMotion * event,
281 static void hildon_pannable_area_handle_move (HildonPannableArea *area,
282 GdkEventMotion * event,
285 static gboolean hildon_pannable_area_motion_notify_cb (GtkWidget * widget,
286 GdkEventMotion * event);
287 static gboolean hildon_pannable_leave_notify_event (GtkWidget *widget,
288 GdkEventCrossing *event);
289 static gboolean hildon_pannable_area_button_release_cb (GtkWidget * widget,
290 GdkEventButton * event);
291 static gboolean hildon_pannable_area_scroll_cb (GtkWidget *widget,
292 GdkEventScroll *event);
293 static void hildon_pannable_area_child_mapped (GtkWidget *widget,
296 static void hildon_pannable_area_add (GtkContainer *container, GtkWidget *child);
297 static void hildon_pannable_area_remove (GtkContainer *container, GtkWidget *child);
298 static void hildon_pannable_calculate_vel_factor (HildonPannableArea * self);
299 static void hildon_pannable_area_set_focus_child (GtkContainer *container,
301 static void hildon_pannable_area_center_on_child_focus (HildonPannableArea *area);
305 hildon_pannable_area_class_init (HildonPannableAreaClass * klass)
307 GObjectClass *object_class = G_OBJECT_CLASS (klass);
308 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
309 GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
312 g_type_class_add_private (klass, sizeof (HildonPannableAreaPrivate));
314 object_class->get_property = hildon_pannable_area_get_property;
315 object_class->set_property = hildon_pannable_area_set_property;
316 object_class->dispose = hildon_pannable_area_dispose;
318 widget_class->realize = hildon_pannable_area_realize;
319 widget_class->unrealize = hildon_pannable_area_unrealize;
320 widget_class->map = hildon_pannable_area_map;
321 widget_class->unmap = hildon_pannable_area_unmap;
322 widget_class->size_request = hildon_pannable_area_size_request;
323 widget_class->size_allocate = hildon_pannable_area_size_allocate;
324 widget_class->expose_event = hildon_pannable_area_expose_event;
325 widget_class->style_set = hildon_pannable_area_style_set;
326 widget_class->button_press_event = hildon_pannable_area_button_press_cb;
327 widget_class->button_release_event = hildon_pannable_area_button_release_cb;
328 widget_class->motion_notify_event = hildon_pannable_area_motion_notify_cb;
329 widget_class->leave_notify_event = hildon_pannable_leave_notify_event;
330 widget_class->scroll_event = hildon_pannable_area_scroll_cb;
332 container_class->add = hildon_pannable_area_add;
333 container_class->remove = hildon_pannable_area_remove;
334 container_class->set_focus_child = hildon_pannable_area_set_focus_child;
336 klass->horizontal_movement = NULL;
337 klass->vertical_movement = NULL;
339 g_object_class_install_property (object_class,
341 g_param_spec_boolean ("enabled",
343 "Enable or disable finger-scroll.",
348 g_object_class_install_property (object_class,
349 PROP_VSCROLLBAR_POLICY,
350 g_param_spec_enum ("vscrollbar_policy",
352 "Visual policy of the vertical scrollbar",
353 GTK_TYPE_POLICY_TYPE,
354 GTK_POLICY_AUTOMATIC,
358 g_object_class_install_property (object_class,
359 PROP_HSCROLLBAR_POLICY,
360 g_param_spec_enum ("hscrollbar_policy",
362 "Visual policy of the horizontal scrollbar",
363 GTK_TYPE_POLICY_TYPE,
364 GTK_POLICY_AUTOMATIC,
368 g_object_class_install_property (object_class,
370 g_param_spec_enum ("mode",
372 "Change the finger-scrolling mode.",
373 HILDON_TYPE_PANNABLE_AREA_MODE,
374 HILDON_PANNABLE_AREA_MODE_AUTO,
378 g_object_class_install_property (object_class,
380 g_param_spec_flags ("mov_mode",
381 "Scroll movement mode",
382 "Controls if the widget can scroll vertically, horizontally or both",
383 HILDON_TYPE_MOVEMENT_MODE,
384 HILDON_MOVEMENT_MODE_VERT,
388 g_object_class_install_property (object_class,
390 g_param_spec_double ("velocity_min",
391 "Minimum scroll velocity",
392 "Minimum distance the child widget should scroll "
393 "per 'frame', in pixels per frame.",
398 g_object_class_install_property (object_class,
400 g_param_spec_double ("velocity_max",
401 "Maximum scroll velocity",
402 "Maximum distance the child widget should scroll "
403 "per 'frame', in pixels per frame.",
408 g_object_class_install_property (object_class,
409 PROP_VEL_MAX_OVERSHOOTING,
410 g_param_spec_double ("velocity_overshooting_max",
411 "Maximum scroll velocity when overshooting",
412 "Maximum distance the child widget should scroll "
413 "per 'frame', in pixels per frame when it overshoots after hitting the edge.",
418 g_object_class_install_property (object_class,
419 PROP_VELOCITY_FAST_FACTOR,
420 g_param_spec_double ("velocity_fast_factor",
421 "Fast velocity factor",
422 "Minimum velocity that is considered 'fast': "
423 "children widgets won't receive button presses. "
424 "Expressed as a fraction of the maximum velocity.",
429 g_object_class_install_property (object_class,
431 g_param_spec_double ("deceleration",
432 "Deceleration multiplier",
433 "The multiplier used when decelerating when in "
434 "acceleration scrolling mode.",
439 g_object_class_install_property (object_class,
441 g_param_spec_double ("drag_inertia",
442 "Inertia of the cursor dragging",
443 "Percentage of the calculated speed in each moment we are are going to use"
444 "to calculate the launch speed, the other part would be the speed"
445 "calculated previously",
450 g_object_class_install_property (object_class,
452 g_param_spec_uint ("sps",
453 "Scrolls per second",
454 "Amount of scroll events to generate per second.",
459 g_object_class_install_property (object_class,
460 PROP_PANNING_THRESHOLD,
461 g_param_spec_uint ("panning_threshold",
462 "Threshold to consider a motion event an scroll",
463 "Amount of pixels to consider a motion event an scroll, if it is less"
464 "it is a click detected incorrectly by the touch screen.",
469 g_object_class_install_property (object_class,
470 PROP_SCROLLBAR_FADE_DELAY,
471 g_param_spec_uint ("scrollbar_fade_delay",
472 "Time before starting to fade the scrollbar",
473 "Time the scrollbar is going to be visible if the widget is not in"
474 "action in miliseconds",
479 g_object_class_install_property (object_class,
481 g_param_spec_uint ("bounce_steps",
483 "Number of steps that is going to be used to bounce when hitting the"
484 "edge, the rubberband effect depends on it",
489 g_object_class_install_property (object_class,
491 g_param_spec_uint ("force",
492 "Multiplier of the calculated speed",
493 "Force applied to the movement, multiplies the calculated speed of the"
494 "user movement the cursor in the screen",
499 g_object_class_install_property (object_class,
500 PROP_DIRECTION_ERROR_MARGIN,
501 g_param_spec_uint ("direction_error_margin",
502 "Margin in the direction detection",
503 "After detecting the direction of the movement (horizontal or"
504 "vertical), we can add this margin of error to allow the movement in"
505 "the other direction even apparently it is not",
510 g_object_class_install_property (object_class,
512 g_param_spec_int ("vovershoot_max",
513 "Vertical overshoot distance",
514 "Space we allow the widget to pass over its vertical limits when"
515 "hitting the edges, set 0 in order to deactivate overshooting.",
520 g_object_class_install_property (object_class,
522 g_param_spec_int ("hovershoot_max",
523 "Horizontal overshoot distance",
524 "Space we allow the widget to pass over its horizontal limits when"
525 "hitting the edges, set 0 in order to deactivate overshooting.",
530 g_object_class_install_property (object_class,
532 g_param_spec_double ("scroll_time",
533 "Time to scroll to a position",
534 "The time to scroll to a position when calling the hildon_pannable_scroll_to function",
539 g_object_class_install_property (object_class,
541 g_param_spec_boolean ("initial-hint",
543 "Whether to hint the user about the pannability of the container.",
548 g_object_class_install_property (object_class,
549 PROP_LOW_FRICTION_MODE,
550 g_param_spec_boolean ("low-friction-mode",
551 "Do not decelerate the initial velocity",
552 "Avoid decelerating the panning movement, like no friction, the widget"
553 "will stop in the edges or if the user clicks.",
558 g_object_class_install_property (object_class,
559 PROP_SIZE_REQUEST_POLICY,
560 g_param_spec_enum ("size-request-policy",
561 "Size Requisition policy",
562 "Controls the size request policy of the widget",
563 HILDON_TYPE_SIZE_REQUEST_POLICY,
564 HILDON_SIZE_REQUEST_MINIMUM,
568 g_object_class_install_property (object_class,
570 g_param_spec_object ("hadjustment",
571 "Horizontal Adjustment",
572 "The GtkAdjustment for the horizontal position",
575 g_object_class_install_property (object_class,
577 g_param_spec_object ("vadjustment",
578 "Vertical Adjustment",
579 "The GtkAdjustment for the vertical position",
583 g_object_class_install_property (object_class,
584 PROP_CENTER_ON_CHILD_FOCUS,
585 g_param_spec_boolean ("center-on-child-focus",
586 "Center on the child with the focus",
587 "Whether to center the pannable on the child that receives the focus.",
593 gtk_widget_class_install_style_property (widget_class,
596 "Width of the scroll indicators",
597 "Pixel width used to draw the scroll indicators.",
602 * HildonPannableArea::horizontal-movement:
603 * @hildonpannable: the object which received the signal
604 * @direction: the direction of the movement #HILDON_MOVEMENT_LEFT or #HILDON_MOVEMENT_RIGHT
605 * @initial_x: the x coordinate of the point where the user clicked to start the movement
606 * @initial_y: the y coordinate of the point where the user clicked to start the movement
608 * The horizontal-movement signal is emitted when the pannable area
609 * detects a horizontal movement. The detection does not mean the
610 * widget is going to move (i.e. maybe the children are smaller
611 * horizontally than the screen).
615 pannable_area_signals[HORIZONTAL_MOVEMENT] =
616 g_signal_new ("horizontal_movement",
617 G_TYPE_FROM_CLASS (object_class),
618 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
619 G_STRUCT_OFFSET (HildonPannableAreaClass, horizontal_movement),
621 _hildon_marshal_VOID__INT_DOUBLE_DOUBLE,
628 * HildonPannableArea::vertical-movement:
629 * @hildonpannable: the object which received the signal
630 * @direction: the direction of the movement #HILDON_MOVEMENT_UP or #HILDON_MOVEMENT_DOWN
631 * @initial_x: the x coordinate of the point where the user clicked to start the movement
632 * @initial_y: the y coordinate of the point where the user clicked to start the movement
634 * The vertical-movement signal is emitted when the pannable area
635 * detects a vertical movement. The detection does not mean the
636 * widget is going to move (i.e. maybe the children are smaller
637 * vertically than the screen).
641 pannable_area_signals[VERTICAL_MOVEMENT] =
642 g_signal_new ("vertical_movement",
643 G_TYPE_FROM_CLASS (object_class),
644 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
645 G_STRUCT_OFFSET (HildonPannableAreaClass, vertical_movement),
647 _hildon_marshal_VOID__INT_DOUBLE_DOUBLE,
654 * HildonPannableArea::panning-started:
655 * @hildonpannable: the pannable area object that is going to start
658 * This signal is emitted before the panning starts. Applications
659 * can return %TRUE to avoid the panning. The main difference with
660 * the vertical-movement and horizontal-movement signals is those
661 * gesture signals are launched no matter if the widget is going to
662 * move, this signal means the widget is going to start moving. It
663 * could even happen that the widget moves and there was no gesture
664 * (i.e. click meanwhile the pannable is overshooting).
666 * Returns: %TRUE to stop the panning launch. %FALSE to continue
671 pannable_area_signals[PANNING_STARTED] =
672 g_signal_new ("panning-started",
673 G_TYPE_FROM_CLASS (object_class),
677 _hildon_marshal_BOOLEAN__VOID,
681 * HildonPannableArea::panning-finished:
682 * @hildonpannable: the pannable area object that finished the
685 * This signal is emitted after the kinetic panning has
690 pannable_area_signals[PANNING_FINISHED] =
691 g_signal_new ("panning-finished",
692 G_TYPE_FROM_CLASS (object_class),
696 _hildon_marshal_VOID__VOID,
702 hildon_pannable_area_init (HildonPannableArea * area)
704 HildonPannableAreaPrivate *priv = PANNABLE_AREA_PRIVATE (area);
706 GTK_WIDGET_UNSET_FLAGS (area, GTK_NO_WINDOW);
711 priv->button_pressed = FALSE;
714 priv->vscroll_visible = TRUE;
715 priv->hscroll_visible = TRUE;
716 priv->indicator_width = 6;
717 priv->overshot_dist_x = 0;
718 priv->overshot_dist_y = 0;
719 priv->overshooting_y = 0;
720 priv->overshooting_x = 0;
724 priv->scroll_indicator_alpha = 0.0;
725 priv->scroll_indicator_timeout = 0;
726 priv->motion_event_scroll_timeout = 0;
727 priv->scroll_indicator_event_interrupt = 0;
728 priv->scroll_delay_counter = 0;
729 priv->scrollbar_fade_delay = 0;
730 priv->scroll_to_x = -1;
731 priv->scroll_to_y = -1;
732 priv->first_drag = TRUE;
733 priv->initial_effect = TRUE;
734 priv->child_width = 0;
735 priv->child_height = 0;
736 priv->last_in = TRUE;
739 priv->center_on_child_focus_pending = FALSE;
741 gtk_style_lookup_color (GTK_WIDGET (area)->style,
742 "SecondaryTextColor", &priv->scroll_color);
745 GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
747 GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
749 g_object_ref_sink (G_OBJECT (priv->hadjust));
750 g_object_ref_sink (G_OBJECT (priv->vadjust));
752 g_signal_connect_swapped (priv->hadjust, "value-changed",
753 G_CALLBACK (hildon_pannable_area_adjust_value_changed), area);
754 g_signal_connect_swapped (priv->vadjust, "value-changed",
755 G_CALLBACK (hildon_pannable_area_adjust_value_changed), area);
756 g_signal_connect_swapped (priv->hadjust, "changed",
757 G_CALLBACK (hildon_pannable_area_adjust_changed), area);
758 g_signal_connect_swapped (priv->vadjust, "changed",
759 G_CALLBACK (hildon_pannable_area_adjust_changed), area);
760 g_signal_connect (area, "grab-notify",
761 G_CALLBACK (hildon_pannable_area_grab_notify), NULL);
765 hildon_pannable_area_get_property (GObject * object,
770 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (object)->priv;
772 switch (property_id) {
774 g_value_set_boolean (value, priv->enabled);
777 g_value_set_enum (value, priv->mode);
779 case PROP_MOVEMENT_MODE:
780 g_value_set_flags (value, priv->mov_mode);
782 case PROP_VELOCITY_MIN:
783 g_value_set_double (value, priv->vmin);
785 case PROP_VELOCITY_MAX:
786 g_value_set_double (value, priv->vmax);
788 case PROP_VEL_MAX_OVERSHOOTING:
789 g_value_set_double (value, priv->vmax_overshooting);
791 case PROP_VELOCITY_FAST_FACTOR:
792 g_value_set_double (value, priv->vfast_factor);
794 case PROP_DECELERATION:
795 g_value_set_double (value, priv->decel);
797 case PROP_DRAG_INERTIA:
798 g_value_set_double (value, priv->drag_inertia);
801 g_value_set_uint (value, priv->sps);
803 case PROP_PANNING_THRESHOLD:
804 g_value_set_uint (value, priv->panning_threshold);
806 case PROP_SCROLLBAR_FADE_DELAY:
807 /* convert to miliseconds */
808 g_value_set_uint (value, priv->scrollbar_fade_delay * SCROLL_FADE_TIMEOUT);
810 case PROP_BOUNCE_STEPS:
811 g_value_set_uint (value, priv->bounce_steps);
814 g_value_set_uint (value, priv->force);
816 case PROP_DIRECTION_ERROR_MARGIN:
817 g_value_set_uint (value, priv->direction_error_margin);
819 case PROP_VSCROLLBAR_POLICY:
820 g_value_set_enum (value, priv->vscrollbar_policy);
822 case PROP_HSCROLLBAR_POLICY:
823 g_value_set_enum (value, priv->hscrollbar_policy);
825 case PROP_VOVERSHOOT_MAX:
826 g_value_set_int (value, priv->vovershoot_max);
828 case PROP_HOVERSHOOT_MAX:
829 g_value_set_int (value, priv->hovershoot_max);
831 case PROP_SCROLL_TIME:
832 g_value_set_double (value, priv->scroll_time);
834 case PROP_INITIAL_HINT:
835 g_value_set_boolean (value, priv->initial_hint);
837 case PROP_LOW_FRICTION_MODE:
838 g_value_set_boolean (value, priv->low_friction_mode);
840 case PROP_SIZE_REQUEST_POLICY:
841 g_value_set_enum (value, priv->size_request_policy);
843 case PROP_HADJUSTMENT:
844 g_value_set_object (value,
845 hildon_pannable_area_get_hadjustment
846 (HILDON_PANNABLE_AREA (object)));
848 case PROP_VADJUSTMENT:
849 g_value_set_object (value,
850 hildon_pannable_area_get_vadjustment
851 (HILDON_PANNABLE_AREA (object)));
853 case PROP_CENTER_ON_CHILD_FOCUS:
854 g_value_set_boolean (value, priv->center_on_child_focus);
857 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
862 hildon_pannable_area_set_property (GObject * object,
864 const GValue * value,
867 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (object)->priv;
870 switch (property_id) {
872 enabled = g_value_get_boolean (value);
874 if ((priv->enabled != enabled) && (GTK_WIDGET_REALIZED (object))) {
876 gdk_window_raise (priv->event_window);
878 gdk_window_lower (priv->event_window);
881 priv->enabled = enabled;
884 priv->mode = g_value_get_enum (value);
886 case PROP_MOVEMENT_MODE:
887 priv->mov_mode = g_value_get_flags (value);
889 case PROP_VELOCITY_MIN:
890 priv->vmin = g_value_get_double (value);
892 case PROP_VELOCITY_MAX:
893 priv->vmax = g_value_get_double (value);
895 case PROP_VEL_MAX_OVERSHOOTING:
896 priv->vmax_overshooting = g_value_get_double (value);
898 case PROP_VELOCITY_FAST_FACTOR:
899 priv->vfast_factor = g_value_get_double (value);
901 case PROP_DECELERATION:
902 priv->decel = g_value_get_double (value);
903 hildon_pannable_calculate_vel_factor (HILDON_PANNABLE_AREA (object));
905 case PROP_DRAG_INERTIA:
906 priv->drag_inertia = g_value_get_double (value);
909 priv->sps = g_value_get_uint (value);
911 case PROP_PANNING_THRESHOLD:
913 GtkSettings *settings = gtk_settings_get_default ();
914 GtkSettingsValue svalue = { NULL, { 0, }, };
916 priv->panning_threshold = g_value_get_uint (value);
918 /* insure gtk dnd is the same we are using, not allowed
919 different thresholds in the same application */
920 svalue.origin = "panning_threshold";
921 g_value_init (&svalue.value, G_TYPE_LONG);
922 g_value_set_long (&svalue.value, priv->panning_threshold);
923 gtk_settings_set_property_value (settings, "gtk-dnd-drag-threshold", &svalue);
924 g_value_unset (&svalue.value);
927 case PROP_SCROLLBAR_FADE_DELAY:
928 /* convert to miliseconds */
929 priv->scrollbar_fade_delay = g_value_get_uint (value)/(SCROLL_FADE_TIMEOUT);
931 case PROP_BOUNCE_STEPS:
932 priv->bounce_steps = g_value_get_uint (value);
935 priv->force = g_value_get_uint (value);
937 case PROP_DIRECTION_ERROR_MARGIN:
938 priv->direction_error_margin = g_value_get_uint (value);
940 case PROP_VSCROLLBAR_POLICY:
941 priv->vscrollbar_policy = g_value_get_enum (value);
943 gtk_widget_queue_resize (GTK_WIDGET (object));
945 case PROP_HSCROLLBAR_POLICY:
946 priv->hscrollbar_policy = g_value_get_enum (value);
948 gtk_widget_queue_resize (GTK_WIDGET (object));
950 case PROP_VOVERSHOOT_MAX:
951 priv->vovershoot_max = g_value_get_int (value);
953 case PROP_HOVERSHOOT_MAX:
954 priv->hovershoot_max = g_value_get_int (value);
956 case PROP_SCROLL_TIME:
957 priv->scroll_time = g_value_get_double (value);
959 hildon_pannable_calculate_vel_factor (HILDON_PANNABLE_AREA (object));
961 case PROP_INITIAL_HINT:
962 priv->initial_hint = g_value_get_boolean (value);
964 case PROP_LOW_FRICTION_MODE:
965 priv->low_friction_mode = g_value_get_boolean (value);
967 case PROP_SIZE_REQUEST_POLICY:
968 hildon_pannable_area_set_size_request_policy (HILDON_PANNABLE_AREA (object),
969 g_value_get_enum (value));
971 case PROP_CENTER_ON_CHILD_FOCUS:
972 priv->center_on_child_focus = g_value_get_boolean (value);
976 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
981 hildon_pannable_area_dispose (GObject * object)
983 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (object)->priv;
984 GtkWidget *child = gtk_bin_get_child (GTK_BIN (object));
987 g_signal_emit (object, pannable_area_signals[PANNING_FINISHED], 0);
988 g_source_remove (priv->idle_id);
992 if (priv->scroll_indicator_timeout){
993 g_source_remove (priv->scroll_indicator_timeout);
994 priv->scroll_indicator_timeout = 0;
997 if (priv->motion_event_scroll_timeout){
998 g_source_remove (priv->motion_event_scroll_timeout);
999 priv->motion_event_scroll_timeout = 0;
1003 g_signal_handlers_disconnect_by_func (child,
1004 hildon_pannable_area_child_mapped,
1008 g_signal_handlers_disconnect_by_func (object,
1009 hildon_pannable_area_grab_notify,
1012 if (priv->hadjust) {
1013 g_signal_handlers_disconnect_by_func (priv->hadjust,
1014 hildon_pannable_area_adjust_value_changed,
1016 g_signal_handlers_disconnect_by_func (priv->hadjust,
1017 hildon_pannable_area_adjust_changed,
1019 g_object_unref (priv->hadjust);
1020 priv->hadjust = NULL;
1023 if (priv->vadjust) {
1024 g_signal_handlers_disconnect_by_func (priv->vadjust,
1025 hildon_pannable_area_adjust_value_changed,
1027 g_signal_handlers_disconnect_by_func (priv->vadjust,
1028 hildon_pannable_area_adjust_changed,
1030 g_object_unref (priv->vadjust);
1031 priv->vadjust = NULL;
1034 if (G_OBJECT_CLASS (hildon_pannable_area_parent_class)->dispose)
1035 G_OBJECT_CLASS (hildon_pannable_area_parent_class)->dispose (object);
1039 hildon_pannable_area_realize (GtkWidget * widget)
1041 GdkWindowAttr attributes;
1042 gint attributes_mask;
1044 HildonPannableAreaPrivate *priv;
1046 priv = HILDON_PANNABLE_AREA (widget)->priv;
1048 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
1050 border_width = GTK_CONTAINER (widget)->border_width;
1052 attributes.x = widget->allocation.x + border_width;
1053 attributes.y = widget->allocation.y + border_width;
1054 attributes.width = MAX (widget->allocation.width - 2 * border_width, 0);
1055 attributes.height = MAX (widget->allocation.height - 2 * border_width, 0);
1056 attributes.window_type = GDK_WINDOW_CHILD;
1058 /* avoid using the hildon_window */
1059 attributes.visual = gtk_widget_get_visual (widget);
1060 attributes.colormap = gtk_widget_get_colormap (widget);
1061 attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK;
1062 attributes.wclass = GDK_INPUT_OUTPUT;
1064 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1066 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
1067 &attributes, attributes_mask);
1068 gdk_window_set_user_data (widget->window, widget);
1070 /* create the events window */
1073 attributes.event_mask = gtk_widget_get_events (widget)
1074 | GDK_BUTTON_MOTION_MASK
1075 | GDK_BUTTON_PRESS_MASK
1076 | GDK_BUTTON_RELEASE_MASK
1078 | GDK_POINTER_MOTION_HINT_MASK
1079 | GDK_EXPOSURE_MASK | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK;
1080 attributes.wclass = GDK_INPUT_ONLY;
1082 attributes_mask = GDK_WA_X | GDK_WA_Y;
1084 priv->event_window = gdk_window_new (widget->window,
1085 &attributes, attributes_mask);
1086 gdk_window_set_user_data (priv->event_window, widget);
1088 widget->style = gtk_style_attach (widget->style, widget->window);
1089 gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
1091 priv->scrollbars_gc = gdk_gc_new (GDK_DRAWABLE (widget->window));
1092 gdk_gc_copy (priv->scrollbars_gc, widget->style->fg_gc[GTK_STATE_INSENSITIVE]);
1096 hildon_pannable_area_unrealize (GtkWidget * widget)
1098 HildonPannableAreaPrivate *priv;
1100 priv = HILDON_PANNABLE_AREA (widget)->priv;
1102 if (priv->event_window != NULL) {
1103 gdk_window_set_user_data (priv->event_window, NULL);
1104 gdk_window_destroy (priv->event_window);
1105 priv->event_window = NULL;
1108 gdk_gc_unref (priv->scrollbars_gc);
1110 if (GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->unrealize)
1111 (*GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->unrealize)(widget);
1115 hildon_pannable_area_size_request (GtkWidget * widget,
1116 GtkRequisition * requisition)
1118 GtkRequisition child_requisition = {0};
1119 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1120 GtkWidget *child = gtk_bin_get_child (GTK_BIN (widget));
1122 if (child && GTK_WIDGET_VISIBLE (child))
1124 gtk_widget_size_request (child, &child_requisition);
1127 if (priv->hscrollbar_policy == GTK_POLICY_NEVER) {
1128 requisition->width = child_requisition.width;
1130 switch (priv->size_request_policy) {
1131 case HILDON_SIZE_REQUEST_CHILDREN:
1132 requisition->width = MIN (PANNABLE_MAX_WIDTH,
1133 child_requisition.width);
1135 case HILDON_SIZE_REQUEST_MINIMUM:
1137 requisition->width = priv->indicator_width;
1141 if (priv->vscrollbar_policy == GTK_POLICY_NEVER) {
1142 requisition->height = child_requisition.height;
1144 switch (priv->size_request_policy) {
1145 case HILDON_SIZE_REQUEST_CHILDREN:
1146 requisition->height = MIN (PANNABLE_MAX_HEIGHT,
1147 child_requisition.height);
1149 case HILDON_SIZE_REQUEST_MINIMUM:
1151 requisition->height = priv->indicator_width;
1155 requisition->width += 2 * GTK_CONTAINER (widget)->border_width;
1156 requisition->height += 2 * GTK_CONTAINER (widget)->border_width;
1160 hildon_pannable_area_child_allocate_calculate (GtkWidget * widget,
1161 GtkAllocation * allocation,
1162 GtkAllocation * child_allocation)
1165 HildonPannableAreaPrivate *priv;
1167 border_width = GTK_CONTAINER (widget)->border_width;
1169 priv = HILDON_PANNABLE_AREA (widget)->priv;
1171 child_allocation->x = 0;
1172 child_allocation->y = 0;
1173 child_allocation->width = MAX (allocation->width - 2 * border_width -
1174 (priv->vscroll_visible ? priv->vscroll_rect.width : 0), 0);
1175 child_allocation->height = MAX (allocation->height - 2 * border_width -
1176 (priv->hscroll_visible ? priv->hscroll_rect.height : 0), 0);
1178 if (priv->overshot_dist_y > 0) {
1179 child_allocation->y = MIN (child_allocation->y + priv->overshot_dist_y,
1180 child_allocation->height);
1181 child_allocation->height = MAX (child_allocation->height - priv->overshot_dist_y, 0);
1182 } else if (priv->overshot_dist_y < 0) {
1183 child_allocation->height = MAX (child_allocation->height + priv->overshot_dist_y, 0);
1186 if (priv->overshot_dist_x > 0) {
1187 child_allocation->x = MIN (child_allocation->x + priv->overshot_dist_x,
1188 child_allocation->width);
1189 child_allocation->width = MAX (child_allocation->width - priv->overshot_dist_x, 0);
1190 } else if (priv->overshot_dist_x < 0) {
1191 child_allocation->width = MAX (child_allocation->width + priv->overshot_dist_x, 0);
1196 hildon_pannable_area_size_allocate (GtkWidget * widget,
1197 GtkAllocation * allocation)
1199 GtkAllocation child_allocation;
1200 HildonPannableAreaPrivate *priv;
1201 GtkWidget *child = gtk_bin_get_child (GTK_BIN (widget));
1205 border_width = GTK_CONTAINER (widget)->border_width;
1207 widget->allocation = *allocation;
1209 priv = HILDON_PANNABLE_AREA (widget)->priv;
1211 if (GTK_WIDGET_REALIZED (widget)) {
1212 gdk_window_move_resize (widget->window,
1213 allocation->x + border_width,
1214 allocation->y + border_width,
1215 allocation->width - border_width * 2,
1216 allocation->height - border_width * 2);
1217 gdk_window_move_resize (priv->event_window,
1220 allocation->width - border_width * 2,
1221 allocation->height - border_width * 2);
1224 if (child && GTK_WIDGET_VISIBLE (child)) {
1226 hildon_pannable_area_child_allocate_calculate (widget,
1230 gtk_widget_size_allocate (child, &child_allocation);
1232 if (hildon_pannable_area_check_scrollbars (HILDON_PANNABLE_AREA (widget))) {
1233 hildon_pannable_area_child_allocate_calculate (widget,
1237 gtk_widget_size_allocate (child, &child_allocation);
1240 hv = priv->hadjust->value;
1241 vv = priv->vadjust->value;
1243 /* we have to do this after child size_allocate because page_size is
1244 * changed when we allocate the size of the children */
1245 if (priv->overshot_dist_y < 0) {
1246 priv->vadjust->value = priv->vadjust->upper - priv->vadjust->page_size;
1249 if (priv->overshot_dist_x < 0) {
1250 priv->hadjust->value = priv->hadjust->upper - priv->hadjust->page_size;
1253 if (hv != priv->hadjust->value)
1254 gtk_adjustment_value_changed (priv->hadjust);
1256 if (vv != priv->vadjust->value)
1257 gtk_adjustment_value_changed (priv->vadjust);
1260 hildon_pannable_area_check_scrollbars (HILDON_PANNABLE_AREA (widget));
1265 hildon_pannable_area_style_set (GtkWidget * widget,
1266 GtkStyle * previous_style)
1268 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1270 GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->
1271 style_set (widget, previous_style);
1273 gtk_style_lookup_color (widget->style, "SecondaryTextColor", &priv->scroll_color);
1274 gtk_widget_style_get (widget, "indicator-width", &priv->indicator_width, NULL);
1278 hildon_pannable_area_map (GtkWidget * widget)
1280 HildonPannableAreaPrivate *priv;
1282 priv = HILDON_PANNABLE_AREA (widget)->priv;
1284 gdk_window_show (widget->window);
1286 if (priv->event_window != NULL && !priv->enabled)
1287 gdk_window_show (priv->event_window);
1289 (*GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->map) (widget);
1291 if (priv->event_window != NULL && priv->enabled)
1292 gdk_window_show (priv->event_window);
1296 hildon_pannable_area_unmap (GtkWidget * widget)
1298 HildonPannableAreaPrivate *priv;
1300 priv = HILDON_PANNABLE_AREA (widget)->priv;
1302 if (priv->event_window != NULL)
1303 gdk_window_hide (priv->event_window);
1305 gdk_window_hide (widget->window);
1307 (*GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->unmap) (widget);
1311 hildon_pannable_area_grab_notify (GtkWidget *widget,
1312 gboolean was_grabbed,
1315 /* an internal widget has grabbed the focus and now has returned it,
1316 we have to do some release actions */
1318 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1320 priv->scroll_indicator_event_interrupt = 0;
1322 if ((!priv->scroll_indicator_timeout)&&(priv->scroll_indicator_alpha)>0.1) {
1323 priv->scroll_delay_counter = priv->scrollbar_fade_delay;
1325 hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget),
1326 priv->scroll_indicator_alpha);
1329 priv->last_type = 3;
1330 priv->moved = FALSE;
1334 #if USE_CAIRO_SCROLLBARS == 1
1337 rgb_from_gdkcolor (GdkColor *color, gdouble *r, gdouble *g, gdouble *b)
1339 *r = (color->red >> 8) / 255.0;
1340 *g = (color->green >> 8) / 255.0;
1341 *b = (color->blue >> 8) / 255.0;
1345 hildon_pannable_draw_vscroll (GtkWidget * widget,
1346 GdkColor *back_color,
1347 GdkColor *scroll_color)
1349 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1352 cairo_pattern_t *pattern;
1354 gint radius = (priv->vscroll_rect.width/2) - 1;
1356 cr = gdk_cairo_create(widget->window);
1358 /* Draw the background */
1359 rgb_from_gdkcolor (back_color, &r, &g, &b);
1360 cairo_set_source_rgb (cr, r, g, b);
1361 cairo_rectangle(cr, priv->vscroll_rect.x, priv->vscroll_rect.y,
1362 priv->vscroll_rect.width,
1363 priv->vscroll_rect.height);
1364 cairo_fill_preserve (cr);
1367 /* Calculate the scroll bar height and position */
1368 y = ((priv->vadjust->value - priv->vadjust->lower) / (priv->vadjust->upper - priv->vadjust->lower)) *
1369 (widget->allocation.height -
1370 (priv->hscroll_visible ? priv->indicator_width : 0));
1371 height = ((((priv->vadjust->value - priv->vadjust->lower) +
1372 priv->vadjust->page_size) /
1373 (priv->vadjust->upper - priv->vadjust->lower)) *
1374 (widget->allocation.height -
1375 (priv->hscroll_visible ? priv->indicator_width : 0))) - y;
1377 /* Set a minimum height */
1378 height = MAX (SCROLL_BAR_MIN_SIZE, height);
1380 /* Check the max y position */
1381 y = MIN (y, widget->allocation.height -
1382 (priv->hscroll_visible ? priv->hscroll_rect.height : 0) -
1385 /* Draw the scrollbar */
1386 rgb_from_gdkcolor (scroll_color, &r, &g, &b);
1388 pattern = cairo_pattern_create_linear(radius+1, y, radius+1,y + height);
1389 cairo_pattern_add_color_stop_rgb(pattern, 0, r, g, b);
1390 cairo_pattern_add_color_stop_rgb(pattern, 1, r/2, g/2, b/2);
1391 cairo_set_source(cr, pattern);
1393 cairo_pattern_destroy(pattern);
1395 cairo_arc(cr, priv->vscroll_rect.x + radius + 1, y + radius + 1, radius, G_PI, 0);
1396 cairo_line_to(cr, priv->vscroll_rect.x + (radius * 2) + 1, y + height - radius);
1397 cairo_arc(cr, priv->vscroll_rect.x + radius + 1, y + height - radius, radius, 0, G_PI);
1398 cairo_line_to(cr, priv->vscroll_rect.x + 1, y + height - radius);
1401 cairo_paint_with_alpha(cr, priv->scroll_indicator_alpha);
1407 hildon_pannable_draw_hscroll (GtkWidget * widget,
1408 GdkColor *back_color,
1409 GdkColor *scroll_color)
1411 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1414 cairo_pattern_t *pattern;
1416 gint radius = (priv->hscroll_rect.height/2) - 1;
1418 cr = gdk_cairo_create(widget->window);
1420 /* Draw the background */
1421 rgb_from_gdkcolor (back_color, &r, &g, &b);
1422 cairo_set_source_rgb (cr, r, g, b);
1423 cairo_rectangle(cr, priv->hscroll_rect.x, priv->hscroll_rect.y,
1424 priv->hscroll_rect.width,
1425 priv->hscroll_rect.height);
1426 cairo_fill_preserve (cr);
1429 /* calculate the scrollbar width and position */
1430 x = ((priv->hadjust->value - priv->hadjust->lower) / (priv->hadjust->upper - priv->hadjust->lower)) *
1431 (widget->allocation.width - (priv->vscroll_visible ? priv->indicator_width : 0));
1432 width =((((priv->hadjust->value - priv->hadjust->lower) +
1433 priv->hadjust->page_size) / (priv->hadjust->upper - priv->hadjust->lower)) *
1434 (widget->allocation.width -
1435 (priv->vscroll_visible ? priv->indicator_width : 0))) - x;
1437 /* Set a minimum width */
1438 width = MAX (SCROLL_BAR_MIN_SIZE, width);
1440 /* Check the max x position */
1441 x = MIN (x, widget->allocation.width -
1442 (priv->vscroll_visible ? priv->vscroll_rect.width : 0) -
1445 /* Draw the scrollbar */
1446 rgb_from_gdkcolor (scroll_color, &r, &g, &b);
1448 pattern = cairo_pattern_create_linear(x, radius+1, x+width, radius+1);
1449 cairo_pattern_add_color_stop_rgb(pattern, 0, r, g, b);
1450 cairo_pattern_add_color_stop_rgb(pattern, 1, r/2, g/2, b/2);
1451 cairo_set_source(cr, pattern);
1453 cairo_pattern_destroy(pattern);
1455 cairo_arc_negative(cr, x + radius + 1, priv->hscroll_rect.y + radius + 1, radius, 3*G_PI_2, G_PI_2);
1456 cairo_line_to(cr, x + width - radius, priv->hscroll_rect.y + (radius * 2) + 1);
1457 cairo_arc_negative(cr, x + width - radius, priv->hscroll_rect.y + radius + 1, radius, G_PI_2, 3*G_PI_2);
1458 cairo_line_to(cr, x + width - radius, priv->hscroll_rect.y + 1);
1461 cairo_paint_with_alpha(cr, priv->scroll_indicator_alpha);
1466 #else /* USE_CAIRO_SCROLLBARS */
1469 tranparency_color (GdkColor *color,
1472 gdouble transparency)
1476 diff = colora.red - colorb.red;
1477 color->red = colora.red-diff*transparency;
1479 diff = colora.green - colorb.green;
1480 color->green = colora.green-diff*transparency;
1482 diff = colora.blue - colorb.blue;
1483 color->blue = colora.blue-diff*transparency;
1487 hildon_pannable_draw_vscroll (GtkWidget *widget,
1488 GdkColor *back_color,
1489 GdkColor *scroll_color)
1491 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1493 GdkColor transp_color;
1494 GdkGC *gc = priv->scrollbars_gc;
1496 gdk_draw_rectangle (widget->window,
1497 widget->style->bg_gc[GTK_STATE_NORMAL],
1499 priv->vscroll_rect.x, priv->vscroll_rect.y,
1500 priv->vscroll_rect.width,
1501 priv->vscroll_rect.height);
1503 y = ((priv->vadjust->value - priv->vadjust->lower) / (priv->vadjust->upper - priv->vadjust->lower)) *
1504 (widget->allocation.height - (priv->hscroll_visible ? priv->indicator_width : 0));
1505 height = ((((priv->vadjust->value - priv->vadjust->lower) + priv->vadjust->page_size) /
1506 (priv->vadjust->upper - priv->vadjust->lower)) *
1507 (widget->allocation.height -
1508 (priv->hscroll_visible ? priv->indicator_width : 0))) - y;
1510 /* Set a minimum height */
1511 height = MAX (SCROLL_BAR_MIN_SIZE, height);
1513 /* Check the max y position */
1514 y = MIN (y, widget->allocation.height -
1515 (priv->hscroll_visible ? priv->hscroll_rect.height : 0) -
1518 if (priv->scroll_indicator_alpha == 1.0) {
1519 transp_color = priv->scroll_color;
1520 } else if (priv->scroll_indicator_alpha < 1.0) {
1521 tranparency_color (&transp_color, *back_color, *scroll_color,
1522 priv->scroll_indicator_alpha);
1524 gdk_gc_set_rgb_fg_color (gc, &transp_color);
1526 gdk_draw_rectangle (widget->window, gc,
1527 TRUE, priv->vscroll_rect.x, y,
1528 priv->vscroll_rect.width, height);
1532 hildon_pannable_draw_hscroll (GtkWidget *widget,
1533 GdkColor *back_color,
1534 GdkColor *scroll_color)
1536 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1538 GdkColor transp_color;
1539 GdkGC *gc = priv->scrollbars_gc;
1541 gdk_draw_rectangle (widget->window,
1542 widget->style->bg_gc[GTK_STATE_INSENSITIVE],
1544 priv->hscroll_rect.x, priv->hscroll_rect.y,
1545 priv->hscroll_rect.width,
1546 priv->hscroll_rect.height);
1548 /* calculate the scrollbar width and position */
1549 x = ((priv->hadjust->value - priv->hadjust->lower) / (priv->hadjust->upper - priv->hadjust->lower)) *
1550 (widget->allocation.width - (priv->vscroll_visible ? priv->indicator_width : 0));
1551 width =((((priv->hadjust->value - priv->hadjust->lower) +
1552 priv->hadjust->page_size) / (priv->hadjust->upper - priv->hadjust->lower)) *
1553 (widget->allocation.width -
1554 (priv->vscroll_visible ? priv->indicator_width : 0))) - x;
1556 /* Set a minimum width */
1557 width = MAX (SCROLL_BAR_MIN_SIZE, width);
1559 /* Check the max x position */
1560 x = MIN (x, widget->allocation.width -
1561 (priv->vscroll_visible ? priv->vscroll_rect.width : 0) -
1564 if (priv->scroll_indicator_alpha == 1.0) {
1565 transp_color = priv->scroll_color;
1566 } else if (priv->scroll_indicator_alpha < 1.0) {
1567 tranparency_color (&transp_color, *back_color, *scroll_color,
1568 priv->scroll_indicator_alpha);
1570 gdk_gc_set_rgb_fg_color (gc, &transp_color);
1572 gdk_draw_rectangle (widget->window, gc,
1573 TRUE, x, priv->hscroll_rect.y, width,
1574 priv->hscroll_rect.height);
1577 #endif /* USE_CAIRO_SCROLLBARS */
1580 hildon_pannable_area_initial_effect (GtkWidget * widget)
1582 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1584 if (priv->initial_hint) {
1585 if (priv->vscroll_visible || priv->hscroll_visible) {
1587 priv->scroll_indicator_event_interrupt = 0;
1588 priv->scroll_delay_counter = priv->scrollbar_fade_delay;
1590 hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget), 1.0);
1596 hildon_pannable_area_launch_fade_timeout (HildonPannableArea * area,
1599 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (area)->priv;
1601 priv->scroll_indicator_alpha = alpha;
1603 if (!priv->scroll_indicator_timeout)
1604 priv->scroll_indicator_timeout =
1605 gdk_threads_add_timeout (SCROLL_FADE_TIMEOUT,
1606 (GSourceFunc) hildon_pannable_area_scroll_indicator_fade,
1611 hildon_pannable_area_adjust_changed (HildonPannableArea * area,
1614 if (GTK_WIDGET_REALIZED (area))
1615 hildon_pannable_area_refresh (area);
1619 hildon_pannable_area_adjust_value_changed (HildonPannableArea * area,
1622 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (area)->priv;
1624 gint x = priv->x_offset;
1625 gint y = priv->y_offset;
1627 priv->x_offset = priv->hadjust->value;
1628 xdiff = x - priv->x_offset;
1629 priv->y_offset = priv->vadjust->value;
1630 ydiff = y - priv->y_offset;
1632 if ((xdiff || ydiff) && GTK_WIDGET_DRAWABLE (area)) {
1633 hildon_pannable_area_redraw (area);
1635 if ((priv->vscroll_visible) || (priv->hscroll_visible)) {
1636 priv->scroll_indicator_event_interrupt = 0;
1637 priv->scroll_delay_counter = priv->scrollbar_fade_delay;
1639 hildon_pannable_area_launch_fade_timeout (area, 1.0);
1645 hildon_pannable_area_redraw (HildonPannableArea * area)
1647 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (area)->priv;
1649 /* Redraw scroll indicators */
1650 if (GTK_WIDGET_DRAWABLE (area)) {
1651 if (priv->hscroll_visible) {
1652 gdk_window_invalidate_rect (GTK_WIDGET (area)->window,
1653 &priv->hscroll_rect, FALSE);
1656 if (priv->vscroll_visible) {
1657 gdk_window_invalidate_rect (GTK_WIDGET (area)->window,
1658 &priv->vscroll_rect, FALSE);
1664 hildon_pannable_area_scroll_indicator_fade(HildonPannableArea * area)
1666 HildonPannableAreaPrivate *priv = area->priv;
1668 /* if moving do not fade out */
1669 if (((ABS (priv->vel_y)>priv->vmin)||
1670 (ABS (priv->vel_x)>priv->vmin))&&(!priv->button_pressed)) {
1675 if (priv->scroll_indicator_event_interrupt) {
1676 /* Stop a fade out, and fade back in */
1677 if (priv->scroll_indicator_alpha > 0.9) {
1678 priv->scroll_indicator_alpha = 1.0;
1679 priv->scroll_indicator_timeout = 0;
1683 priv->scroll_indicator_alpha += 0.2;
1684 hildon_pannable_area_redraw (area);
1690 if ((priv->scroll_indicator_alpha > 0.9) &&
1691 (priv->scroll_delay_counter > 0)) {
1692 priv->scroll_delay_counter--;
1697 if (!priv->scroll_indicator_event_interrupt) {
1698 /* Continue fade out */
1699 if (priv->scroll_indicator_alpha < 0.1) {
1700 priv->scroll_indicator_timeout = 0;
1701 priv->scroll_indicator_alpha = 0.0;
1705 priv->scroll_indicator_alpha -= 0.2;
1706 hildon_pannable_area_redraw (area);
1716 hildon_pannable_area_expose_event (GtkWidget * widget,
1717 GdkEventExpose * event)
1720 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1721 #if USE_CAIRO_SCROLLBARS == 1
1722 GdkColor back_color = widget->style->bg[GTK_STATE_NORMAL];
1723 GdkColor scroll_color = widget->style->base[GTK_STATE_SELECTED];
1724 #else /* USE_CAIRO_SCROLLBARS */
1725 GdkColor back_color = widget->style->bg[GTK_STATE_NORMAL];
1726 GdkColor scroll_color = priv->scroll_color;
1729 if (G_UNLIKELY (priv->initial_effect)) {
1730 hildon_pannable_area_initial_effect (widget);
1732 priv->initial_effect = FALSE;
1735 if (gtk_bin_get_child (GTK_BIN (widget))) {
1737 if (priv->scroll_indicator_alpha > 0.1) {
1738 if (priv->vscroll_visible) {
1739 hildon_pannable_draw_vscroll (widget, &back_color, &scroll_color);
1741 if (priv->hscroll_visible) {
1742 hildon_pannable_draw_hscroll (widget, &back_color, &scroll_color);
1746 /* draw overshooting rectangles */
1747 if (priv->overshot_dist_y > 0) {
1748 gint overshot_height;
1750 overshot_height = MIN (priv->overshot_dist_y, widget->allocation.height -
1751 (priv->hscroll_visible ? priv->hscroll_rect.height : 0));
1753 gdk_draw_rectangle (widget->window,
1754 widget->style->bg_gc[GTK_STATE_NORMAL],
1758 widget->allocation.width -
1759 (priv->vscroll_visible ? priv->vscroll_rect.width : 0),
1761 } else if (priv->overshot_dist_y < 0) {
1762 gint overshot_height;
1766 MAX (priv->overshot_dist_y,
1767 -(widget->allocation.height -
1768 (priv->hscroll_visible ? priv->hscroll_rect.height : 0)));
1770 overshot_y = MAX (widget->allocation.height +
1772 (priv->hscroll_visible ? priv->hscroll_rect.height : 0), 0);
1774 gdk_draw_rectangle (widget->window,
1775 widget->style->bg_gc[GTK_STATE_NORMAL],
1779 widget->allocation.width -
1780 priv->vscroll_rect.width,
1784 if (priv->overshot_dist_x > 0) {
1785 gint overshot_width;
1787 overshot_width = MIN (priv->overshot_dist_x, widget->allocation.width -
1788 (priv->vscroll_visible ? priv->vscroll_rect.width : 0));
1790 gdk_draw_rectangle (widget->window,
1791 widget->style->bg_gc[GTK_STATE_NORMAL],
1796 widget->allocation.height -
1797 (priv->hscroll_visible ? priv->hscroll_rect.height : 0));
1798 } else if (priv->overshot_dist_x < 0) {
1799 gint overshot_width;
1803 MAX (priv->overshot_dist_x,
1804 -(widget->allocation.width -
1805 (priv->vscroll_visible ? priv->vscroll_rect.width : 0)));
1807 overshot_x = MAX (widget->allocation.width +
1809 (priv->vscroll_visible ? priv->vscroll_rect.width : 0), 0);
1811 gdk_draw_rectangle (widget->window,
1812 widget->style->bg_gc[GTK_STATE_NORMAL],
1817 widget->allocation.height -
1818 priv->hscroll_rect.height);
1823 return GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->expose_event (widget, event);
1827 hildon_pannable_area_get_topmost (GdkWindow * window,
1829 gint * tx, gint * ty,
1832 /* Find the GdkWindow at the given point, by recursing from a given
1833 * parent GdkWindow. Optionally return the co-ordinates transformed
1834 * relative to the child window.
1837 GList *c, *children;
1838 GdkWindow *selected_window = NULL;
1840 gdk_drawable_get_size (GDK_DRAWABLE (window), &width, &height);
1841 if ((x < 0) || (x >= width) || (y < 0) || (y >= height))
1844 children = gdk_window_peek_children (window);
1851 selected_window = window;
1854 for (c = children; c; c = c->next) {
1855 GdkWindow *child = (GdkWindow *) c->data;
1858 gdk_drawable_get_size (GDK_DRAWABLE (child), &width, &height);
1859 gdk_window_get_position (child, &wx, &wy);
1861 if ((x >= wx) && (x < (wx + width)) && (y >= wy) && (y < (wy + height)) &&
1862 (gdk_window_is_visible (child))) {
1864 if (gdk_window_peek_children (child)) {
1865 selected_window = hildon_pannable_area_get_topmost (child, x-wx, y-wy,
1867 if (!selected_window) {
1872 selected_window = child;
1875 if ((gdk_window_get_events (child)&mask)) {
1880 selected_window = child;
1886 return selected_window;
1890 synth_crossing (GdkWindow * child,
1892 gint x_root, gint y_root,
1893 guint32 time, gboolean in)
1895 GdkEventCrossing *crossing_event;
1896 GdkEventType type = in ? GDK_ENTER_NOTIFY : GDK_LEAVE_NOTIFY;
1898 /* Send synthetic enter event */
1899 crossing_event = (GdkEventCrossing *) gdk_event_new (type);
1900 ((GdkEventAny *) crossing_event)->type = type;
1901 ((GdkEventAny *) crossing_event)->window = g_object_ref (child);
1902 ((GdkEventAny *) crossing_event)->send_event = FALSE;
1903 crossing_event->subwindow = g_object_ref (child);
1904 crossing_event->time = time;
1905 crossing_event->x = x;
1906 crossing_event->y = y;
1907 crossing_event->x_root = x_root;
1908 crossing_event->y_root = y_root;
1909 crossing_event->mode = GDK_CROSSING_NORMAL;
1910 crossing_event->detail = GDK_NOTIFY_UNKNOWN;
1911 crossing_event->focus = FALSE;
1912 crossing_event->state = 0;
1913 gdk_event_put ((GdkEvent *) crossing_event);
1914 gdk_event_free ((GdkEvent *) crossing_event);
1918 hildon_pannable_area_button_press_cb (GtkWidget * widget,
1919 GdkEventButton * event)
1922 HildonPannableArea *area = HILDON_PANNABLE_AREA (widget);
1923 HildonPannableAreaPrivate *priv = area->priv;
1925 if ((!priv->enabled) || (event->button != 1) ||
1926 ((event->time == priv->last_time) &&
1927 (priv->last_type == 1)) || (gtk_bin_get_child (GTK_BIN (widget)) == NULL))
1930 priv->scroll_indicator_event_interrupt = 1;
1932 hildon_pannable_area_launch_fade_timeout (area,
1933 priv->scroll_indicator_alpha);
1935 priv->last_time = event->time;
1936 priv->last_type = 1;
1938 priv->scroll_to_x = -1;
1939 priv->scroll_to_y = -1;
1941 if (priv->button_pressed && priv->child) {
1942 /* Widget stole focus on last click, send crossing-out event */
1943 synth_crossing (priv->child, 0, 0, event->x_root, event->y_root,
1944 event->time, FALSE);
1952 /* Don't allow a click if we're still moving fast */
1953 if ((ABS (priv->vel_x) <= (priv->vmax * priv->vfast_factor)) &&
1954 (ABS (priv->vel_y) <= (priv->vmax * priv->vfast_factor)))
1956 hildon_pannable_area_get_topmost (gtk_bin_get_child (GTK_BIN (widget))->window,
1957 event->x, event->y, &x, &y, GDK_BUTTON_PRESS_MASK);
1961 priv->button_pressed = TRUE;
1963 /* Stop scrolling on mouse-down (so you can flick, then hold to stop) */
1966 if (priv->idle_id) {
1967 g_source_remove (priv->idle_id);
1969 g_signal_emit (area, pannable_area_signals[PANNING_FINISHED], 0);
1974 gdk_drawable_get_size (priv->child, &priv->child_width,
1975 &priv->child_height);
1976 priv->last_in = TRUE;
1978 g_object_add_weak_pointer ((GObject *) priv->child,
1979 (gpointer) & priv->child);
1981 event = (GdkEventButton *) gdk_event_copy ((GdkEvent *) event);
1987 synth_crossing (priv->child, x, y, event->x_root,
1988 event->y_root, event->time, TRUE);
1990 /* Send synthetic click (button press/release) event */
1991 ((GdkEventAny *) event)->window = g_object_ref (priv->child);
1993 gdk_event_put ((GdkEvent *) event);
1994 gdk_event_free ((GdkEvent *) event);
2002 hildon_pannable_area_check_scrollbars (HildonPannableArea * area)
2004 HildonPannableAreaPrivate *priv = area->priv;
2005 gboolean prev_hscroll_visible, prev_vscroll_visible;
2007 prev_hscroll_visible = priv->hscroll_visible;
2008 prev_vscroll_visible = priv->vscroll_visible;
2010 if (!gtk_bin_get_child (GTK_BIN (area))) {
2011 priv->vscroll_visible = FALSE;
2012 priv->hscroll_visible = FALSE;
2014 switch (priv->hscrollbar_policy) {
2015 case GTK_POLICY_ALWAYS:
2016 priv->hscroll_visible = TRUE;
2018 case GTK_POLICY_NEVER:
2019 priv->hscroll_visible = FALSE;
2022 priv->hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
2023 priv->hadjust->page_size);
2026 switch (priv->vscrollbar_policy) {
2027 case GTK_POLICY_ALWAYS:
2028 priv->vscroll_visible = TRUE;
2030 case GTK_POLICY_NEVER:
2031 priv->vscroll_visible = FALSE;
2034 priv->vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
2035 priv->vadjust->page_size);
2038 /* Store the vscroll/hscroll areas for redrawing */
2039 if (priv->vscroll_visible) {
2040 GtkAllocation *allocation = >K_WIDGET (area)->allocation;
2041 priv->vscroll_rect.x = allocation->width - priv->indicator_width;
2042 priv->vscroll_rect.y = 0;
2043 priv->vscroll_rect.width = priv->indicator_width;
2044 priv->vscroll_rect.height = allocation->height -
2045 (priv->hscroll_visible ? priv->indicator_width : 0);
2047 if (priv->hscroll_visible) {
2048 GtkAllocation *allocation = >K_WIDGET (area)->allocation;
2049 priv->hscroll_rect.y = allocation->height - priv->indicator_width;
2050 priv->hscroll_rect.x = 0;
2051 priv->hscroll_rect.height = priv->indicator_width;
2052 priv->hscroll_rect.width = allocation->width -
2053 (priv->vscroll_visible ? priv->indicator_width : 0);
2057 return ((priv->hscroll_visible != prev_hscroll_visible) ||
2058 (priv->vscroll_visible != prev_vscroll_visible));
2062 hildon_pannable_area_refresh (HildonPannableArea * area)
2064 if (GTK_WIDGET_DRAWABLE (area) &&
2065 hildon_pannable_area_check_scrollbars (area)) {
2066 HildonPannableAreaPrivate *priv = area->priv;
2068 gtk_widget_queue_resize (GTK_WIDGET (area));
2070 if ((priv->vscroll_visible) || (priv->hscroll_visible)) {
2071 priv->scroll_indicator_event_interrupt = 0;
2072 priv->scroll_delay_counter = area->priv->scrollbar_fade_delay;
2074 hildon_pannable_area_launch_fade_timeout (area, 1.0);
2077 hildon_pannable_area_redraw (area);
2081 /* Scroll by a particular amount (in pixels). Optionally, return if
2082 * the scroll on a particular axis was successful.
2085 hildon_pannable_axis_scroll (HildonPannableArea *area,
2086 GtkAdjustment *adjust,
2090 gint *overshot_dist,
2096 HildonPannableAreaPrivate *priv = area->priv;
2098 dist = gtk_adjustment_get_value (adjust) - inc;
2101 * We use overshot_dist to define the distance of the current overshoot,
2102 * and overshooting to define the direction/whether or not we are overshot
2104 if (!(*overshooting)) {
2106 /* Initiation of the overshoot happens when the finger is released
2107 * and the current position of the pannable contents are out of range
2109 if (dist < adjust->lower) {
2112 dist = adjust->lower;
2114 if (overshoot_max!=0) {
2117 *overshot_dist = CLAMP (*overshot_dist + *vel, 0, overshoot_max);
2118 *vel = MIN (priv->vmax_overshooting, *vel);
2119 gtk_widget_queue_resize (GTK_WIDGET (area));
2123 } else if (dist > adjust->upper - adjust->page_size) {
2126 dist = adjust->upper - adjust->page_size;
2128 if (overshoot_max!=0) {
2131 *overshot_dist = CLAMP (*overshot_dist + *vel, -overshoot_max, 0);
2132 *vel = MAX (-priv->vmax_overshooting, *vel);
2133 gtk_widget_queue_resize (GTK_WIDGET (area));
2138 if ((*scroll_to) != -1) {
2139 if (((inc < 0)&&(*scroll_to <= dist))||
2140 ((inc > 0)&&(*scroll_to >= dist))) {
2148 adjust->value = dist;
2150 if (!priv->button_pressed) {
2152 /* When the overshoot has started we continue for
2153 * PROP_BOUNCE_STEPS more steps into the overshoot before we
2154 * reverse direction. The deceleration factor is calculated
2155 * based on the percentage distance from the first item with
2156 * each iteration, therefore always returning us to the
2157 * top/bottom most element
2159 if (*overshot_dist > 0) {
2161 if ((*overshooting < priv->bounce_steps) && (*vel > 0)) {
2163 *vel = (((gdouble)*overshot_dist)/overshoot_max) * (*vel);
2164 } else if ((*overshooting >= priv->bounce_steps) && (*vel > 0)) {
2166 } else if ((*overshooting > 1) && (*vel < 0)) {
2167 /* we add the MIN in order to avoid very small speeds */
2168 *vel = MIN ((((gdouble)*overshot_dist)*0.4) * -1, -2.0);
2171 *overshot_dist = CLAMP (*overshot_dist + *vel, 0, overshoot_max);
2173 gtk_widget_queue_resize (GTK_WIDGET (area));
2175 } else if (*overshot_dist < 0) {
2177 if ((*overshooting < priv->bounce_steps) && (*vel < 0)) {
2179 *vel = (((gdouble)*overshot_dist)/overshoot_max) * (*vel) * -1;
2180 } else if ((*overshooting >= priv->bounce_steps) && (*vel < 0)) {
2182 } else if ((*overshooting > 1) && (*vel > 0)) {
2183 /* we add the MAX in order to avoid very small speeds */
2184 *vel = MAX ((((gdouble)*overshot_dist)*0.4) * -1, 2.0);
2187 *overshot_dist = CLAMP (*overshot_dist + (*vel), -overshoot_max, 0);
2189 gtk_widget_queue_resize (GTK_WIDGET (area));
2194 gtk_widget_queue_resize (GTK_WIDGET (area));
2198 gint overshot_dist_old = *overshot_dist;
2200 if (*overshot_dist > 0) {
2201 *overshot_dist = CLAMP ((*overshot_dist) + inc, 0, overshoot_max);
2202 } else if (*overshot_dist < 0) {
2203 *overshot_dist = CLAMP ((*overshot_dist) + inc, -1 * overshoot_max, 0);
2206 adjust->value = CLAMP (dist,
2212 if (*overshot_dist != overshot_dist_old)
2213 gtk_widget_queue_resize (GTK_WIDGET (area));
2219 hildon_pannable_area_scroll (HildonPannableArea *area,
2220 gdouble x, gdouble y)
2223 HildonPannableAreaPrivate *priv = area->priv;
2224 gboolean hscroll_visible, vscroll_visible;
2227 if (gtk_bin_get_child (GTK_BIN (area)) == NULL)
2230 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
2231 priv->vadjust->page_size);
2232 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
2233 priv->hadjust->page_size);
2238 hv = priv->hadjust->value;
2239 vv = priv->vadjust->value;
2241 if (vscroll_visible) {
2242 hildon_pannable_axis_scroll (area, priv->vadjust, &priv->vel_y, y,
2243 &priv->overshooting_y, &priv->overshot_dist_y,
2244 &priv->scroll_to_y, priv->vovershoot_max, &sy);
2249 if (hscroll_visible) {
2250 hildon_pannable_axis_scroll (area, priv->hadjust, &priv->vel_x, x,
2251 &priv->overshooting_x, &priv->overshot_dist_x,
2252 &priv->scroll_to_x, priv->hovershoot_max, &sx);
2257 if (hv != priv->hadjust->value)
2258 gtk_adjustment_value_changed (priv->hadjust);
2260 if (vv != priv->vadjust->value)
2261 gtk_adjustment_value_changed (priv->vadjust);
2263 /* If the scroll on a particular axis wasn't succesful, reset the
2264 * initial scroll position to the new mouse co-ordinate. This means
2265 * when you get to the top of the page, dragging down works immediately.
2267 if (priv->mode == HILDON_PANNABLE_AREA_MODE_ACCEL) {
2279 hildon_pannable_area_timeout (HildonPannableArea * area)
2281 HildonPannableAreaPrivate *priv = area->priv;
2283 if ((!priv->enabled) || (priv->mode == HILDON_PANNABLE_AREA_MODE_PUSH)) {
2285 g_signal_emit (area, pannable_area_signals[PANNING_FINISHED], 0);
2290 if (!priv->button_pressed) {
2291 /* Decelerate gradually when pointer is raised */
2292 if ((!priv->overshot_dist_y) &&
2293 (!priv->overshot_dist_x)) {
2295 /* in case we move to a specific point do not decelerate when arriving */
2296 if ((priv->scroll_to_x != -1)||(priv->scroll_to_y != -1)) {
2298 if (ABS (priv->vel_x) >= 1.5) {
2299 priv->vel_x *= priv->decel;
2302 if (ABS (priv->vel_y) >= 1.5) {
2303 priv->vel_y *= priv->decel;
2307 if ((!priv->low_friction_mode) ||
2308 ((priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ) &&
2309 (ABS (priv->vel_x) < 0.8*priv->vmax)))
2310 priv->vel_x *= priv->decel;
2312 if ((!priv->low_friction_mode) ||
2313 ((priv->mov_mode&HILDON_MOVEMENT_MODE_VERT) &&
2314 (ABS (priv->vel_y) < 0.8*priv->vmax)))
2315 priv->vel_y *= priv->decel;
2317 if ((ABS (priv->vel_x) < 1.0) && (ABS (priv->vel_y) < 1.0)) {
2322 g_signal_emit (area, pannable_area_signals[PANNING_FINISHED], 0);
2328 } else if (priv->mode == HILDON_PANNABLE_AREA_MODE_AUTO) {
2334 hildon_pannable_area_scroll (area, priv->vel_x, priv->vel_y);
2336 gdk_window_process_updates (GTK_WIDGET (area)->window, FALSE);
2342 hildon_pannable_area_calculate_velocity (gdouble *vel,
2346 gdouble drag_inertia,
2352 if (ABS (dist) >= RATIO_TOLERANCE) {
2353 rawvel = (dist / ABS (delta)) * force;
2354 *vel = *vel * (1 - drag_inertia) +
2355 rawvel * drag_inertia;
2356 *vel = *vel > 0 ? MIN (*vel, vmax)
2357 : MAX (*vel, -1 * vmax);
2362 hildon_pannable_area_motion_event_scroll_timeout (HildonPannableArea *area)
2364 HildonPannableAreaPrivate *priv = area->priv;
2366 if ((priv->motion_x != 0)||(priv->motion_y != 0))
2367 hildon_pannable_area_scroll (area, priv->motion_x, priv->motion_y);
2369 priv->motion_event_scroll_timeout = 0;
2375 hildon_pannable_area_motion_event_scroll (HildonPannableArea *area,
2376 gdouble x, gdouble y)
2378 HildonPannableAreaPrivate *priv = area->priv;
2380 if (priv->motion_event_scroll_timeout) {
2382 priv->motion_x += x;
2383 priv->motion_y += y;
2387 /* we do not delay the first event but the next ones */
2388 hildon_pannable_area_scroll (area, x, y);
2393 priv->motion_event_scroll_timeout = gdk_threads_add_timeout
2394 ((gint) (1000.0 / (gdouble) MOTION_EVENTS_PER_SECOND),
2395 (GSourceFunc) hildon_pannable_area_motion_event_scroll_timeout, area);
2400 hildon_pannable_area_check_move (HildonPannableArea *area,
2401 GdkEventMotion * event,
2405 HildonPannableAreaPrivate *priv = area->priv;
2407 if (priv->first_drag && (!priv->moved) &&
2408 ((ABS (*x) > (priv->panning_threshold))
2409 || (ABS (*y) > (priv->panning_threshold)))) {
2414 if (priv->first_drag) {
2415 gboolean vscroll_visible;
2416 gboolean hscroll_visible;
2418 if (ABS (priv->iy - event->y) >=
2419 ABS (priv->ix - event->x)) {
2421 g_signal_emit (area,
2422 pannable_area_signals[VERTICAL_MOVEMENT],
2423 0, (priv->iy > event->y) ?
2424 HILDON_MOVEMENT_UP :
2425 HILDON_MOVEMENT_DOWN,
2426 (gdouble)priv->ix, (gdouble)priv->iy);
2428 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
2429 priv->vadjust->page_size);
2431 if (!((vscroll_visible)&&
2432 (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT))) {
2434 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
2435 priv->hadjust->page_size);
2437 /* even in case we do not have to move we check if this
2438 could be a fake horizontal movement */
2439 if (!((hscroll_visible)&&
2440 (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ)) ||
2441 (ABS (priv->iy - event->y) -
2442 ABS (priv->ix - event->x) >= priv->direction_error_margin))
2443 priv->moved = FALSE;
2447 g_signal_emit (area,
2448 pannable_area_signals[HORIZONTAL_MOVEMENT],
2449 0, (priv->ix > event->x) ?
2450 HILDON_MOVEMENT_LEFT :
2451 HILDON_MOVEMENT_RIGHT,
2452 (gdouble)priv->ix, (gdouble)priv->iy);
2454 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
2455 priv->hadjust->page_size);
2457 if (!((hscroll_visible)&&
2458 (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ))) {
2460 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
2461 priv->vadjust->page_size);
2463 /* even in case we do not have to move we check if this
2464 could be a fake vertical movement */
2465 if (!((vscroll_visible) &&
2466 (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT)) ||
2467 (ABS (priv->ix - event->x) -
2468 ABS (priv->iy - event->y) >= priv->direction_error_margin))
2469 priv->moved = FALSE;
2473 if ((priv->moved)&&(priv->child)) {
2476 pos_x = priv->cx + (event->x - priv->ix);
2477 pos_y = priv->cy + (event->y - priv->iy);
2479 synth_crossing (priv->child, pos_x, pos_y, event->x_root,
2480 event->y_root, event->time, FALSE);
2484 gboolean result_val;
2486 g_signal_emit (area,
2487 pannable_area_signals[PANNING_STARTED],
2490 priv->moved = !result_val;
2494 priv->first_drag = FALSE;
2496 if ((priv->mode != HILDON_PANNABLE_AREA_MODE_PUSH) &&
2497 (priv->mode != HILDON_PANNABLE_AREA_MODE_AUTO)) {
2500 priv->idle_id = gdk_threads_add_timeout_full (GDK_PRIORITY_EVENTS-10,
2501 (guint)(1000.0 / (gdouble) priv->sps),
2502 (GSourceFunc)hildon_pannable_area_timeout,
2509 hildon_pannable_area_handle_move (HildonPannableArea *area,
2510 GdkEventMotion * event,
2514 HildonPannableAreaPrivate *priv = area->priv;
2517 switch (priv->mode) {
2518 case HILDON_PANNABLE_AREA_MODE_PUSH:
2519 /* Scroll by the amount of pixels the cursor has moved
2520 * since the last motion event.
2522 hildon_pannable_area_motion_event_scroll (area, *x, *y);
2526 case HILDON_PANNABLE_AREA_MODE_ACCEL:
2527 /* Set acceleration relative to the initial click */
2528 priv->ex = event->x;
2529 priv->ey = event->y;
2530 priv->vel_x = ((*x > 0) ? 1 : -1) *
2532 (gdouble) GTK_WIDGET (area)->allocation.width) *
2533 (priv->vmax - priv->vmin)) + priv->vmin);
2534 priv->vel_y = ((*y > 0) ? 1 : -1) *
2536 (gdouble) GTK_WIDGET (area)->allocation.height) *
2537 (priv->vmax - priv->vmin)) + priv->vmin);
2539 case HILDON_PANNABLE_AREA_MODE_AUTO:
2541 delta = event->time - priv->last_time;
2543 if (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT) {
2544 gdouble dist = event->y - priv->y;
2546 hildon_pannable_area_calculate_velocity (&priv->vel_y,
2558 if (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ) {
2559 gdouble dist = event->x - priv->x;
2561 hildon_pannable_area_calculate_velocity (&priv->vel_x,
2573 hildon_pannable_area_motion_event_scroll (area, *x, *y);
2575 if (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ)
2577 if (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT)
2587 hildon_pannable_area_motion_notify_cb (GtkWidget * widget,
2588 GdkEventMotion * event)
2590 HildonPannableArea *area = HILDON_PANNABLE_AREA (widget);
2591 HildonPannableAreaPrivate *priv = area->priv;
2594 if (gtk_bin_get_child (GTK_BIN (widget)) == NULL)
2597 if ((!priv->enabled) || (!priv->button_pressed) ||
2598 ((event->time == priv->last_time) && (priv->last_type == 2))) {
2599 gdk_window_get_pointer (widget->window, NULL, NULL, 0);
2603 if (priv->last_type == 1) {
2604 priv->first_drag = TRUE;
2607 x = event->x - priv->x;
2608 y = event->y - priv->y;
2611 hildon_pannable_area_check_move (area, event, &x, &y);
2615 hildon_pannable_area_handle_move (area, event, &x, &y);
2616 } else if (priv->child) {
2620 pos_x = priv->cx + (event->x - priv->ix);
2621 pos_y = priv->cy + (event->y - priv->iy);
2623 in = (((0 <= pos_x)&&(priv->child_width >= pos_x)) &&
2624 ((0 <= pos_y)&&(priv->child_height >= pos_y)));
2626 if (((!priv->last_in)&&in)||((priv->last_in)&&(!in))) {
2628 synth_crossing (priv->child, pos_x, pos_y, event->x_root,
2629 event->y_root, event->time, in);
2635 priv->last_time = event->time;
2636 priv->last_type = 2;
2639 /* Send motion notify to child */
2640 event = (GdkEventMotion *) gdk_event_copy ((GdkEvent *) event);
2641 event->x = priv->cx + (event->x - priv->ix);
2642 event->y = priv->cy + (event->y - priv->iy);
2643 event->window = g_object_ref (priv->child);
2644 gdk_event_put ((GdkEvent *) event);
2645 gdk_event_free ((GdkEvent *) event);
2648 gdk_window_get_pointer (widget->window, NULL, NULL, 0);
2654 hildon_pannable_leave_notify_event (GtkWidget *widget,
2655 GdkEventCrossing *event)
2657 HildonPannableArea *area = HILDON_PANNABLE_AREA (widget);
2658 HildonPannableAreaPrivate *priv = area->priv;
2660 if ((priv->child)&&(priv->last_in)) {
2661 priv->last_in = FALSE;
2663 synth_crossing (priv->child, 0, 0, event->x_root,
2664 event->y_root, event->time, FALSE);
2671 hildon_pannable_area_button_release_cb (GtkWidget * widget,
2672 GdkEventButton * event)
2674 HildonPannableArea *area = HILDON_PANNABLE_AREA (widget);
2675 HildonPannableAreaPrivate *priv = area->priv;
2680 if (((event->time == priv->last_time) && (priv->last_type == 3))
2681 || (gtk_bin_get_child (GTK_BIN (widget)) == NULL)
2682 || (!priv->button_pressed) || (!priv->enabled) || (event->button != 1))
2685 /* if last event was a motion-notify we have to check the movement
2686 and launch the animation */
2687 if (priv->last_type == 2) {
2689 dx = event->x - priv->x;
2690 dy = event->y - priv->y;
2692 hildon_pannable_area_check_move (area, (GdkEventMotion *) event, &dx, &dy);
2695 gdouble delta = event->time - priv->last_time;
2697 hildon_pannable_area_handle_move (area, (GdkEventMotion *) event, &dx, &dy);
2699 /* move all the way to the last position now */
2700 if (priv->motion_event_scroll_timeout) {
2701 g_source_remove (priv->motion_event_scroll_timeout);
2702 hildon_pannable_area_motion_event_scroll_timeout (HILDON_PANNABLE_AREA (widget));
2707 if ((ABS (dx) < 4.0) && (delta >= CURSOR_STOPPED_TIMEOUT))
2710 if ((ABS (dy) < 4.0) && (delta >= CURSOR_STOPPED_TIMEOUT))
2715 /* If overshoot has been initiated with a finger down, on release set max speed */
2716 if (priv->overshot_dist_y != 0) {
2717 priv->overshooting_y = priv->bounce_steps; /* Hack to stop a bounce in the finger down case */
2718 priv->vel_y = priv->vmax_overshooting;
2721 if (priv->overshot_dist_x != 0) {
2722 priv->overshooting_x = priv->bounce_steps; /* Hack to stop a bounce in the finger down case */
2723 priv->vel_x = priv->vmax_overshooting;
2726 priv->button_pressed = FALSE;
2728 if ((ABS (priv->vel_y) >= priv->vmin) ||
2729 (ABS (priv->vel_x) >= priv->vmin)) {
2731 /* we have to move because we are in overshooting position*/
2733 gboolean result_val;
2735 g_signal_emit (area,
2736 pannable_area_signals[PANNING_STARTED],
2740 priv->scroll_indicator_alpha = 1.0;
2742 if (ABS (priv->vel_x) > MAX_SPEED_THRESHOLD)
2743 priv->vel_x = (priv->vel_x > 0) ? priv->vmax : -priv->vmax;
2745 if (ABS (priv->vel_y) > MAX_SPEED_THRESHOLD)
2746 priv->vel_y = (priv->vel_y > 0) ? priv->vmax : -priv->vmax;
2749 priv->idle_id = gdk_threads_add_timeout_full (GDK_PRIORITY_EVENTS-10,
2750 (guint)(1000.0 / (gdouble) priv->sps),
2751 (GSourceFunc)hildon_pannable_area_timeout,
2754 if (priv->center_on_child_focus_pending) {
2755 hildon_pannable_area_center_on_child_focus (area);
2759 g_signal_emit (widget, pannable_area_signals[PANNING_FINISHED], 0);
2762 area->priv->center_on_child_focus_pending = FALSE;
2764 priv->scroll_indicator_event_interrupt = 0;
2765 priv->scroll_delay_counter = priv->scrollbar_fade_delay;
2767 hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget),
2768 priv->scroll_indicator_alpha);
2770 priv->last_time = event->time;
2771 priv->last_type = 3;
2774 priv->moved = FALSE;
2779 hildon_pannable_area_get_topmost (gtk_bin_get_child (GTK_BIN (widget))->window,
2780 event->x, event->y, &x, &y, GDK_BUTTON_RELEASE_MASK);
2782 event = (GdkEventButton *) gdk_event_copy ((GdkEvent *) event);
2786 /* Leave the widget if we've moved - This doesn't break selection,
2787 * but stops buttons from being clicked.
2789 if ((child != priv->child) || (priv->moved)) {
2790 /* Send synthetic leave event */
2791 synth_crossing (priv->child, x, y, event->x_root,
2792 event->y_root, event->time, FALSE);
2793 /* insure no click will happen for widgets that do not handle
2797 /* Send synthetic button release event */
2798 ((GdkEventAny *) event)->window = g_object_ref (priv->child);
2799 gdk_event_put ((GdkEvent *) event);
2801 /* Send synthetic button release event */
2802 ((GdkEventAny *) event)->window = g_object_ref (child);
2803 gdk_event_put ((GdkEvent *) event);
2804 /* Send synthetic leave event */
2805 synth_crossing (priv->child, x, y, event->x_root,
2806 event->y_root, event->time, FALSE);
2808 g_object_remove_weak_pointer ((GObject *) priv->child,
2809 (gpointer) & priv->child);
2811 priv->moved = FALSE;
2812 gdk_event_free ((GdkEvent *) event);
2817 /* utility event handler */
2819 hildon_pannable_area_scroll_cb (GtkWidget *widget,
2820 GdkEventScroll *event)
2822 GtkAdjustment *adj = NULL;
2823 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
2825 if ((!priv->enabled) ||
2826 (gtk_bin_get_child (GTK_BIN (widget)) == NULL))
2829 priv->scroll_indicator_event_interrupt = 0;
2830 priv->scroll_delay_counter = priv->scrollbar_fade_delay + 20;
2832 hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget), 1.0);
2834 /* Stop inertial scrolling */
2835 if (priv->idle_id) {
2838 priv->overshooting_x = 0;
2839 priv->overshooting_y = 0;
2841 if ((priv->overshot_dist_x>0)||(priv->overshot_dist_y>0)) {
2842 priv->overshot_dist_x = 0;
2843 priv->overshot_dist_y = 0;
2845 gtk_widget_queue_resize (GTK_WIDGET (widget));
2848 g_signal_emit (widget, pannable_area_signals[PANNING_FINISHED], 0);
2850 g_source_remove (priv->idle_id);
2854 if (event->direction == GDK_SCROLL_UP || event->direction == GDK_SCROLL_DOWN)
2855 adj = priv->vadjust;
2857 adj = priv->hadjust;
2861 gdouble delta, new_value;
2863 /* from gtkrange.c calculate delta*/
2864 delta = pow (adj->page_size, 2.0 / 3.0);
2866 if (event->direction == GDK_SCROLL_UP ||
2867 event->direction == GDK_SCROLL_LEFT)
2870 new_value = CLAMP (adj->value + delta, adj->lower, adj->upper - adj->page_size);
2872 gtk_adjustment_set_value (adj, new_value);
2879 hildon_pannable_area_child_mapped (GtkWidget *widget,
2883 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (user_data)->priv;
2885 if (priv->event_window != NULL && priv->enabled)
2886 gdk_window_raise (priv->event_window);
2890 hildon_pannable_area_add (GtkContainer *container, GtkWidget *child)
2892 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (container)->priv;
2894 g_return_if_fail (gtk_bin_get_child (GTK_BIN (container)) == NULL);
2896 gtk_widget_set_parent (child, GTK_WIDGET (container));
2897 GTK_BIN (container)->child = child;
2899 g_signal_connect_after (child, "map-event",
2900 G_CALLBACK (hildon_pannable_area_child_mapped),
2903 if (!gtk_widget_set_scroll_adjustments (child, priv->hadjust, priv->vadjust)) {
2904 g_warning ("%s: cannot add non scrollable widget, "
2905 "wrap it in a viewport", __FUNCTION__);
2909 /* call this function if you are not panning */
2911 hildon_pannable_area_center_on_child_focus (HildonPannableArea *area)
2913 GtkWidget *focused_child = NULL;
2914 GtkWidget *window = NULL;
2916 window = gtk_widget_get_toplevel (GTK_WIDGET (area));
2918 if (GTK_WIDGET_TOPLEVEL (window)) {
2919 focused_child = gtk_window_get_focus (GTK_WINDOW (window));
2922 if (focused_child) {
2923 hildon_pannable_area_scroll_to_child (area, focused_child);
2928 hildon_pannable_area_set_focus_child (GtkContainer *container,
2931 HildonPannableArea *area = HILDON_PANNABLE_AREA (container);
2933 if (!area->priv->center_on_child_focus) {
2937 if (GTK_IS_WIDGET (child)) {
2938 area->priv->center_on_child_focus_pending = TRUE;
2943 hildon_pannable_area_remove (GtkContainer *container, GtkWidget *child)
2945 g_return_if_fail (HILDON_IS_PANNABLE_AREA (container));
2946 g_return_if_fail (child != NULL);
2947 g_return_if_fail (gtk_bin_get_child (GTK_BIN (container)) == child);
2949 gtk_widget_set_scroll_adjustments (child, NULL, NULL);
2951 g_signal_handlers_disconnect_by_func (child,
2952 hildon_pannable_area_child_mapped,
2955 /* chain parent class handler to remove child */
2956 GTK_CONTAINER_CLASS (hildon_pannable_area_parent_class)->remove (container, child);
2960 * This method calculates a factor necessary to determine the initial distance
2961 * to jump in hildon_pannable_area_scroll_to(). For fixed time and frames per
2962 * second, we know in how many frames 'n' we need to reach the destination
2963 * point. We know that, for a distance d,
2965 * d = d_0 + d_1 + ... + d_n
2967 * where d_i is the distance travelled in the i-th frame and decel_factor is
2968 * the deceleration factor. This can be rewritten as
2970 * d = d_0 + (d_0 * decel_factor) + ... + (d_n-1 * decel_factor),
2972 * since the distance travelled on each frame is the distance travelled in the
2973 * previous frame reduced by the deceleration factor. Reducing this and
2974 * factoring d_0 out, we get
2976 * d = d_0 (1 + decel_factor + ... + decel_factor^(n-1)).
2978 * Since the sum is independent of the distance to be travelled, we can define
2981 * vel_factor = 1 + decel_factor + ... + decel_factor^(n-1).
2983 * That's the gem we calculate in this method.
2986 hildon_pannable_calculate_vel_factor (HildonPannableArea * self)
2988 HildonPannableAreaPrivate *priv = self->priv;
2993 n = ceil (priv->sps * priv->scroll_time);
2995 for (i = 1; i < n && fct_i >= RATIO_TOLERANCE; i++) {
2996 fct_i *= priv->decel;
3000 priv->vel_factor = fct;
3004 * hildon_pannable_area_new:
3006 * Create a new pannable area widget
3008 * Returns: the newly created #HildonPannableArea
3014 hildon_pannable_area_new (void)
3016 return g_object_new (HILDON_TYPE_PANNABLE_AREA, NULL);
3020 * hildon_pannable_area_new_full:
3021 * @mode: #HildonPannableAreaMode
3022 * @enabled: Value for the enabled property
3023 * @vel_min: Value for the velocity-min property
3024 * @vel_max: Value for the velocity-max property
3025 * @decel: Value for the deceleration property
3026 * @sps: Value for the sps property
3028 * Create a new #HildonPannableArea widget and set various properties
3030 * returns: the newly create #HildonPannableArea
3036 hildon_pannable_area_new_full (gint mode, gboolean enabled,
3037 gdouble vel_min, gdouble vel_max,
3038 gdouble decel, guint sps)
3040 return g_object_new (HILDON_TYPE_PANNABLE_AREA,
3043 "velocity_min", vel_min,
3044 "velocity_max", vel_max,
3045 "deceleration", decel, "sps", sps, NULL);
3049 * hildon_pannable_area_add_with_viewport:
3050 * @area: A #HildonPannableArea
3051 * @child: Child widget to add to the viewport
3053 * Convenience function used to add a child to a #GtkViewport, and add the
3054 * viewport to the scrolled window.
3060 hildon_pannable_area_add_with_viewport (HildonPannableArea * area,
3064 GtkWidget *viewport;
3066 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3067 g_return_if_fail (GTK_IS_WIDGET (child));
3068 g_return_if_fail (child->parent == NULL);
3070 bin = GTK_BIN (area);
3072 if (bin->child != NULL)
3074 g_return_if_fail (GTK_IS_VIEWPORT (bin->child));
3075 g_return_if_fail (GTK_BIN (bin->child)->child == NULL);
3077 viewport = bin->child;
3081 HildonPannableAreaPrivate *priv = area->priv;
3083 viewport = gtk_viewport_new (priv->hadjust,
3085 gtk_viewport_set_shadow_type (GTK_VIEWPORT (viewport), GTK_SHADOW_NONE);
3086 gtk_container_add (GTK_CONTAINER (area), viewport);
3089 gtk_widget_show (viewport);
3090 gtk_container_add (GTK_CONTAINER (viewport), child);
3094 * hildon_pannable_area_scroll_to:
3095 * @area: A #HildonPannableArea.
3096 * @x: The x coordinate of the destination point or -1 to ignore this axis.
3097 * @y: The y coordinate of the destination point or -1 to ignore this axis.
3099 * Smoothly scrolls @area to ensure that (@x, @y) is a visible point
3100 * on the widget. To move in only one coordinate, you must set the other one
3101 * to -1. Notice that, in %HILDON_PANNABLE_AREA_MODE_PUSH mode, this function
3102 * works just like hildon_pannable_area_jump_to().
3104 * This function is useful if you need to present the user with a particular
3105 * element inside a scrollable widget, like #GtkTreeView. For instance,
3106 * the following example shows how to scroll inside a #GtkTreeView to
3107 * make visible an item, indicated by the #GtkTreeIter @iter.
3111 * GtkTreePath *path;
3112 * GdkRectangle *rect;
3114 * path = gtk_tree_model_get_path (model, &iter);
3115 * gtk_tree_view_get_background_area (GTK_TREE_VIEW (treeview),
3116 * path, NULL, &rect);
3117 * gtk_tree_view_convert_bin_window_to_tree_coords (GTK_TREE_VIEW (treeview),
3118 * 0, rect.y, NULL, &y);
3119 * hildon_pannable_area_scroll_to (panarea, -1, y);
3120 * gtk_tree_path_free (path);
3124 * If you want to present a child widget in simpler scenarios,
3125 * use hildon_pannable_area_scroll_to_child() instead.
3127 * There is a precondition to this function: the widget must be
3128 * already realized. Check the hildon_pannable_area_jump_to_child() for
3129 * more tips regarding how to call this function during
3135 hildon_pannable_area_scroll_to (HildonPannableArea *area,
3136 const gint x, const gint y)
3138 HildonPannableAreaPrivate *priv;
3140 gint dist_x, dist_y;
3141 gboolean hscroll_visible, vscroll_visible;
3143 g_return_if_fail (GTK_WIDGET_REALIZED (area));
3144 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3148 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
3149 priv->vadjust->page_size);
3150 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
3151 priv->hadjust->page_size);
3153 if (((!vscroll_visible)&&(!hscroll_visible)) ||
3154 (x == -1 && y == -1)) {
3158 if (priv->mode == HILDON_PANNABLE_AREA_MODE_PUSH)
3159 hildon_pannable_area_jump_to (area, x, y);
3161 width = priv->hadjust->upper - priv->hadjust->lower;
3162 height = priv->vadjust->upper - priv->vadjust->lower;
3164 g_return_if_fail (x < width || y < height);
3166 if ((x > -1)&&(hscroll_visible)) {
3167 priv->scroll_to_x = x - priv->hadjust->page_size/2;
3168 dist_x = priv->scroll_to_x - priv->hadjust->value;
3170 priv->scroll_to_x = -1;
3172 priv->vel_x = - dist_x/priv->vel_factor;
3175 priv->scroll_to_x = -1;
3178 if ((y > -1)&&(vscroll_visible)) {
3179 priv->scroll_to_y = y - priv->vadjust->page_size/2;
3180 dist_y = priv->scroll_to_y - priv->vadjust->value;
3182 priv->scroll_to_y = -1;
3184 priv->vel_y = - dist_y/priv->vel_factor;
3187 priv->scroll_to_y = y;
3190 if ((priv->scroll_to_y == -1) && (priv->scroll_to_y == -1)) {
3194 hildon_pannable_area_launch_fade_timeout (area, 1.0);
3197 priv->idle_id = gdk_threads_add_timeout_full (GDK_PRIORITY_EVENTS-10,
3198 (guint)(1000.0 / (gdouble) priv->sps),
3199 (GSourceFunc)hildon_pannable_area_timeout,
3204 * hildon_pannable_area_jump_to:
3205 * @area: A #HildonPannableArea.
3206 * @x: The x coordinate of the destination point or -1 to ignore this axis.
3207 * @y: The y coordinate of the destination point or -1 to ignore this axis.
3209 * Jumps the position of @area to ensure that (@x, @y) is a visible
3210 * point in the widget. In order to move in only one coordinate, you
3211 * must set the other one to -1. See hildon_pannable_area_scroll_to()
3212 * function for an example of how to calculate the position of
3213 * children in scrollable widgets like #GtkTreeview.
3215 * There is a precondition to this function: the widget must be
3216 * already realized. Check the hildon_pannable_area_jump_to_child() for
3217 * more tips regarding how to call this function during
3223 hildon_pannable_area_jump_to (HildonPannableArea *area,
3224 const gint x, const gint y)
3226 HildonPannableAreaPrivate *priv;
3230 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3231 g_return_if_fail (GTK_WIDGET_REALIZED (area));
3232 g_return_if_fail (x >= -1 && y >= -1);
3234 if (x == -1 && y == -1) {
3240 width = priv->hadjust->upper - priv->hadjust->lower;
3241 height = priv->vadjust->upper - priv->vadjust->lower;
3243 g_return_if_fail (x < width || y < height);
3245 hv = priv->hadjust->value;
3246 vv = priv->vadjust->value;
3249 gdouble jump_to = x - priv->hadjust->page_size/2;
3251 priv->hadjust->value = CLAMP (jump_to,
3252 priv->hadjust->lower,
3253 priv->hadjust->upper -
3254 priv->hadjust->page_size);
3258 gdouble jump_to = y - priv->vadjust->page_size/2;
3260 priv->vadjust->value = CLAMP (jump_to,
3261 priv->vadjust->lower,
3262 priv->vadjust->upper -
3263 priv->vadjust->page_size);
3266 if (hv != priv->hadjust->value)
3267 gtk_adjustment_value_changed (priv->hadjust);
3269 if (vv != priv->vadjust->value)
3270 gtk_adjustment_value_changed (priv->vadjust);
3272 priv->scroll_indicator_alpha = 1.0;
3274 if (priv->scroll_indicator_timeout) {
3275 g_source_remove (priv->scroll_indicator_timeout);
3276 priv->scroll_indicator_timeout = 0;
3279 if (priv->idle_id) {
3282 priv->overshooting_x = 0;
3283 priv->overshooting_y = 0;
3285 if ((priv->overshot_dist_x>0)||(priv->overshot_dist_y>0)) {
3286 priv->overshot_dist_x = 0;
3287 priv->overshot_dist_y = 0;
3289 gtk_widget_queue_resize (GTK_WIDGET (area));
3292 g_signal_emit (area, pannable_area_signals[PANNING_FINISHED], 0);
3293 g_source_remove (priv->idle_id);
3299 * hildon_pannable_area_scroll_to_child:
3300 * @area: A #HildonPannableArea.
3301 * @child: A #GtkWidget, descendant of @area.
3303 * Smoothly scrolls until @child is visible inside @area. @child must
3304 * be a descendant of @area. If you need to scroll inside a scrollable
3305 * widget, e.g., #GtkTreeview, see hildon_pannable_area_scroll_to().
3307 * There is a precondition to this function: the widget must be
3308 * already realized. Check the hildon_pannable_area_jump_to_child() for
3309 * more tips regarding how to call this function during
3315 hildon_pannable_area_scroll_to_child (HildonPannableArea *area, GtkWidget *child)
3317 GtkWidget *bin_child;
3320 g_return_if_fail (GTK_WIDGET_REALIZED (area));
3321 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3322 g_return_if_fail (GTK_IS_WIDGET (child));
3323 g_return_if_fail (gtk_widget_is_ancestor (child, GTK_WIDGET (area)));
3325 if (GTK_BIN (area)->child == NULL)
3328 /* We need to get to check the child of the inside the area */
3329 bin_child = GTK_BIN (area)->child;
3331 /* we check if we added a viewport */
3332 if (GTK_IS_VIEWPORT (bin_child)) {
3333 bin_child = GTK_BIN (bin_child)->child;
3336 if (gtk_widget_translate_coordinates (child, bin_child, 0, 0, &x, &y))
3337 hildon_pannable_area_scroll_to (area, x, y);
3341 * hildon_pannable_area_jump_to_child:
3342 * @area: A #HildonPannableArea.
3343 * @child: A #GtkWidget, descendant of @area.
3345 * Jumps to make sure @child is visible inside @area. @child must
3346 * be a descendant of @area. If you want to move inside a scrollable
3347 * widget, like, #GtkTreeview, see hildon_pannable_area_scroll_to().
3349 * There is a precondition to this function: the widget must be
3350 * already realized. You can control if the widget is ready with the
3351 * GTK_WIDGET_REALIZED macro. If you want to call this function during
3352 * the initialization process of the widget do it inside a callback to
3353 * the ::realize signal, using g_signal_connect_after() function.
3358 hildon_pannable_area_jump_to_child (HildonPannableArea *area, GtkWidget *child)
3360 GtkWidget *bin_child;
3363 g_return_if_fail (GTK_WIDGET_REALIZED (area));
3364 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3365 g_return_if_fail (GTK_IS_WIDGET (child));
3366 g_return_if_fail (gtk_widget_is_ancestor (child, GTK_WIDGET (area)));
3368 if (gtk_bin_get_child (GTK_BIN (area)) == NULL)
3371 /* We need to get to check the child of the inside the area */
3372 bin_child = gtk_bin_get_child (GTK_BIN (area));
3374 /* we check if we added a viewport */
3375 if (GTK_IS_VIEWPORT (bin_child)) {
3376 bin_child = gtk_bin_get_child (GTK_BIN (bin_child));
3379 if (gtk_widget_translate_coordinates (child, bin_child, 0, 0, &x, &y))
3380 hildon_pannable_area_jump_to (area, x, y);
3384 * hildon_pannable_get_child_widget_at:
3385 * @area: A #HildonPannableArea.
3386 * @x: horizontal coordinate of the point
3387 * @y: vertical coordinate of the point
3389 * Get the widget at the point (x, y) inside the pannable area. In
3390 * case no widget found it returns NULL.
3392 * returns: the #GtkWidget if we find a widget, NULL in any other case
3397 hildon_pannable_get_child_widget_at (HildonPannableArea *area,
3398 gdouble x, gdouble y)
3400 GdkWindow *window = NULL;
3401 GtkWidget *child_widget = NULL;
3403 window = hildon_pannable_area_get_topmost
3404 (gtk_bin_get_child (GTK_BIN (area))->window,
3405 x, y, NULL, NULL, GDK_ALL_EVENTS_MASK);
3407 gdk_window_get_user_data (window, (gpointer) &child_widget);
3409 return child_widget;
3414 * hildon_pannable_area_get_hadjustment:
3415 * @area: A #HildonPannableArea.
3417 * Returns the horizontal adjustment. This adjustment is the internal
3418 * widget adjustment used to control the animations. Do not modify it
3419 * directly to change the position of the pannable, to do that use the
3420 * pannable API. If you modify the object directly it could cause
3421 * artifacts in the animations.
3423 * returns: The horizontal #GtkAdjustment
3428 hildon_pannable_area_get_hadjustment (HildonPannableArea *area)
3431 g_return_val_if_fail (HILDON_IS_PANNABLE_AREA (area), NULL);
3433 return area->priv->hadjust;
3437 * hildon_pannable_area_get_vadjustment:
3438 * @area: A #HildonPannableArea.
3440 * Returns the vertical adjustment. This adjustment is the internal
3441 * widget adjustment used to control the animations. Do not modify it
3442 * directly to change the position of the pannable, to do that use the
3443 * pannable API. If you modify the object directly it could cause
3444 * artifacts in the animations.
3446 * returns: The vertical #GtkAdjustment
3451 hildon_pannable_area_get_vadjustment (HildonPannableArea *area)
3453 g_return_val_if_fail (HILDON_IS_PANNABLE_AREA (area), NULL);
3455 return area->priv->vadjust;
3460 * hildon_pannable_area_get_size_request_policy:
3461 * @area: A #HildonPannableArea.
3463 * This function returns the current size request policy of the
3464 * widget. That policy controls the way the size_request is done in
3465 * the pannable area. Check
3466 * hildon_pannable_area_set_size_request_policy() for a more detailed
3469 * returns: the policy is currently being used in the widget
3470 * #HildonSizeRequestPolicy.
3474 HildonSizeRequestPolicy
3475 hildon_pannable_area_get_size_request_policy (HildonPannableArea *area)
3477 HildonPannableAreaPrivate *priv;
3479 g_return_val_if_fail (HILDON_IS_PANNABLE_AREA (area), FALSE);
3483 return priv->size_request_policy;
3487 * hildon_pannable_area_set_size_request_policy:
3488 * @area: A #HildonPannableArea.
3489 * @size_request_policy: One of the allowed #HildonSizeRequestPolicy
3491 * This function sets the pannable area size request policy. That
3492 * policy controls the way the size_request is done in the pannable
3493 * area. Pannable can use the size request of its children
3494 * (#HILDON_SIZE_REQUEST_CHILDREN) or the minimum size required for
3495 * the area itself (#HILDON_SIZE_REQUEST_MINIMUM), the latter is the
3496 * default. Recall this size depends on the scrolling policy you are
3497 * requesting to the pannable area, if you set #GTK_POLICY_NEVER this
3498 * parameter will not have any effect with
3499 * #HILDON_SIZE_REQUEST_MINIMUM set.
3503 * Deprecated: This method and the policy request is deprecated, DO
3504 * NOT use it in future code, the only policy properly supported in
3505 * gtk+ nowadays is the minimum size. Use #gtk_window_set_default_size
3506 * or #gtk_window_set_geometry_hints with the proper size in your case
3507 * to define the height of your dialogs.
3510 hildon_pannable_area_set_size_request_policy (HildonPannableArea *area,
3511 HildonSizeRequestPolicy size_request_policy)
3513 HildonPannableAreaPrivate *priv;
3515 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3519 if (priv->size_request_policy == size_request_policy)
3522 priv->size_request_policy = size_request_policy;
3524 gtk_widget_queue_resize (GTK_WIDGET (area));
3526 g_object_notify (G_OBJECT (area), "size-request-policy");
3530 * hildon_pannable_area_get_center_on_child_focus
3531 * @area: A #HildonPannableArea
3533 * Gets the @area #HildonPannableArea:center-on-child-focus property
3536 * See #HildonPannableArea:center-on-child-focus for more information.
3538 * Returns: the @area #HildonPannableArea:center-on-child-focus value
3543 hildon_pannable_area_get_center_on_child_focus (HildonPannableArea *area)
3545 g_return_val_if_fail (HILDON_IS_PANNABLE_AREA (area), FALSE);
3547 return area->priv->center_on_child_focus;
3551 * hildon_pannable_area_set_center_on_child_focus
3552 * @area: A #HildonPannableArea
3553 * @value: the new value
3555 * Sets the @area #HildonPannableArea:center-on-child-focus property
3558 * See #HildonPannableArea:center-on-child-focus for more information.
3563 hildon_pannable_area_set_center_on_child_focus (HildonPannableArea *area,
3566 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3568 area->priv->center_on_child_focus = value;