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 ((gint)
2501 (1000.0 / (gdouble) priv->sps),
2503 hildon_pannable_area_timeout, area);
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 ((gint) (1000.0 / (gdouble) priv->sps),
2751 hildon_pannable_area_timeout, widget);
2753 if (priv->center_on_child_focus_pending) {
2754 hildon_pannable_area_center_on_child_focus (area);
2758 g_signal_emit (widget, pannable_area_signals[PANNING_FINISHED], 0);
2761 area->priv->center_on_child_focus_pending = FALSE;
2763 priv->scroll_indicator_event_interrupt = 0;
2764 priv->scroll_delay_counter = priv->scrollbar_fade_delay;
2766 hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget),
2767 priv->scroll_indicator_alpha);
2769 priv->last_time = event->time;
2770 priv->last_type = 3;
2773 priv->moved = FALSE;
2778 hildon_pannable_area_get_topmost (gtk_bin_get_child (GTK_BIN (widget))->window,
2779 event->x, event->y, &x, &y, GDK_BUTTON_RELEASE_MASK);
2781 event = (GdkEventButton *) gdk_event_copy ((GdkEvent *) event);
2785 /* Leave the widget if we've moved - This doesn't break selection,
2786 * but stops buttons from being clicked.
2788 if ((child != priv->child) || (priv->moved)) {
2789 /* Send synthetic leave event */
2790 synth_crossing (priv->child, x, y, event->x_root,
2791 event->y_root, event->time, FALSE);
2792 /* insure no click will happen for widgets that do not handle
2796 /* Send synthetic button release event */
2797 ((GdkEventAny *) event)->window = g_object_ref (priv->child);
2798 gdk_event_put ((GdkEvent *) event);
2800 /* Send synthetic button release event */
2801 ((GdkEventAny *) event)->window = g_object_ref (child);
2802 gdk_event_put ((GdkEvent *) event);
2803 /* Send synthetic leave event */
2804 synth_crossing (priv->child, x, y, event->x_root,
2805 event->y_root, event->time, FALSE);
2807 g_object_remove_weak_pointer ((GObject *) priv->child,
2808 (gpointer) & priv->child);
2810 priv->moved = FALSE;
2811 gdk_event_free ((GdkEvent *) event);
2816 /* utility event handler */
2818 hildon_pannable_area_scroll_cb (GtkWidget *widget,
2819 GdkEventScroll *event)
2821 GtkAdjustment *adj = NULL;
2822 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
2824 if ((!priv->enabled) ||
2825 (gtk_bin_get_child (GTK_BIN (widget)) == NULL))
2828 priv->scroll_indicator_event_interrupt = 0;
2829 priv->scroll_delay_counter = priv->scrollbar_fade_delay + 20;
2831 hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget), 1.0);
2833 /* Stop inertial scrolling */
2834 if (priv->idle_id) {
2837 priv->overshooting_x = 0;
2838 priv->overshooting_y = 0;
2840 if ((priv->overshot_dist_x>0)||(priv->overshot_dist_y>0)) {
2841 priv->overshot_dist_x = 0;
2842 priv->overshot_dist_y = 0;
2844 gtk_widget_queue_resize (GTK_WIDGET (widget));
2847 g_signal_emit (widget, pannable_area_signals[PANNING_FINISHED], 0);
2849 g_source_remove (priv->idle_id);
2853 if (event->direction == GDK_SCROLL_UP || event->direction == GDK_SCROLL_DOWN)
2854 adj = priv->vadjust;
2856 adj = priv->hadjust;
2860 gdouble delta, new_value;
2862 /* from gtkrange.c calculate delta*/
2863 delta = pow (adj->page_size, 2.0 / 3.0);
2865 if (event->direction == GDK_SCROLL_UP ||
2866 event->direction == GDK_SCROLL_LEFT)
2869 new_value = CLAMP (adj->value + delta, adj->lower, adj->upper - adj->page_size);
2871 gtk_adjustment_set_value (adj, new_value);
2878 hildon_pannable_area_child_mapped (GtkWidget *widget,
2882 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (user_data)->priv;
2884 if (priv->event_window != NULL && priv->enabled)
2885 gdk_window_raise (priv->event_window);
2889 hildon_pannable_area_add (GtkContainer *container, GtkWidget *child)
2891 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (container)->priv;
2893 g_return_if_fail (gtk_bin_get_child (GTK_BIN (container)) == NULL);
2895 gtk_widget_set_parent (child, GTK_WIDGET (container));
2896 GTK_BIN (container)->child = child;
2898 g_signal_connect_after (child, "map-event",
2899 G_CALLBACK (hildon_pannable_area_child_mapped),
2902 if (!gtk_widget_set_scroll_adjustments (child, priv->hadjust, priv->vadjust)) {
2903 g_warning ("%s: cannot add non scrollable widget, "
2904 "wrap it in a viewport", __FUNCTION__);
2908 /* call this function if you are not panning */
2910 hildon_pannable_area_center_on_child_focus (HildonPannableArea *area)
2912 GtkWidget *focused_child = NULL;
2913 GtkWidget *window = NULL;
2915 window = gtk_widget_get_toplevel (GTK_WIDGET (area));
2917 if (GTK_WIDGET_TOPLEVEL (window)) {
2918 focused_child = gtk_window_get_focus (GTK_WINDOW (window));
2921 if (focused_child) {
2922 hildon_pannable_area_scroll_to_child (area, focused_child);
2927 hildon_pannable_area_set_focus_child (GtkContainer *container,
2930 HildonPannableArea *area = HILDON_PANNABLE_AREA (container);
2932 if (!area->priv->center_on_child_focus) {
2936 if (GTK_IS_WIDGET (child)) {
2937 area->priv->center_on_child_focus_pending = TRUE;
2942 hildon_pannable_area_remove (GtkContainer *container, GtkWidget *child)
2944 g_return_if_fail (HILDON_IS_PANNABLE_AREA (container));
2945 g_return_if_fail (child != NULL);
2946 g_return_if_fail (gtk_bin_get_child (GTK_BIN (container)) == child);
2948 gtk_widget_set_scroll_adjustments (child, NULL, NULL);
2950 g_signal_handlers_disconnect_by_func (child,
2951 hildon_pannable_area_child_mapped,
2954 /* chain parent class handler to remove child */
2955 GTK_CONTAINER_CLASS (hildon_pannable_area_parent_class)->remove (container, child);
2959 * This method calculates a factor necessary to determine the initial distance
2960 * to jump in hildon_pannable_area_scroll_to(). For fixed time and frames per
2961 * second, we know in how many frames 'n' we need to reach the destination
2962 * point. We know that, for a distance d,
2964 * d = d_0 + d_1 + ... + d_n
2966 * where d_i is the distance travelled in the i-th frame and decel_factor is
2967 * the deceleration factor. This can be rewritten as
2969 * d = d_0 + (d_0 * decel_factor) + ... + (d_n-1 * decel_factor),
2971 * since the distance travelled on each frame is the distance travelled in the
2972 * previous frame reduced by the deceleration factor. Reducing this and
2973 * factoring d_0 out, we get
2975 * d = d_0 (1 + decel_factor + ... + decel_factor^(n-1)).
2977 * Since the sum is independent of the distance to be travelled, we can define
2980 * vel_factor = 1 + decel_factor + ... + decel_factor^(n-1).
2982 * That's the gem we calculate in this method.
2985 hildon_pannable_calculate_vel_factor (HildonPannableArea * self)
2987 HildonPannableAreaPrivate *priv = self->priv;
2992 n = ceil (priv->sps * priv->scroll_time);
2994 for (i = 1; i < n && fct_i >= RATIO_TOLERANCE; i++) {
2995 fct_i *= priv->decel;
2999 priv->vel_factor = fct;
3003 * hildon_pannable_area_new:
3005 * Create a new pannable area widget
3007 * Returns: the newly created #HildonPannableArea
3013 hildon_pannable_area_new (void)
3015 return g_object_new (HILDON_TYPE_PANNABLE_AREA, NULL);
3019 * hildon_pannable_area_new_full:
3020 * @mode: #HildonPannableAreaMode
3021 * @enabled: Value for the enabled property
3022 * @vel_min: Value for the velocity-min property
3023 * @vel_max: Value for the velocity-max property
3024 * @decel: Value for the deceleration property
3025 * @sps: Value for the sps property
3027 * Create a new #HildonPannableArea widget and set various properties
3029 * returns: the newly create #HildonPannableArea
3035 hildon_pannable_area_new_full (gint mode, gboolean enabled,
3036 gdouble vel_min, gdouble vel_max,
3037 gdouble decel, guint sps)
3039 return g_object_new (HILDON_TYPE_PANNABLE_AREA,
3042 "velocity_min", vel_min,
3043 "velocity_max", vel_max,
3044 "deceleration", decel, "sps", sps, NULL);
3048 * hildon_pannable_area_add_with_viewport:
3049 * @area: A #HildonPannableArea
3050 * @child: Child widget to add to the viewport
3052 * Convenience function used to add a child to a #GtkViewport, and add the
3053 * viewport to the scrolled window.
3059 hildon_pannable_area_add_with_viewport (HildonPannableArea * area,
3063 GtkWidget *viewport;
3065 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3066 g_return_if_fail (GTK_IS_WIDGET (child));
3067 g_return_if_fail (child->parent == NULL);
3069 bin = GTK_BIN (area);
3071 if (bin->child != NULL)
3073 g_return_if_fail (GTK_IS_VIEWPORT (bin->child));
3074 g_return_if_fail (GTK_BIN (bin->child)->child == NULL);
3076 viewport = bin->child;
3080 HildonPannableAreaPrivate *priv = area->priv;
3082 viewport = gtk_viewport_new (priv->hadjust,
3084 gtk_viewport_set_shadow_type (GTK_VIEWPORT (viewport), GTK_SHADOW_NONE);
3085 gtk_container_add (GTK_CONTAINER (area), viewport);
3088 gtk_widget_show (viewport);
3089 gtk_container_add (GTK_CONTAINER (viewport), child);
3093 * hildon_pannable_area_scroll_to:
3094 * @area: A #HildonPannableArea.
3095 * @x: The x coordinate of the destination point or -1 to ignore this axis.
3096 * @y: The y coordinate of the destination point or -1 to ignore this axis.
3098 * Smoothly scrolls @area to ensure that (@x, @y) is a visible point
3099 * on the widget. To move in only one coordinate, you must set the other one
3100 * to -1. Notice that, in %HILDON_PANNABLE_AREA_MODE_PUSH mode, this function
3101 * works just like hildon_pannable_area_jump_to().
3103 * This function is useful if you need to present the user with a particular
3104 * element inside a scrollable widget, like #GtkTreeView. For instance,
3105 * the following example shows how to scroll inside a #GtkTreeView to
3106 * make visible an item, indicated by the #GtkTreeIter @iter.
3110 * GtkTreePath *path;
3111 * GdkRectangle *rect;
3113 * path = gtk_tree_model_get_path (model, &iter);
3114 * gtk_tree_view_get_background_area (GTK_TREE_VIEW (treeview),
3115 * path, NULL, &rect);
3116 * gtk_tree_view_convert_bin_window_to_tree_coords (GTK_TREE_VIEW (treeview),
3117 * 0, rect.y, NULL, &y);
3118 * hildon_pannable_area_scroll_to (panarea, -1, y);
3119 * gtk_tree_path_free (path);
3123 * If you want to present a child widget in simpler scenarios,
3124 * use hildon_pannable_area_scroll_to_child() instead.
3126 * There is a precondition to this function: the widget must be
3127 * already realized. Check the hildon_pannable_area_jump_to_child() for
3128 * more tips regarding how to call this function during
3134 hildon_pannable_area_scroll_to (HildonPannableArea *area,
3135 const gint x, const gint y)
3137 HildonPannableAreaPrivate *priv;
3139 gint dist_x, dist_y;
3140 gboolean hscroll_visible, vscroll_visible;
3142 g_return_if_fail (GTK_WIDGET_REALIZED (area));
3143 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3147 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
3148 priv->vadjust->page_size);
3149 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
3150 priv->hadjust->page_size);
3152 if (((!vscroll_visible)&&(!hscroll_visible)) ||
3153 (x == -1 && y == -1)) {
3157 if (priv->mode == HILDON_PANNABLE_AREA_MODE_PUSH)
3158 hildon_pannable_area_jump_to (area, x, y);
3160 width = priv->hadjust->upper - priv->hadjust->lower;
3161 height = priv->vadjust->upper - priv->vadjust->lower;
3163 g_return_if_fail (x < width || y < height);
3165 if ((x > -1)&&(hscroll_visible)) {
3166 priv->scroll_to_x = x - priv->hadjust->page_size/2;
3167 dist_x = priv->scroll_to_x - priv->hadjust->value;
3169 priv->scroll_to_x = -1;
3171 priv->vel_x = - dist_x/priv->vel_factor;
3174 priv->scroll_to_x = -1;
3177 if ((y > -1)&&(vscroll_visible)) {
3178 priv->scroll_to_y = y - priv->vadjust->page_size/2;
3179 dist_y = priv->scroll_to_y - priv->vadjust->value;
3181 priv->scroll_to_y = -1;
3183 priv->vel_y = - dist_y/priv->vel_factor;
3186 priv->scroll_to_y = y;
3189 if ((priv->scroll_to_y == -1) && (priv->scroll_to_y == -1)) {
3193 hildon_pannable_area_launch_fade_timeout (area, 1.0);
3196 priv->idle_id = gdk_threads_add_timeout ((gint) (1000.0 / (gdouble) priv->sps),
3198 hildon_pannable_area_timeout, area);
3202 * hildon_pannable_area_jump_to:
3203 * @area: A #HildonPannableArea.
3204 * @x: The x coordinate of the destination point or -1 to ignore this axis.
3205 * @y: The y coordinate of the destination point or -1 to ignore this axis.
3207 * Jumps the position of @area to ensure that (@x, @y) is a visible
3208 * point in the widget. In order to move in only one coordinate, you
3209 * must set the other one to -1. See hildon_pannable_area_scroll_to()
3210 * function for an example of how to calculate the position of
3211 * children in scrollable widgets like #GtkTreeview.
3213 * There is a precondition to this function: the widget must be
3214 * already realized. Check the hildon_pannable_area_jump_to_child() for
3215 * more tips regarding how to call this function during
3221 hildon_pannable_area_jump_to (HildonPannableArea *area,
3222 const gint x, const gint y)
3224 HildonPannableAreaPrivate *priv;
3228 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3229 g_return_if_fail (GTK_WIDGET_REALIZED (area));
3230 g_return_if_fail (x >= -1 && y >= -1);
3232 if (x == -1 && y == -1) {
3238 width = priv->hadjust->upper - priv->hadjust->lower;
3239 height = priv->vadjust->upper - priv->vadjust->lower;
3241 g_return_if_fail (x < width || y < height);
3243 hv = priv->hadjust->value;
3244 vv = priv->vadjust->value;
3247 gdouble jump_to = x - priv->hadjust->page_size/2;
3249 priv->hadjust->value = CLAMP (jump_to,
3250 priv->hadjust->lower,
3251 priv->hadjust->upper -
3252 priv->hadjust->page_size);
3256 gdouble jump_to = y - priv->vadjust->page_size/2;
3258 priv->vadjust->value = CLAMP (jump_to,
3259 priv->vadjust->lower,
3260 priv->vadjust->upper -
3261 priv->vadjust->page_size);
3264 if (hv != priv->hadjust->value)
3265 gtk_adjustment_value_changed (priv->hadjust);
3267 if (vv != priv->vadjust->value)
3268 gtk_adjustment_value_changed (priv->vadjust);
3270 priv->scroll_indicator_alpha = 1.0;
3272 if (priv->scroll_indicator_timeout) {
3273 g_source_remove (priv->scroll_indicator_timeout);
3274 priv->scroll_indicator_timeout = 0;
3277 if (priv->idle_id) {
3280 priv->overshooting_x = 0;
3281 priv->overshooting_y = 0;
3283 if ((priv->overshot_dist_x>0)||(priv->overshot_dist_y>0)) {
3284 priv->overshot_dist_x = 0;
3285 priv->overshot_dist_y = 0;
3287 gtk_widget_queue_resize (GTK_WIDGET (area));
3290 g_signal_emit (area, pannable_area_signals[PANNING_FINISHED], 0);
3291 g_source_remove (priv->idle_id);
3297 * hildon_pannable_area_scroll_to_child:
3298 * @area: A #HildonPannableArea.
3299 * @child: A #GtkWidget, descendant of @area.
3301 * Smoothly scrolls until @child is visible inside @area. @child must
3302 * be a descendant of @area. If you need to scroll inside a scrollable
3303 * widget, e.g., #GtkTreeview, see hildon_pannable_area_scroll_to().
3305 * There is a precondition to this function: the widget must be
3306 * already realized. Check the hildon_pannable_area_jump_to_child() for
3307 * more tips regarding how to call this function during
3313 hildon_pannable_area_scroll_to_child (HildonPannableArea *area, GtkWidget *child)
3315 GtkWidget *bin_child;
3318 g_return_if_fail (GTK_WIDGET_REALIZED (area));
3319 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3320 g_return_if_fail (GTK_IS_WIDGET (child));
3321 g_return_if_fail (gtk_widget_is_ancestor (child, GTK_WIDGET (area)));
3323 if (GTK_BIN (area)->child == NULL)
3326 /* We need to get to check the child of the inside the area */
3327 bin_child = GTK_BIN (area)->child;
3329 /* we check if we added a viewport */
3330 if (GTK_IS_VIEWPORT (bin_child)) {
3331 bin_child = GTK_BIN (bin_child)->child;
3334 if (gtk_widget_translate_coordinates (child, bin_child, 0, 0, &x, &y))
3335 hildon_pannable_area_scroll_to (area, x, y);
3339 * hildon_pannable_area_jump_to_child:
3340 * @area: A #HildonPannableArea.
3341 * @child: A #GtkWidget, descendant of @area.
3343 * Jumps to make sure @child is visible inside @area. @child must
3344 * be a descendant of @area. If you want to move inside a scrollable
3345 * widget, like, #GtkTreeview, see hildon_pannable_area_scroll_to().
3347 * There is a precondition to this function: the widget must be
3348 * already realized. You can control if the widget is ready with the
3349 * GTK_WIDGET_REALIZED macro. If you want to call this function during
3350 * the initialization process of the widget do it inside a callback to
3351 * the ::realize signal, using g_signal_connect_after() function.
3356 hildon_pannable_area_jump_to_child (HildonPannableArea *area, GtkWidget *child)
3358 GtkWidget *bin_child;
3361 g_return_if_fail (GTK_WIDGET_REALIZED (area));
3362 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3363 g_return_if_fail (GTK_IS_WIDGET (child));
3364 g_return_if_fail (gtk_widget_is_ancestor (child, GTK_WIDGET (area)));
3366 if (gtk_bin_get_child (GTK_BIN (area)) == NULL)
3369 /* We need to get to check the child of the inside the area */
3370 bin_child = gtk_bin_get_child (GTK_BIN (area));
3372 /* we check if we added a viewport */
3373 if (GTK_IS_VIEWPORT (bin_child)) {
3374 bin_child = gtk_bin_get_child (GTK_BIN (bin_child));
3377 if (gtk_widget_translate_coordinates (child, bin_child, 0, 0, &x, &y))
3378 hildon_pannable_area_jump_to (area, x, y);
3382 * hildon_pannable_get_child_widget_at:
3383 * @area: A #HildonPannableArea.
3384 * @x: horizontal coordinate of the point
3385 * @y: vertical coordinate of the point
3387 * Get the widget at the point (x, y) inside the pannable area. In
3388 * case no widget found it returns NULL.
3390 * returns: the #GtkWidget if we find a widget, NULL in any other case
3395 hildon_pannable_get_child_widget_at (HildonPannableArea *area,
3396 gdouble x, gdouble y)
3398 GdkWindow *window = NULL;
3399 GtkWidget *child_widget = NULL;
3401 window = hildon_pannable_area_get_topmost
3402 (gtk_bin_get_child (GTK_BIN (area))->window,
3403 x, y, NULL, NULL, GDK_ALL_EVENTS_MASK);
3405 gdk_window_get_user_data (window, (gpointer) &child_widget);
3407 return child_widget;
3412 * hildon_pannable_area_get_hadjustment:
3413 * @area: A #HildonPannableArea.
3415 * Returns the horizontal adjustment. This adjustment is the internal
3416 * widget adjustment used to control the animations. Do not modify it
3417 * directly to change the position of the pannable, to do that use the
3418 * pannable API. If you modify the object directly it could cause
3419 * artifacts in the animations.
3421 * returns: The horizontal #GtkAdjustment
3426 hildon_pannable_area_get_hadjustment (HildonPannableArea *area)
3429 g_return_val_if_fail (HILDON_IS_PANNABLE_AREA (area), NULL);
3431 return area->priv->hadjust;
3435 * hildon_pannable_area_get_vadjustment:
3436 * @area: A #HildonPannableArea.
3438 * Returns the vertical adjustment. This adjustment is the internal
3439 * widget adjustment used to control the animations. Do not modify it
3440 * directly to change the position of the pannable, to do that use the
3441 * pannable API. If you modify the object directly it could cause
3442 * artifacts in the animations.
3444 * returns: The vertical #GtkAdjustment
3449 hildon_pannable_area_get_vadjustment (HildonPannableArea *area)
3451 g_return_val_if_fail (HILDON_IS_PANNABLE_AREA (area), NULL);
3453 return area->priv->vadjust;
3458 * hildon_pannable_area_get_size_request_policy:
3459 * @area: A #HildonPannableArea.
3461 * This function returns the current size request policy of the
3462 * widget. That policy controls the way the size_request is done in
3463 * the pannable area. Check
3464 * hildon_pannable_area_set_size_request_policy() for a more detailed
3467 * returns: the policy is currently being used in the widget
3468 * #HildonSizeRequestPolicy.
3472 HildonSizeRequestPolicy
3473 hildon_pannable_area_get_size_request_policy (HildonPannableArea *area)
3475 HildonPannableAreaPrivate *priv;
3477 g_return_val_if_fail (HILDON_IS_PANNABLE_AREA (area), FALSE);
3481 return priv->size_request_policy;
3485 * hildon_pannable_area_set_size_request_policy:
3486 * @area: A #HildonPannableArea.
3487 * @size_request_policy: One of the allowed #HildonSizeRequestPolicy
3489 * This function sets the pannable area size request policy. That
3490 * policy controls the way the size_request is done in the pannable
3491 * area. Pannable can use the size request of its children
3492 * (#HILDON_SIZE_REQUEST_CHILDREN) or the minimum size required for
3493 * the area itself (#HILDON_SIZE_REQUEST_MINIMUM), the latter is the
3494 * default. Recall this size depends on the scrolling policy you are
3495 * requesting to the pannable area, if you set #GTK_POLICY_NEVER this
3496 * parameter will not have any effect with
3497 * #HILDON_SIZE_REQUEST_MINIMUM set.
3501 * Deprecated: This method and the policy request is deprecated, DO
3502 * NOT use it in future code, the only policy properly supported in
3503 * gtk+ nowadays is the minimum size. Use #gtk_window_set_default_size
3504 * or #gtk_window_set_geometry_hints with the proper size in your case
3505 * to define the height of your dialogs.
3508 hildon_pannable_area_set_size_request_policy (HildonPannableArea *area,
3509 HildonSizeRequestPolicy size_request_policy)
3511 HildonPannableAreaPrivate *priv;
3513 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3517 if (priv->size_request_policy == size_request_policy)
3520 priv->size_request_policy = size_request_policy;
3522 gtk_widget_queue_resize (GTK_WIDGET (area));
3524 g_object_notify (G_OBJECT (area), "size-request-policy");
3528 * hildon_pannable_area_get_center_on_child_focus
3529 * @area: A #HildonPannableArea
3531 * Gets the @area #HildonPannableArea:center-on-child-focus property
3534 * See #HildonPannableArea:center-on-child-focus for more information.
3536 * Returns: the @area #HildonPannableArea:center-on-child-focus value
3541 hildon_pannable_area_get_center_on_child_focus (HildonPannableArea *area)
3543 g_return_val_if_fail (HILDON_IS_PANNABLE_AREA (area), FALSE);
3545 return area->priv->center_on_child_focus;
3549 * hildon_pannable_area_set_center_on_child_focus
3550 * @area: A #HildonPannableArea
3551 * @value: the new value
3553 * Sets the @area #HildonPannableArea:center-on-child-focus property
3556 * See #HildonPannableArea:center-on-child-focus for more information.
3561 hildon_pannable_area_set_center_on_child_focus (HildonPannableArea *area,
3564 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3566 area->priv->center_on_child_focus = value;