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_check_scrollbars (HILDON_PANNABLE_AREA (widget));
1228 hildon_pannable_area_child_allocate_calculate (widget,
1232 gtk_widget_size_allocate (child, &child_allocation);
1234 if (hildon_pannable_area_check_scrollbars (HILDON_PANNABLE_AREA (widget))) {
1235 hildon_pannable_area_child_allocate_calculate (widget,
1239 gtk_widget_size_allocate (child, &child_allocation);
1242 hv = priv->hadjust->value;
1243 vv = priv->vadjust->value;
1245 /* we have to do this after child size_allocate because page_size is
1246 * changed when we allocate the size of the children */
1247 if (priv->overshot_dist_y < 0) {
1248 priv->vadjust->value = priv->vadjust->upper - priv->vadjust->page_size;
1251 if (priv->overshot_dist_x < 0) {
1252 priv->hadjust->value = priv->hadjust->upper - priv->hadjust->page_size;
1255 if (hv != priv->hadjust->value)
1256 gtk_adjustment_value_changed (priv->hadjust);
1258 if (vv != priv->vadjust->value)
1259 gtk_adjustment_value_changed (priv->vadjust);
1262 hildon_pannable_area_check_scrollbars (HILDON_PANNABLE_AREA (widget));
1267 hildon_pannable_area_style_set (GtkWidget * widget,
1268 GtkStyle * previous_style)
1270 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1272 GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->
1273 style_set (widget, previous_style);
1275 gtk_style_lookup_color (widget->style, "SecondaryTextColor", &priv->scroll_color);
1276 gtk_widget_style_get (widget, "indicator-width", &priv->indicator_width, NULL);
1280 hildon_pannable_area_map (GtkWidget * widget)
1282 HildonPannableAreaPrivate *priv;
1284 priv = HILDON_PANNABLE_AREA (widget)->priv;
1286 gdk_window_show (widget->window);
1288 if (priv->event_window != NULL && !priv->enabled)
1289 gdk_window_show (priv->event_window);
1291 (*GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->map) (widget);
1293 if (priv->event_window != NULL && priv->enabled)
1294 gdk_window_show (priv->event_window);
1298 hildon_pannable_area_unmap (GtkWidget * widget)
1300 HildonPannableAreaPrivate *priv;
1302 priv = HILDON_PANNABLE_AREA (widget)->priv;
1304 if (priv->event_window != NULL)
1305 gdk_window_hide (priv->event_window);
1307 gdk_window_hide (widget->window);
1309 (*GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->unmap) (widget);
1313 hildon_pannable_area_grab_notify (GtkWidget *widget,
1314 gboolean was_grabbed,
1317 /* an internal widget has grabbed the focus and now has returned it,
1318 we have to do some release actions */
1320 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1322 priv->scroll_indicator_event_interrupt = 0;
1324 if ((!priv->scroll_indicator_timeout)&&(priv->scroll_indicator_alpha)>0.1) {
1325 priv->scroll_delay_counter = priv->scrollbar_fade_delay;
1327 hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget),
1328 priv->scroll_indicator_alpha);
1331 priv->last_type = 3;
1332 priv->moved = FALSE;
1336 #if USE_CAIRO_SCROLLBARS == 1
1339 rgb_from_gdkcolor (GdkColor *color, gdouble *r, gdouble *g, gdouble *b)
1341 *r = (color->red >> 8) / 255.0;
1342 *g = (color->green >> 8) / 255.0;
1343 *b = (color->blue >> 8) / 255.0;
1347 hildon_pannable_draw_vscroll (GtkWidget * widget,
1348 GdkColor *back_color,
1349 GdkColor *scroll_color)
1351 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1354 cairo_pattern_t *pattern;
1356 gint radius = (priv->vscroll_rect.width/2) - 1;
1358 cr = gdk_cairo_create(widget->window);
1360 /* Draw the background */
1361 rgb_from_gdkcolor (back_color, &r, &g, &b);
1362 cairo_set_source_rgb (cr, r, g, b);
1363 cairo_rectangle(cr, priv->vscroll_rect.x, priv->vscroll_rect.y,
1364 priv->vscroll_rect.width,
1365 priv->vscroll_rect.height);
1366 cairo_fill_preserve (cr);
1369 /* Calculate the scroll bar height and position */
1370 y = ((priv->vadjust->value - priv->vadjust->lower) / (priv->vadjust->upper - priv->vadjust->lower)) *
1371 (widget->allocation.height -
1372 (priv->hscroll_visible ? priv->indicator_width : 0));
1373 height = ((((priv->vadjust->value - priv->vadjust->lower) +
1374 priv->vadjust->page_size) /
1375 (priv->vadjust->upper - priv->vadjust->lower)) *
1376 (widget->allocation.height -
1377 (priv->hscroll_visible ? priv->indicator_width : 0))) - y;
1379 /* Set a minimum height */
1380 height = MAX (SCROLL_BAR_MIN_SIZE, height);
1382 /* Check the max y position */
1383 y = MIN (y, widget->allocation.height -
1384 (priv->hscroll_visible ? priv->hscroll_rect.height : 0) -
1387 /* Draw the scrollbar */
1388 rgb_from_gdkcolor (scroll_color, &r, &g, &b);
1390 pattern = cairo_pattern_create_linear(radius+1, y, radius+1,y + height);
1391 cairo_pattern_add_color_stop_rgb(pattern, 0, r, g, b);
1392 cairo_pattern_add_color_stop_rgb(pattern, 1, r/2, g/2, b/2);
1393 cairo_set_source(cr, pattern);
1395 cairo_pattern_destroy(pattern);
1397 cairo_arc(cr, priv->vscroll_rect.x + radius + 1, y + radius + 1, radius, G_PI, 0);
1398 cairo_line_to(cr, priv->vscroll_rect.x + (radius * 2) + 1, y + height - radius);
1399 cairo_arc(cr, priv->vscroll_rect.x + radius + 1, y + height - radius, radius, 0, G_PI);
1400 cairo_line_to(cr, priv->vscroll_rect.x + 1, y + height - radius);
1403 cairo_paint_with_alpha(cr, priv->scroll_indicator_alpha);
1409 hildon_pannable_draw_hscroll (GtkWidget * widget,
1410 GdkColor *back_color,
1411 GdkColor *scroll_color)
1413 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1416 cairo_pattern_t *pattern;
1418 gint radius = (priv->hscroll_rect.height/2) - 1;
1420 cr = gdk_cairo_create(widget->window);
1422 /* Draw the background */
1423 rgb_from_gdkcolor (back_color, &r, &g, &b);
1424 cairo_set_source_rgb (cr, r, g, b);
1425 cairo_rectangle(cr, priv->hscroll_rect.x, priv->hscroll_rect.y,
1426 priv->hscroll_rect.width,
1427 priv->hscroll_rect.height);
1428 cairo_fill_preserve (cr);
1431 /* calculate the scrollbar width and position */
1432 x = ((priv->hadjust->value - priv->hadjust->lower) / (priv->hadjust->upper - priv->hadjust->lower)) *
1433 (widget->allocation.width - (priv->vscroll_visible ? priv->indicator_width : 0));
1434 width =((((priv->hadjust->value - priv->hadjust->lower) +
1435 priv->hadjust->page_size) / (priv->hadjust->upper - priv->hadjust->lower)) *
1436 (widget->allocation.width -
1437 (priv->vscroll_visible ? priv->indicator_width : 0))) - x;
1439 /* Set a minimum width */
1440 width = MAX (SCROLL_BAR_MIN_SIZE, width);
1442 /* Check the max x position */
1443 x = MIN (x, widget->allocation.width -
1444 (priv->vscroll_visible ? priv->vscroll_rect.width : 0) -
1447 /* Draw the scrollbar */
1448 rgb_from_gdkcolor (scroll_color, &r, &g, &b);
1450 pattern = cairo_pattern_create_linear(x, radius+1, x+width, radius+1);
1451 cairo_pattern_add_color_stop_rgb(pattern, 0, r, g, b);
1452 cairo_pattern_add_color_stop_rgb(pattern, 1, r/2, g/2, b/2);
1453 cairo_set_source(cr, pattern);
1455 cairo_pattern_destroy(pattern);
1457 cairo_arc_negative(cr, x + radius + 1, priv->hscroll_rect.y + radius + 1, radius, 3*G_PI_2, G_PI_2);
1458 cairo_line_to(cr, x + width - radius, priv->hscroll_rect.y + (radius * 2) + 1);
1459 cairo_arc_negative(cr, x + width - radius, priv->hscroll_rect.y + radius + 1, radius, G_PI_2, 3*G_PI_2);
1460 cairo_line_to(cr, x + width - radius, priv->hscroll_rect.y + 1);
1463 cairo_paint_with_alpha(cr, priv->scroll_indicator_alpha);
1468 #else /* USE_CAIRO_SCROLLBARS */
1471 tranparency_color (GdkColor *color,
1474 gdouble transparency)
1478 diff = colora.red - colorb.red;
1479 color->red = colora.red-diff*transparency;
1481 diff = colora.green - colorb.green;
1482 color->green = colora.green-diff*transparency;
1484 diff = colora.blue - colorb.blue;
1485 color->blue = colora.blue-diff*transparency;
1489 hildon_pannable_draw_vscroll (GtkWidget *widget,
1490 GdkColor *back_color,
1491 GdkColor *scroll_color)
1493 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1495 GdkColor transp_color;
1496 GdkGC *gc = priv->scrollbars_gc;
1498 gdk_draw_rectangle (widget->window,
1499 widget->style->bg_gc[GTK_STATE_NORMAL],
1501 priv->vscroll_rect.x, priv->vscroll_rect.y,
1502 priv->vscroll_rect.width,
1503 priv->vscroll_rect.height);
1505 y = ((priv->vadjust->value - priv->vadjust->lower) / (priv->vadjust->upper - priv->vadjust->lower)) *
1506 (widget->allocation.height - (priv->hscroll_visible ? priv->indicator_width : 0));
1507 height = ((((priv->vadjust->value - priv->vadjust->lower) + priv->vadjust->page_size) /
1508 (priv->vadjust->upper - priv->vadjust->lower)) *
1509 (widget->allocation.height -
1510 (priv->hscroll_visible ? priv->indicator_width : 0))) - y;
1512 /* Set a minimum height */
1513 height = MAX (SCROLL_BAR_MIN_SIZE, height);
1515 /* Check the max y position */
1516 y = MIN (y, widget->allocation.height -
1517 (priv->hscroll_visible ? priv->hscroll_rect.height : 0) -
1520 if (priv->scroll_indicator_alpha == 1.0) {
1521 transp_color = priv->scroll_color;
1522 } else if (priv->scroll_indicator_alpha < 1.0) {
1523 tranparency_color (&transp_color, *back_color, *scroll_color,
1524 priv->scroll_indicator_alpha);
1526 gdk_gc_set_rgb_fg_color (gc, &transp_color);
1528 gdk_draw_rectangle (widget->window, gc,
1529 TRUE, priv->vscroll_rect.x, y,
1530 priv->vscroll_rect.width, height);
1534 hildon_pannable_draw_hscroll (GtkWidget *widget,
1535 GdkColor *back_color,
1536 GdkColor *scroll_color)
1538 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1540 GdkColor transp_color;
1541 GdkGC *gc = priv->scrollbars_gc;
1543 gdk_draw_rectangle (widget->window,
1544 widget->style->bg_gc[GTK_STATE_INSENSITIVE],
1546 priv->hscroll_rect.x, priv->hscroll_rect.y,
1547 priv->hscroll_rect.width,
1548 priv->hscroll_rect.height);
1550 /* calculate the scrollbar width and position */
1551 x = ((priv->hadjust->value - priv->hadjust->lower) / (priv->hadjust->upper - priv->hadjust->lower)) *
1552 (widget->allocation.width - (priv->vscroll_visible ? priv->indicator_width : 0));
1553 width =((((priv->hadjust->value - priv->hadjust->lower) +
1554 priv->hadjust->page_size) / (priv->hadjust->upper - priv->hadjust->lower)) *
1555 (widget->allocation.width -
1556 (priv->vscroll_visible ? priv->indicator_width : 0))) - x;
1558 /* Set a minimum width */
1559 width = MAX (SCROLL_BAR_MIN_SIZE, width);
1561 /* Check the max x position */
1562 x = MIN (x, widget->allocation.width -
1563 (priv->vscroll_visible ? priv->vscroll_rect.width : 0) -
1566 if (priv->scroll_indicator_alpha == 1.0) {
1567 transp_color = priv->scroll_color;
1568 } else if (priv->scroll_indicator_alpha < 1.0) {
1569 tranparency_color (&transp_color, *back_color, *scroll_color,
1570 priv->scroll_indicator_alpha);
1572 gdk_gc_set_rgb_fg_color (gc, &transp_color);
1574 gdk_draw_rectangle (widget->window, gc,
1575 TRUE, x, priv->hscroll_rect.y, width,
1576 priv->hscroll_rect.height);
1579 #endif /* USE_CAIRO_SCROLLBARS */
1582 hildon_pannable_area_initial_effect (GtkWidget * widget)
1584 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1586 if (priv->initial_hint) {
1587 if (priv->vscroll_visible || priv->hscroll_visible) {
1589 priv->scroll_indicator_event_interrupt = 0;
1590 priv->scroll_delay_counter = priv->scrollbar_fade_delay;
1592 hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget), 1.0);
1598 hildon_pannable_area_launch_fade_timeout (HildonPannableArea * area,
1601 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (area)->priv;
1603 priv->scroll_indicator_alpha = alpha;
1605 if (!priv->scroll_indicator_timeout)
1606 priv->scroll_indicator_timeout =
1607 gdk_threads_add_timeout (SCROLL_FADE_TIMEOUT,
1608 (GSourceFunc) hildon_pannable_area_scroll_indicator_fade,
1613 hildon_pannable_area_adjust_changed (HildonPannableArea * area,
1616 if (GTK_WIDGET_REALIZED (area))
1617 hildon_pannable_area_refresh (area);
1621 hildon_pannable_area_adjust_value_changed (HildonPannableArea * area,
1624 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (area)->priv;
1626 gint x = priv->x_offset;
1627 gint y = priv->y_offset;
1629 priv->x_offset = priv->hadjust->value;
1630 xdiff = x - priv->x_offset;
1631 priv->y_offset = priv->vadjust->value;
1632 ydiff = y - priv->y_offset;
1634 if ((xdiff || ydiff) && GTK_WIDGET_DRAWABLE (area)) {
1635 hildon_pannable_area_redraw (area);
1637 if ((priv->vscroll_visible) || (priv->hscroll_visible)) {
1638 priv->scroll_indicator_event_interrupt = 0;
1639 priv->scroll_delay_counter = priv->scrollbar_fade_delay;
1641 hildon_pannable_area_launch_fade_timeout (area, 1.0);
1647 hildon_pannable_area_redraw (HildonPannableArea * area)
1649 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (area)->priv;
1651 /* Redraw scroll indicators */
1652 if (GTK_WIDGET_DRAWABLE (area)) {
1653 if (priv->hscroll_visible) {
1654 gdk_window_invalidate_rect (GTK_WIDGET (area)->window,
1655 &priv->hscroll_rect, FALSE);
1658 if (priv->vscroll_visible) {
1659 gdk_window_invalidate_rect (GTK_WIDGET (area)->window,
1660 &priv->vscroll_rect, FALSE);
1666 hildon_pannable_area_scroll_indicator_fade(HildonPannableArea * area)
1668 HildonPannableAreaPrivate *priv = area->priv;
1670 /* if moving do not fade out */
1671 if (((ABS (priv->vel_y)>priv->vmin)||
1672 (ABS (priv->vel_x)>priv->vmin))&&(!priv->button_pressed)) {
1677 if (priv->scroll_indicator_event_interrupt) {
1678 /* Stop a fade out, and fade back in */
1679 if (priv->scroll_indicator_alpha > 0.9) {
1680 priv->scroll_indicator_alpha = 1.0;
1681 priv->scroll_indicator_timeout = 0;
1685 priv->scroll_indicator_alpha += 0.2;
1686 hildon_pannable_area_redraw (area);
1692 if ((priv->scroll_indicator_alpha > 0.9) &&
1693 (priv->scroll_delay_counter > 0)) {
1694 priv->scroll_delay_counter--;
1699 if (!priv->scroll_indicator_event_interrupt) {
1700 /* Continue fade out */
1701 if (priv->scroll_indicator_alpha < 0.1) {
1702 priv->scroll_indicator_timeout = 0;
1703 priv->scroll_indicator_alpha = 0.0;
1707 priv->scroll_indicator_alpha -= 0.2;
1708 hildon_pannable_area_redraw (area);
1718 hildon_pannable_area_expose_event (GtkWidget * widget,
1719 GdkEventExpose * event)
1722 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1723 #if USE_CAIRO_SCROLLBARS == 1
1724 GdkColor back_color = widget->style->bg[GTK_STATE_NORMAL];
1725 GdkColor scroll_color = widget->style->base[GTK_STATE_SELECTED];
1726 #else /* USE_CAIRO_SCROLLBARS */
1727 GdkColor back_color = widget->style->bg[GTK_STATE_NORMAL];
1728 GdkColor scroll_color = priv->scroll_color;
1731 if (G_UNLIKELY (priv->initial_effect)) {
1732 hildon_pannable_area_initial_effect (widget);
1734 priv->initial_effect = FALSE;
1737 if (gtk_bin_get_child (GTK_BIN (widget))) {
1739 if (priv->scroll_indicator_alpha > 0.1) {
1740 if (priv->vscroll_visible) {
1741 hildon_pannable_draw_vscroll (widget, &back_color, &scroll_color);
1743 if (priv->hscroll_visible) {
1744 hildon_pannable_draw_hscroll (widget, &back_color, &scroll_color);
1748 /* draw overshooting rectangles */
1749 if (priv->overshot_dist_y > 0) {
1750 gint overshot_height;
1752 overshot_height = MIN (priv->overshot_dist_y, widget->allocation.height -
1753 (priv->hscroll_visible ? priv->hscroll_rect.height : 0));
1755 gdk_draw_rectangle (widget->window,
1756 widget->style->bg_gc[GTK_STATE_NORMAL],
1760 widget->allocation.width -
1761 (priv->vscroll_visible ? priv->vscroll_rect.width : 0),
1763 } else if (priv->overshot_dist_y < 0) {
1764 gint overshot_height;
1768 MAX (priv->overshot_dist_y,
1769 -(widget->allocation.height -
1770 (priv->hscroll_visible ? priv->hscroll_rect.height : 0)));
1772 overshot_y = MAX (widget->allocation.height +
1774 (priv->hscroll_visible ? priv->hscroll_rect.height : 0), 0);
1776 gdk_draw_rectangle (widget->window,
1777 widget->style->bg_gc[GTK_STATE_NORMAL],
1781 widget->allocation.width -
1782 priv->vscroll_rect.width,
1786 if (priv->overshot_dist_x > 0) {
1787 gint overshot_width;
1789 overshot_width = MIN (priv->overshot_dist_x, widget->allocation.width -
1790 (priv->vscroll_visible ? priv->vscroll_rect.width : 0));
1792 gdk_draw_rectangle (widget->window,
1793 widget->style->bg_gc[GTK_STATE_NORMAL],
1798 widget->allocation.height -
1799 (priv->hscroll_visible ? priv->hscroll_rect.height : 0));
1800 } else if (priv->overshot_dist_x < 0) {
1801 gint overshot_width;
1805 MAX (priv->overshot_dist_x,
1806 -(widget->allocation.width -
1807 (priv->vscroll_visible ? priv->vscroll_rect.width : 0)));
1809 overshot_x = MAX (widget->allocation.width +
1811 (priv->vscroll_visible ? priv->vscroll_rect.width : 0), 0);
1813 gdk_draw_rectangle (widget->window,
1814 widget->style->bg_gc[GTK_STATE_NORMAL],
1819 widget->allocation.height -
1820 priv->hscroll_rect.height);
1825 return GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->expose_event (widget, event);
1829 hildon_pannable_area_get_topmost (GdkWindow * window,
1831 gint * tx, gint * ty,
1834 /* Find the GdkWindow at the given point, by recursing from a given
1835 * parent GdkWindow. Optionally return the co-ordinates transformed
1836 * relative to the child window.
1839 GList *c, *children;
1840 GdkWindow *selected_window = NULL;
1842 gdk_drawable_get_size (GDK_DRAWABLE (window), &width, &height);
1843 if ((x < 0) || (x >= width) || (y < 0) || (y >= height))
1846 children = gdk_window_peek_children (window);
1853 selected_window = window;
1856 for (c = children; c; c = c->next) {
1857 GdkWindow *child = (GdkWindow *) c->data;
1860 gdk_drawable_get_size (GDK_DRAWABLE (child), &width, &height);
1861 gdk_window_get_position (child, &wx, &wy);
1863 if ((x >= wx) && (x < (wx + width)) && (y >= wy) && (y < (wy + height)) &&
1864 (gdk_window_is_visible (child))) {
1866 if (gdk_window_peek_children (child)) {
1867 selected_window = hildon_pannable_area_get_topmost (child, x-wx, y-wy,
1869 if (!selected_window) {
1874 selected_window = child;
1877 if ((gdk_window_get_events (child)&mask)) {
1882 selected_window = child;
1888 return selected_window;
1892 synth_crossing (GdkWindow * child,
1894 gint x_root, gint y_root,
1895 guint32 time, gboolean in)
1897 GdkEventCrossing *crossing_event;
1898 GdkEventType type = in ? GDK_ENTER_NOTIFY : GDK_LEAVE_NOTIFY;
1900 /* Send synthetic enter event */
1901 crossing_event = (GdkEventCrossing *) gdk_event_new (type);
1902 ((GdkEventAny *) crossing_event)->type = type;
1903 ((GdkEventAny *) crossing_event)->window = g_object_ref (child);
1904 ((GdkEventAny *) crossing_event)->send_event = FALSE;
1905 crossing_event->subwindow = g_object_ref (child);
1906 crossing_event->time = time;
1907 crossing_event->x = x;
1908 crossing_event->y = y;
1909 crossing_event->x_root = x_root;
1910 crossing_event->y_root = y_root;
1911 crossing_event->mode = GDK_CROSSING_NORMAL;
1912 crossing_event->detail = GDK_NOTIFY_UNKNOWN;
1913 crossing_event->focus = FALSE;
1914 crossing_event->state = 0;
1915 gdk_event_put ((GdkEvent *) crossing_event);
1916 gdk_event_free ((GdkEvent *) crossing_event);
1920 hildon_pannable_area_button_press_cb (GtkWidget * widget,
1921 GdkEventButton * event)
1924 HildonPannableArea *area = HILDON_PANNABLE_AREA (widget);
1925 HildonPannableAreaPrivate *priv = area->priv;
1927 if ((!priv->enabled) || (event->button != 1) ||
1928 ((event->time == priv->last_time) &&
1929 (priv->last_type == 1)) || (gtk_bin_get_child (GTK_BIN (widget)) == NULL))
1932 priv->scroll_indicator_event_interrupt = 1;
1934 hildon_pannable_area_launch_fade_timeout (area,
1935 priv->scroll_indicator_alpha);
1937 priv->last_time = event->time;
1938 priv->last_type = 1;
1940 priv->scroll_to_x = -1;
1941 priv->scroll_to_y = -1;
1943 if (priv->button_pressed && priv->child) {
1944 /* Widget stole focus on last click, send crossing-out event */
1945 synth_crossing (priv->child, 0, 0, event->x_root, event->y_root,
1946 event->time, FALSE);
1954 /* Don't allow a click if we're still moving fast */
1955 if ((ABS (priv->vel_x) <= (priv->vmax * priv->vfast_factor)) &&
1956 (ABS (priv->vel_y) <= (priv->vmax * priv->vfast_factor)))
1958 hildon_pannable_area_get_topmost (gtk_bin_get_child (GTK_BIN (widget))->window,
1959 event->x, event->y, &x, &y, GDK_BUTTON_PRESS_MASK);
1963 priv->button_pressed = TRUE;
1965 /* Stop scrolling on mouse-down (so you can flick, then hold to stop) */
1968 if (priv->idle_id) {
1969 g_source_remove (priv->idle_id);
1971 g_signal_emit (area, pannable_area_signals[PANNING_FINISHED], 0);
1976 gdk_drawable_get_size (priv->child, &priv->child_width,
1977 &priv->child_height);
1978 priv->last_in = TRUE;
1980 g_object_add_weak_pointer ((GObject *) priv->child,
1981 (gpointer) & priv->child);
1983 event = (GdkEventButton *) gdk_event_copy ((GdkEvent *) event);
1984 /* remove the reference we added with the copy */
1985 g_object_unref (priv->event_window);
1991 synth_crossing (priv->child, x, y, event->x_root,
1992 event->y_root, event->time, TRUE);
1994 /* Send synthetic click (button press/release) event */
1995 ((GdkEventAny *) event)->window = g_object_ref (priv->child);
1997 gdk_event_put ((GdkEvent *) event);
1998 gdk_event_free ((GdkEvent *) event);
2006 hildon_pannable_area_check_scrollbars (HildonPannableArea * area)
2008 HildonPannableAreaPrivate *priv = area->priv;
2009 gboolean prev_hscroll_visible, prev_vscroll_visible;
2011 prev_hscroll_visible = priv->hscroll_visible;
2012 prev_vscroll_visible = priv->vscroll_visible;
2014 if (!gtk_bin_get_child (GTK_BIN (area))) {
2015 priv->vscroll_visible = FALSE;
2016 priv->hscroll_visible = FALSE;
2018 switch (priv->hscrollbar_policy) {
2019 case GTK_POLICY_ALWAYS:
2020 priv->hscroll_visible = TRUE;
2022 case GTK_POLICY_NEVER:
2023 priv->hscroll_visible = FALSE;
2026 priv->hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
2027 priv->hadjust->page_size);
2030 switch (priv->vscrollbar_policy) {
2031 case GTK_POLICY_ALWAYS:
2032 priv->vscroll_visible = TRUE;
2034 case GTK_POLICY_NEVER:
2035 priv->vscroll_visible = FALSE;
2038 priv->vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
2039 priv->vadjust->page_size);
2042 /* Store the vscroll/hscroll areas for redrawing */
2043 if (priv->vscroll_visible) {
2044 GtkAllocation *allocation = >K_WIDGET (area)->allocation;
2045 priv->vscroll_rect.x = allocation->width - priv->indicator_width;
2046 priv->vscroll_rect.y = 0;
2047 priv->vscroll_rect.width = priv->indicator_width;
2048 priv->vscroll_rect.height = allocation->height -
2049 (priv->hscroll_visible ? priv->indicator_width : 0);
2051 if (priv->hscroll_visible) {
2052 GtkAllocation *allocation = >K_WIDGET (area)->allocation;
2053 priv->hscroll_rect.y = allocation->height - priv->indicator_width;
2054 priv->hscroll_rect.x = 0;
2055 priv->hscroll_rect.height = priv->indicator_width;
2056 priv->hscroll_rect.width = allocation->width -
2057 (priv->vscroll_visible ? priv->indicator_width : 0);
2061 return ((priv->hscroll_visible != prev_hscroll_visible) ||
2062 (priv->vscroll_visible != prev_vscroll_visible));
2066 hildon_pannable_area_refresh (HildonPannableArea * area)
2068 if (GTK_WIDGET_DRAWABLE (area) &&
2069 hildon_pannable_area_check_scrollbars (area)) {
2070 HildonPannableAreaPrivate *priv = area->priv;
2072 gtk_widget_queue_resize (GTK_WIDGET (area));
2074 if ((priv->vscroll_visible) || (priv->hscroll_visible)) {
2075 priv->scroll_indicator_event_interrupt = 0;
2076 priv->scroll_delay_counter = area->priv->scrollbar_fade_delay;
2078 hildon_pannable_area_launch_fade_timeout (area, 1.0);
2081 hildon_pannable_area_redraw (area);
2085 /* Scroll by a particular amount (in pixels). Optionally, return if
2086 * the scroll on a particular axis was successful.
2089 hildon_pannable_axis_scroll (HildonPannableArea *area,
2090 GtkAdjustment *adjust,
2094 gint *overshot_dist,
2100 HildonPannableAreaPrivate *priv = area->priv;
2102 dist = gtk_adjustment_get_value (adjust) - inc;
2105 * We use overshot_dist to define the distance of the current overshoot,
2106 * and overshooting to define the direction/whether or not we are overshot
2108 if (!(*overshooting)) {
2110 /* Initiation of the overshoot happens when the finger is released
2111 * and the current position of the pannable contents are out of range
2113 if (dist < adjust->lower) {
2116 dist = adjust->lower;
2118 if (overshoot_max!=0) {
2121 *overshot_dist = CLAMP (*overshot_dist + *vel, 0, overshoot_max);
2122 *vel = MIN (priv->vmax_overshooting, *vel);
2123 gtk_widget_queue_resize (GTK_WIDGET (area));
2127 } else if (dist > adjust->upper - adjust->page_size) {
2130 dist = adjust->upper - adjust->page_size;
2132 if (overshoot_max!=0) {
2135 *overshot_dist = CLAMP (*overshot_dist + *vel, -overshoot_max, 0);
2136 *vel = MAX (-priv->vmax_overshooting, *vel);
2137 gtk_widget_queue_resize (GTK_WIDGET (area));
2142 if ((*scroll_to) != -1) {
2143 if (((inc < 0)&&(*scroll_to <= dist))||
2144 ((inc > 0)&&(*scroll_to >= dist))) {
2152 adjust->value = dist;
2154 if (!priv->button_pressed) {
2156 /* When the overshoot has started we continue for
2157 * PROP_BOUNCE_STEPS more steps into the overshoot before we
2158 * reverse direction. The deceleration factor is calculated
2159 * based on the percentage distance from the first item with
2160 * each iteration, therefore always returning us to the
2161 * top/bottom most element
2163 if (*overshot_dist > 0) {
2165 if ((*overshooting < priv->bounce_steps) && (*vel > 0)) {
2167 *vel = (((gdouble)*overshot_dist)/overshoot_max) * (*vel);
2168 } else if ((*overshooting >= priv->bounce_steps) && (*vel > 0)) {
2170 } else if ((*overshooting > 1) && (*vel < 0)) {
2171 /* we add the MIN in order to avoid very small speeds */
2172 *vel = MIN (((((gdouble)*overshot_dist)*0.8) * -1), -10.0);
2175 *overshot_dist = CLAMP (*overshot_dist + *vel, 0, overshoot_max);
2177 gtk_widget_queue_resize (GTK_WIDGET (area));
2179 } else if (*overshot_dist < 0) {
2181 if ((*overshooting < priv->bounce_steps) && (*vel < 0)) {
2183 *vel = (((gdouble)*overshot_dist)/overshoot_max) * (*vel) * -1;
2184 } else if ((*overshooting >= priv->bounce_steps) && (*vel < 0)) {
2186 } else if ((*overshooting > 1) && (*vel > 0)) {
2187 /* we add the MAX in order to avoid very small speeds */
2188 *vel = MAX (((((gdouble)*overshot_dist)*0.8) * -1), 10.0);
2191 *overshot_dist = CLAMP (*overshot_dist + (*vel), -overshoot_max, 0);
2193 gtk_widget_queue_resize (GTK_WIDGET (area));
2198 gtk_widget_queue_resize (GTK_WIDGET (area));
2202 gint overshot_dist_old = *overshot_dist;
2204 if (*overshot_dist > 0) {
2205 *overshot_dist = CLAMP ((*overshot_dist) + inc, 0, overshoot_max);
2206 } else if (*overshot_dist < 0) {
2207 *overshot_dist = CLAMP ((*overshot_dist) + inc, -1 * overshoot_max, 0);
2210 adjust->value = CLAMP (dist,
2216 if (*overshot_dist != overshot_dist_old)
2217 gtk_widget_queue_resize (GTK_WIDGET (area));
2223 hildon_pannable_area_scroll (HildonPannableArea *area,
2224 gdouble x, gdouble y)
2227 HildonPannableAreaPrivate *priv = area->priv;
2228 gboolean hscroll_visible, vscroll_visible;
2231 if (gtk_bin_get_child (GTK_BIN (area)) == NULL)
2234 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
2235 priv->vadjust->page_size);
2236 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
2237 priv->hadjust->page_size);
2242 hv = priv->hadjust->value;
2243 vv = priv->vadjust->value;
2245 if (vscroll_visible) {
2246 hildon_pannable_axis_scroll (area, priv->vadjust, &priv->vel_y, y,
2247 &priv->overshooting_y, &priv->overshot_dist_y,
2248 &priv->scroll_to_y, priv->vovershoot_max, &sy);
2253 if (hscroll_visible) {
2254 hildon_pannable_axis_scroll (area, priv->hadjust, &priv->vel_x, x,
2255 &priv->overshooting_x, &priv->overshot_dist_x,
2256 &priv->scroll_to_x, priv->hovershoot_max, &sx);
2261 if (hv != priv->hadjust->value)
2262 gtk_adjustment_value_changed (priv->hadjust);
2264 if (vv != priv->vadjust->value)
2265 gtk_adjustment_value_changed (priv->vadjust);
2267 /* If the scroll on a particular axis wasn't succesful, reset the
2268 * initial scroll position to the new mouse co-ordinate. This means
2269 * when you get to the top of the page, dragging down works immediately.
2271 if (priv->mode == HILDON_PANNABLE_AREA_MODE_ACCEL) {
2283 hildon_pannable_area_timeout (HildonPannableArea * area)
2285 HildonPannableAreaPrivate *priv = area->priv;
2287 if ((!priv->enabled) || (priv->mode == HILDON_PANNABLE_AREA_MODE_PUSH)) {
2289 g_signal_emit (area, pannable_area_signals[PANNING_FINISHED], 0);
2294 if (!priv->button_pressed) {
2295 /* Decelerate gradually when pointer is raised */
2296 if ((!priv->overshot_dist_y) &&
2297 (!priv->overshot_dist_x)) {
2299 /* in case we move to a specific point do not decelerate when arriving */
2300 if ((priv->scroll_to_x != -1)||(priv->scroll_to_y != -1)) {
2302 if (ABS (priv->vel_x) >= 1.5) {
2303 priv->vel_x *= priv->decel;
2306 if (ABS (priv->vel_y) >= 1.5) {
2307 priv->vel_y *= priv->decel;
2311 if ((!priv->low_friction_mode) ||
2312 ((priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ) &&
2313 (ABS (priv->vel_x) < 0.8*priv->vmax)))
2314 priv->vel_x *= priv->decel;
2316 if ((!priv->low_friction_mode) ||
2317 ((priv->mov_mode&HILDON_MOVEMENT_MODE_VERT) &&
2318 (ABS (priv->vel_y) < 0.8*priv->vmax)))
2319 priv->vel_y *= priv->decel;
2321 if ((ABS (priv->vel_x) < 1.0) && (ABS (priv->vel_y) < 1.0)) {
2326 g_signal_emit (area, pannable_area_signals[PANNING_FINISHED], 0);
2332 } else if (priv->mode == HILDON_PANNABLE_AREA_MODE_AUTO) {
2338 hildon_pannable_area_scroll (area, priv->vel_x, priv->vel_y);
2340 gdk_window_process_updates (GTK_WIDGET (area)->window, FALSE);
2346 hildon_pannable_area_calculate_velocity (gdouble *vel,
2350 gdouble drag_inertia,
2356 if (ABS (dist) >= RATIO_TOLERANCE) {
2357 rawvel = (dist / ABS (delta)) * force;
2358 *vel = *vel * (1 - drag_inertia) +
2359 rawvel * drag_inertia;
2360 *vel = *vel > 0 ? MIN (*vel, vmax)
2361 : MAX (*vel, -1 * vmax);
2366 hildon_pannable_area_motion_event_scroll_timeout (HildonPannableArea *area)
2368 HildonPannableAreaPrivate *priv = area->priv;
2370 if ((priv->motion_x != 0)||(priv->motion_y != 0))
2371 hildon_pannable_area_scroll (area, priv->motion_x, priv->motion_y);
2373 priv->motion_event_scroll_timeout = 0;
2379 hildon_pannable_area_motion_event_scroll (HildonPannableArea *area,
2380 gdouble x, gdouble y)
2382 HildonPannableAreaPrivate *priv = area->priv;
2384 if (priv->motion_event_scroll_timeout) {
2386 priv->motion_x += x;
2387 priv->motion_y += y;
2391 /* we do not delay the first event but the next ones */
2392 hildon_pannable_area_scroll (area, x, y);
2397 priv->motion_event_scroll_timeout = gdk_threads_add_timeout
2398 ((gint) (1000.0 / (gdouble) MOTION_EVENTS_PER_SECOND),
2399 (GSourceFunc) hildon_pannable_area_motion_event_scroll_timeout, area);
2404 hildon_pannable_area_check_move (HildonPannableArea *area,
2405 GdkEventMotion * event,
2409 HildonPannableAreaPrivate *priv = area->priv;
2411 if (priv->first_drag && (!priv->moved) &&
2412 ((ABS (*x) > (priv->panning_threshold))
2413 || (ABS (*y) > (priv->panning_threshold)))) {
2418 if (priv->first_drag) {
2419 gboolean vscroll_visible;
2420 gboolean hscroll_visible;
2422 if (ABS (priv->iy - event->y) >=
2423 ABS (priv->ix - event->x)) {
2425 g_signal_emit (area,
2426 pannable_area_signals[VERTICAL_MOVEMENT],
2427 0, (priv->iy > event->y) ?
2428 HILDON_MOVEMENT_UP :
2429 HILDON_MOVEMENT_DOWN,
2430 (gdouble)priv->ix, (gdouble)priv->iy);
2432 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
2433 priv->vadjust->page_size);
2435 if (!((vscroll_visible)&&
2436 (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT))) {
2438 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
2439 priv->hadjust->page_size);
2441 /* even in case we do not have to move we check if this
2442 could be a fake horizontal movement */
2443 if (!((hscroll_visible)&&
2444 (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ)) ||
2445 (ABS (priv->iy - event->y) -
2446 ABS (priv->ix - event->x) >= priv->direction_error_margin))
2447 priv->moved = FALSE;
2451 g_signal_emit (area,
2452 pannable_area_signals[HORIZONTAL_MOVEMENT],
2453 0, (priv->ix > event->x) ?
2454 HILDON_MOVEMENT_LEFT :
2455 HILDON_MOVEMENT_RIGHT,
2456 (gdouble)priv->ix, (gdouble)priv->iy);
2458 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
2459 priv->hadjust->page_size);
2461 if (!((hscroll_visible)&&
2462 (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ))) {
2464 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
2465 priv->vadjust->page_size);
2467 /* even in case we do not have to move we check if this
2468 could be a fake vertical movement */
2469 if (!((vscroll_visible) &&
2470 (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT)) ||
2471 (ABS (priv->ix - event->x) -
2472 ABS (priv->iy - event->y) >= priv->direction_error_margin))
2473 priv->moved = FALSE;
2477 if ((priv->moved)&&(priv->child)) {
2480 pos_x = priv->cx + (event->x - priv->ix);
2481 pos_y = priv->cy + (event->y - priv->iy);
2483 synth_crossing (priv->child, pos_x, pos_y, event->x_root,
2484 event->y_root, event->time, FALSE);
2488 gboolean result_val;
2490 g_signal_emit (area,
2491 pannable_area_signals[PANNING_STARTED],
2494 priv->moved = !result_val;
2498 priv->first_drag = FALSE;
2500 if ((priv->mode != HILDON_PANNABLE_AREA_MODE_PUSH) &&
2501 (priv->mode != HILDON_PANNABLE_AREA_MODE_AUTO)) {
2504 priv->idle_id = gdk_threads_add_timeout ((gint)
2505 (1000.0 / (gdouble) priv->sps),
2507 hildon_pannable_area_timeout, area);
2513 hildon_pannable_area_handle_move (HildonPannableArea *area,
2514 GdkEventMotion * event,
2518 HildonPannableAreaPrivate *priv = area->priv;
2521 switch (priv->mode) {
2522 case HILDON_PANNABLE_AREA_MODE_PUSH:
2523 /* Scroll by the amount of pixels the cursor has moved
2524 * since the last motion event.
2526 hildon_pannable_area_motion_event_scroll (area, *x, *y);
2530 case HILDON_PANNABLE_AREA_MODE_ACCEL:
2531 /* Set acceleration relative to the initial click */
2532 priv->ex = event->x;
2533 priv->ey = event->y;
2534 priv->vel_x = ((*x > 0) ? 1 : -1) *
2536 (gdouble) GTK_WIDGET (area)->allocation.width) *
2537 (priv->vmax - priv->vmin)) + priv->vmin);
2538 priv->vel_y = ((*y > 0) ? 1 : -1) *
2540 (gdouble) GTK_WIDGET (area)->allocation.height) *
2541 (priv->vmax - priv->vmin)) + priv->vmin);
2543 case HILDON_PANNABLE_AREA_MODE_AUTO:
2545 delta = event->time - priv->last_time;
2547 if (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT) {
2548 gdouble dist = event->y - priv->y;
2550 hildon_pannable_area_calculate_velocity (&priv->vel_y,
2562 if (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ) {
2563 gdouble dist = event->x - priv->x;
2565 hildon_pannable_area_calculate_velocity (&priv->vel_x,
2577 hildon_pannable_area_motion_event_scroll (area, *x, *y);
2579 if (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ)
2581 if (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT)
2591 hildon_pannable_area_motion_notify_cb (GtkWidget * widget,
2592 GdkEventMotion * event)
2594 HildonPannableArea *area = HILDON_PANNABLE_AREA (widget);
2595 HildonPannableAreaPrivate *priv = area->priv;
2598 if (gtk_bin_get_child (GTK_BIN (widget)) == NULL)
2601 if ((!priv->enabled) || (!priv->button_pressed) ||
2602 ((event->time == priv->last_time) && (priv->last_type == 2))) {
2603 gdk_window_get_pointer (widget->window, NULL, NULL, 0);
2607 if (priv->last_type == 1) {
2608 priv->first_drag = TRUE;
2611 x = event->x - priv->x;
2612 y = event->y - priv->y;
2615 hildon_pannable_area_check_move (area, event, &x, &y);
2619 hildon_pannable_area_handle_move (area, event, &x, &y);
2620 } else if (priv->child) {
2624 pos_x = priv->cx + (event->x - priv->ix);
2625 pos_y = priv->cy + (event->y - priv->iy);
2627 in = (((0 <= pos_x)&&(priv->child_width >= pos_x)) &&
2628 ((0 <= pos_y)&&(priv->child_height >= pos_y)));
2630 if (((!priv->last_in)&&in)||((priv->last_in)&&(!in))) {
2632 synth_crossing (priv->child, pos_x, pos_y, event->x_root,
2633 event->y_root, event->time, in);
2639 priv->last_time = event->time;
2640 priv->last_type = 2;
2643 /* Send motion notify to child */
2644 event = (GdkEventMotion *) gdk_event_copy ((GdkEvent *) event);
2645 /* remove the reference we added with the copy */
2646 g_object_unref (priv->event_window);
2647 event->x = priv->cx + (event->x - priv->ix);
2648 event->y = priv->cy + (event->y - priv->iy);
2649 event->window = g_object_ref (priv->child);
2650 gdk_event_put ((GdkEvent *) event);
2651 gdk_event_free ((GdkEvent *) event);
2654 gdk_window_get_pointer (widget->window, NULL, NULL, 0);
2660 hildon_pannable_leave_notify_event (GtkWidget *widget,
2661 GdkEventCrossing *event)
2663 HildonPannableArea *area = HILDON_PANNABLE_AREA (widget);
2664 HildonPannableAreaPrivate *priv = area->priv;
2666 if ((priv->child)&&(priv->last_in)) {
2667 priv->last_in = FALSE;
2669 synth_crossing (priv->child, 0, 0, event->x_root,
2670 event->y_root, event->time, FALSE);
2677 hildon_pannable_area_button_release_cb (GtkWidget * widget,
2678 GdkEventButton * event)
2680 HildonPannableArea *area = HILDON_PANNABLE_AREA (widget);
2681 HildonPannableAreaPrivate *priv = area->priv;
2686 if (((event->time == priv->last_time) && (priv->last_type == 3))
2687 || (gtk_bin_get_child (GTK_BIN (widget)) == NULL)
2688 || (!priv->button_pressed) || (!priv->enabled) || (event->button != 1))
2691 /* if last event was a motion-notify we have to check the movement
2692 and launch the animation */
2693 if (priv->last_type == 2) {
2695 dx = event->x - priv->x;
2696 dy = event->y - priv->y;
2698 hildon_pannable_area_check_move (area, (GdkEventMotion *) event, &dx, &dy);
2701 gdouble delta = event->time - priv->last_time;
2703 hildon_pannable_area_handle_move (area, (GdkEventMotion *) event, &dx, &dy);
2705 /* move all the way to the last position now */
2706 if (priv->motion_event_scroll_timeout) {
2707 g_source_remove (priv->motion_event_scroll_timeout);
2708 hildon_pannable_area_motion_event_scroll_timeout (HILDON_PANNABLE_AREA (widget));
2713 if ((ABS (dx) < 4.0) && (delta >= CURSOR_STOPPED_TIMEOUT))
2716 if ((ABS (dy) < 4.0) && (delta >= CURSOR_STOPPED_TIMEOUT))
2721 /* If overshoot has been initiated with a finger down, on release set max speed */
2722 if (priv->overshot_dist_y != 0) {
2723 priv->overshooting_y = priv->bounce_steps; /* Hack to stop a bounce in the finger down case */
2724 priv->vel_y = priv->overshot_dist_y * 0.9;
2727 if (priv->overshot_dist_x != 0) {
2728 priv->overshooting_x = priv->bounce_steps; /* Hack to stop a bounce in the finger down case */
2729 priv->vel_x = priv->overshot_dist_x * 0.9;
2732 priv->button_pressed = FALSE;
2734 if ((ABS (priv->vel_y) >= priv->vmin) ||
2735 (ABS (priv->vel_x) >= priv->vmin)) {
2737 /* we have to move because we are in overshooting position*/
2739 gboolean result_val;
2741 g_signal_emit (area,
2742 pannable_area_signals[PANNING_STARTED],
2746 priv->scroll_indicator_alpha = 1.0;
2748 if (ABS (priv->vel_x) > MAX_SPEED_THRESHOLD)
2749 priv->vel_x = (priv->vel_x > 0) ? priv->vmax : -priv->vmax;
2751 if (ABS (priv->vel_y) > MAX_SPEED_THRESHOLD)
2752 priv->vel_y = (priv->vel_y > 0) ? priv->vmax : -priv->vmax;
2755 priv->idle_id = gdk_threads_add_timeout ((gint) (1000.0 / (gdouble) priv->sps),
2757 hildon_pannable_area_timeout, widget);
2759 if (priv->center_on_child_focus_pending) {
2760 hildon_pannable_area_center_on_child_focus (area);
2764 g_signal_emit (widget, pannable_area_signals[PANNING_FINISHED], 0);
2767 area->priv->center_on_child_focus_pending = FALSE;
2769 priv->scroll_indicator_event_interrupt = 0;
2770 priv->scroll_delay_counter = priv->scrollbar_fade_delay;
2772 hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget),
2773 priv->scroll_indicator_alpha);
2775 priv->last_time = event->time;
2776 priv->last_type = 3;
2779 priv->moved = FALSE;
2784 hildon_pannable_area_get_topmost (gtk_bin_get_child (GTK_BIN (widget))->window,
2785 event->x, event->y, &x, &y, GDK_BUTTON_RELEASE_MASK);
2787 event = (GdkEventButton *) gdk_event_copy ((GdkEvent *) event);
2788 /* remove the reference we added with the copy */
2789 g_object_unref (priv->event_window);
2793 /* Leave the widget if we've moved - This doesn't break selection,
2794 * but stops buttons from being clicked.
2796 if ((child != priv->child) || (priv->moved)) {
2797 /* Send synthetic leave event */
2798 synth_crossing (priv->child, x, y, event->x_root,
2799 event->y_root, event->time, FALSE);
2800 /* insure no click will happen for widgets that do not handle
2804 /* Send synthetic button release event */
2805 ((GdkEventAny *) event)->window = g_object_ref (priv->child);
2806 gdk_event_put ((GdkEvent *) event);
2808 /* Send synthetic button release event */
2809 ((GdkEventAny *) event)->window = g_object_ref (child);
2810 gdk_event_put ((GdkEvent *) event);
2811 /* Send synthetic leave event */
2812 synth_crossing (priv->child, x, y, event->x_root,
2813 event->y_root, event->time, FALSE);
2815 g_object_remove_weak_pointer ((GObject *) priv->child,
2816 (gpointer) & priv->child);
2818 priv->moved = FALSE;
2819 gdk_event_free ((GdkEvent *) event);
2824 /* utility event handler */
2826 hildon_pannable_area_scroll_cb (GtkWidget *widget,
2827 GdkEventScroll *event)
2829 GtkAdjustment *adj = NULL;
2830 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
2832 if ((!priv->enabled) ||
2833 (gtk_bin_get_child (GTK_BIN (widget)) == NULL))
2836 priv->scroll_indicator_event_interrupt = 0;
2837 priv->scroll_delay_counter = priv->scrollbar_fade_delay + 20;
2839 hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget), 1.0);
2841 /* Stop inertial scrolling */
2842 if (priv->idle_id) {
2845 priv->overshooting_x = 0;
2846 priv->overshooting_y = 0;
2848 if ((priv->overshot_dist_x>0)||(priv->overshot_dist_y>0)) {
2849 priv->overshot_dist_x = 0;
2850 priv->overshot_dist_y = 0;
2852 gtk_widget_queue_resize (GTK_WIDGET (widget));
2855 g_signal_emit (widget, pannable_area_signals[PANNING_FINISHED], 0);
2857 g_source_remove (priv->idle_id);
2861 if (event->direction == GDK_SCROLL_UP || event->direction == GDK_SCROLL_DOWN)
2862 adj = priv->vadjust;
2864 adj = priv->hadjust;
2868 gdouble delta, new_value;
2870 /* from gtkrange.c calculate delta*/
2871 delta = pow (adj->page_size, 2.0 / 3.0);
2873 if (event->direction == GDK_SCROLL_UP ||
2874 event->direction == GDK_SCROLL_LEFT)
2877 new_value = CLAMP (adj->value + delta, adj->lower, adj->upper - adj->page_size);
2879 gtk_adjustment_set_value (adj, new_value);
2886 hildon_pannable_area_child_mapped (GtkWidget *widget,
2890 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (user_data)->priv;
2892 if (priv->event_window != NULL && priv->enabled)
2893 gdk_window_raise (priv->event_window);
2897 hildon_pannable_area_add (GtkContainer *container, GtkWidget *child)
2899 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (container)->priv;
2901 g_return_if_fail (gtk_bin_get_child (GTK_BIN (container)) == NULL);
2903 gtk_widget_set_parent (child, GTK_WIDGET (container));
2904 GTK_BIN (container)->child = child;
2906 g_signal_connect_after (child, "map-event",
2907 G_CALLBACK (hildon_pannable_area_child_mapped),
2910 if (!gtk_widget_set_scroll_adjustments (child, priv->hadjust, priv->vadjust)) {
2911 g_warning ("%s: cannot add non scrollable widget, "
2912 "wrap it in a viewport", __FUNCTION__);
2916 /* call this function if you are not panning */
2918 hildon_pannable_area_center_on_child_focus (HildonPannableArea *area)
2920 GtkWidget *focused_child = NULL;
2921 GtkWidget *window = NULL;
2923 window = gtk_widget_get_toplevel (GTK_WIDGET (area));
2925 if (GTK_WIDGET_TOPLEVEL (window)) {
2926 focused_child = gtk_window_get_focus (GTK_WINDOW (window));
2929 if (focused_child) {
2930 hildon_pannable_area_scroll_to_child (area, focused_child);
2935 hildon_pannable_area_set_focus_child (GtkContainer *container,
2938 HildonPannableArea *area = HILDON_PANNABLE_AREA (container);
2940 if (!area->priv->center_on_child_focus) {
2944 if (GTK_IS_WIDGET (child)) {
2945 area->priv->center_on_child_focus_pending = TRUE;
2950 hildon_pannable_area_remove (GtkContainer *container, GtkWidget *child)
2952 g_return_if_fail (HILDON_IS_PANNABLE_AREA (container));
2953 g_return_if_fail (child != NULL);
2954 g_return_if_fail (gtk_bin_get_child (GTK_BIN (container)) == child);
2956 gtk_widget_set_scroll_adjustments (child, NULL, NULL);
2958 g_signal_handlers_disconnect_by_func (child,
2959 hildon_pannable_area_child_mapped,
2962 /* chain parent class handler to remove child */
2963 GTK_CONTAINER_CLASS (hildon_pannable_area_parent_class)->remove (container, child);
2967 * This method calculates a factor necessary to determine the initial distance
2968 * to jump in hildon_pannable_area_scroll_to(). For fixed time and frames per
2969 * second, we know in how many frames 'n' we need to reach the destination
2970 * point. We know that, for a distance d,
2972 * d = d_0 + d_1 + ... + d_n
2974 * where d_i is the distance travelled in the i-th frame and decel_factor is
2975 * the deceleration factor. This can be rewritten as
2977 * d = d_0 + (d_0 * decel_factor) + ... + (d_n-1 * decel_factor),
2979 * since the distance travelled on each frame is the distance travelled in the
2980 * previous frame reduced by the deceleration factor. Reducing this and
2981 * factoring d_0 out, we get
2983 * d = d_0 (1 + decel_factor + ... + decel_factor^(n-1)).
2985 * Since the sum is independent of the distance to be travelled, we can define
2988 * vel_factor = 1 + decel_factor + ... + decel_factor^(n-1).
2990 * That's the gem we calculate in this method.
2993 hildon_pannable_calculate_vel_factor (HildonPannableArea * self)
2995 HildonPannableAreaPrivate *priv = self->priv;
3000 n = ceil (priv->sps * priv->scroll_time);
3002 for (i = 1; i < n && fct_i >= RATIO_TOLERANCE; i++) {
3003 fct_i *= priv->decel;
3007 priv->vel_factor = fct;
3011 * hildon_pannable_area_new:
3013 * Create a new pannable area widget
3015 * Returns: the newly created #HildonPannableArea
3021 hildon_pannable_area_new (void)
3023 return g_object_new (HILDON_TYPE_PANNABLE_AREA, NULL);
3027 * hildon_pannable_area_new_full:
3028 * @mode: #HildonPannableAreaMode
3029 * @enabled: Value for the enabled property
3030 * @vel_min: Value for the velocity-min property
3031 * @vel_max: Value for the velocity-max property
3032 * @decel: Value for the deceleration property
3033 * @sps: Value for the sps property
3035 * Create a new #HildonPannableArea widget and set various properties
3037 * returns: the newly create #HildonPannableArea
3043 hildon_pannable_area_new_full (gint mode, gboolean enabled,
3044 gdouble vel_min, gdouble vel_max,
3045 gdouble decel, guint sps)
3047 return g_object_new (HILDON_TYPE_PANNABLE_AREA,
3050 "velocity_min", vel_min,
3051 "velocity_max", vel_max,
3052 "deceleration", decel, "sps", sps, NULL);
3056 * hildon_pannable_area_add_with_viewport:
3057 * @area: A #HildonPannableArea
3058 * @child: Child widget to add to the viewport
3060 * Convenience function used to add a child to a #GtkViewport, and add the
3061 * viewport to the scrolled window.
3067 hildon_pannable_area_add_with_viewport (HildonPannableArea * area,
3071 GtkWidget *viewport;
3073 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3074 g_return_if_fail (GTK_IS_WIDGET (child));
3075 g_return_if_fail (child->parent == NULL);
3077 bin = GTK_BIN (area);
3079 if (bin->child != NULL)
3081 g_return_if_fail (GTK_IS_VIEWPORT (bin->child));
3082 g_return_if_fail (GTK_BIN (bin->child)->child == NULL);
3084 viewport = bin->child;
3088 HildonPannableAreaPrivate *priv = area->priv;
3090 viewport = gtk_viewport_new (priv->hadjust,
3092 gtk_viewport_set_shadow_type (GTK_VIEWPORT (viewport), GTK_SHADOW_NONE);
3093 gtk_container_add (GTK_CONTAINER (area), viewport);
3096 gtk_widget_show (viewport);
3097 gtk_container_add (GTK_CONTAINER (viewport), child);
3101 * hildon_pannable_area_scroll_to:
3102 * @area: A #HildonPannableArea.
3103 * @x: The x coordinate of the destination point or -1 to ignore this axis.
3104 * @y: The y coordinate of the destination point or -1 to ignore this axis.
3106 * Smoothly scrolls @area to ensure that (@x, @y) is a visible point
3107 * on the widget. To move in only one coordinate, you must set the other one
3108 * to -1. Notice that, in %HILDON_PANNABLE_AREA_MODE_PUSH mode, this function
3109 * works just like hildon_pannable_area_jump_to().
3111 * This function is useful if you need to present the user with a particular
3112 * element inside a scrollable widget, like #GtkTreeView. For instance,
3113 * the following example shows how to scroll inside a #GtkTreeView to
3114 * make visible an item, indicated by the #GtkTreeIter @iter.
3118 * GtkTreePath *path;
3119 * GdkRectangle *rect;
3121 * path = gtk_tree_model_get_path (model, &iter);
3122 * gtk_tree_view_get_background_area (GTK_TREE_VIEW (treeview),
3123 * path, NULL, &rect);
3124 * gtk_tree_view_convert_bin_window_to_tree_coords (GTK_TREE_VIEW (treeview),
3125 * 0, rect.y, NULL, &y);
3126 * hildon_pannable_area_scroll_to (panarea, -1, y);
3127 * gtk_tree_path_free (path);
3131 * If you want to present a child widget in simpler scenarios,
3132 * use hildon_pannable_area_scroll_to_child() instead.
3134 * There is a precondition to this function: the widget must be
3135 * already realized. Check the hildon_pannable_area_jump_to_child() for
3136 * more tips regarding how to call this function during
3142 hildon_pannable_area_scroll_to (HildonPannableArea *area,
3143 const gint x, const gint y)
3145 HildonPannableAreaPrivate *priv;
3147 gint dist_x, dist_y;
3148 gboolean hscroll_visible, vscroll_visible;
3150 g_return_if_fail (GTK_WIDGET_REALIZED (area));
3151 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3155 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
3156 priv->vadjust->page_size);
3157 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
3158 priv->hadjust->page_size);
3160 if (((!vscroll_visible)&&(!hscroll_visible)) ||
3161 (x == -1 && y == -1)) {
3165 if (priv->mode == HILDON_PANNABLE_AREA_MODE_PUSH)
3166 hildon_pannable_area_jump_to (area, x, y);
3168 width = priv->hadjust->upper - priv->hadjust->lower;
3169 height = priv->vadjust->upper - priv->vadjust->lower;
3171 g_return_if_fail (x < width || y < height);
3173 if ((x > -1)&&(hscroll_visible)) {
3174 priv->scroll_to_x = x - priv->hadjust->page_size/2;
3175 dist_x = priv->scroll_to_x - priv->hadjust->value;
3177 priv->scroll_to_x = -1;
3179 priv->vel_x = - dist_x/priv->vel_factor;
3182 priv->scroll_to_x = -1;
3185 if ((y > -1)&&(vscroll_visible)) {
3186 priv->scroll_to_y = y - priv->vadjust->page_size/2;
3187 dist_y = priv->scroll_to_y - priv->vadjust->value;
3189 priv->scroll_to_y = -1;
3191 priv->vel_y = - dist_y/priv->vel_factor;
3194 priv->scroll_to_y = y;
3197 if ((priv->scroll_to_y == -1) && (priv->scroll_to_y == -1)) {
3201 hildon_pannable_area_launch_fade_timeout (area, 1.0);
3204 priv->idle_id = gdk_threads_add_timeout ((gint) (1000.0 / (gdouble) priv->sps),
3206 hildon_pannable_area_timeout, area);
3210 * hildon_pannable_area_jump_to:
3211 * @area: A #HildonPannableArea.
3212 * @x: The x coordinate of the destination point or -1 to ignore this axis.
3213 * @y: The y coordinate of the destination point or -1 to ignore this axis.
3215 * Jumps the position of @area to ensure that (@x, @y) is a visible
3216 * point in the widget. In order to move in only one coordinate, you
3217 * must set the other one to -1. See hildon_pannable_area_scroll_to()
3218 * function for an example of how to calculate the position of
3219 * children in scrollable widgets like #GtkTreeview.
3221 * There is a precondition to this function: the widget must be
3222 * already realized. Check the hildon_pannable_area_jump_to_child() for
3223 * more tips regarding how to call this function during
3229 hildon_pannable_area_jump_to (HildonPannableArea *area,
3230 const gint x, const gint y)
3232 HildonPannableAreaPrivate *priv;
3236 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3237 g_return_if_fail (GTK_WIDGET_REALIZED (area));
3238 g_return_if_fail (x >= -1 && y >= -1);
3240 if (x == -1 && y == -1) {
3246 width = priv->hadjust->upper - priv->hadjust->lower;
3247 height = priv->vadjust->upper - priv->vadjust->lower;
3249 g_return_if_fail (x < width || y < height);
3251 hv = priv->hadjust->value;
3252 vv = priv->vadjust->value;
3255 gdouble jump_to = x - priv->hadjust->page_size/2;
3257 priv->hadjust->value = CLAMP (jump_to,
3258 priv->hadjust->lower,
3259 priv->hadjust->upper -
3260 priv->hadjust->page_size);
3264 gdouble jump_to = y - priv->vadjust->page_size/2;
3266 priv->vadjust->value = CLAMP (jump_to,
3267 priv->vadjust->lower,
3268 priv->vadjust->upper -
3269 priv->vadjust->page_size);
3272 if (hv != priv->hadjust->value)
3273 gtk_adjustment_value_changed (priv->hadjust);
3275 if (vv != priv->vadjust->value)
3276 gtk_adjustment_value_changed (priv->vadjust);
3278 priv->scroll_indicator_alpha = 1.0;
3280 if (priv->scroll_indicator_timeout) {
3281 g_source_remove (priv->scroll_indicator_timeout);
3282 priv->scroll_indicator_timeout = 0;
3285 if (priv->idle_id) {
3288 priv->overshooting_x = 0;
3289 priv->overshooting_y = 0;
3291 if ((priv->overshot_dist_x>0)||(priv->overshot_dist_y>0)) {
3292 priv->overshot_dist_x = 0;
3293 priv->overshot_dist_y = 0;
3295 gtk_widget_queue_resize (GTK_WIDGET (area));
3298 g_signal_emit (area, pannable_area_signals[PANNING_FINISHED], 0);
3299 g_source_remove (priv->idle_id);
3305 * hildon_pannable_area_scroll_to_child:
3306 * @area: A #HildonPannableArea.
3307 * @child: A #GtkWidget, descendant of @area.
3309 * Smoothly scrolls until @child is visible inside @area. @child must
3310 * be a descendant of @area. If you need to scroll inside a scrollable
3311 * widget, e.g., #GtkTreeview, see hildon_pannable_area_scroll_to().
3313 * There is a precondition to this function: the widget must be
3314 * already realized. Check the hildon_pannable_area_jump_to_child() for
3315 * more tips regarding how to call this function during
3321 hildon_pannable_area_scroll_to_child (HildonPannableArea *area, GtkWidget *child)
3323 GtkWidget *bin_child;
3326 g_return_if_fail (GTK_WIDGET_REALIZED (area));
3327 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3328 g_return_if_fail (GTK_IS_WIDGET (child));
3329 g_return_if_fail (gtk_widget_is_ancestor (child, GTK_WIDGET (area)));
3331 if (GTK_BIN (area)->child == NULL)
3334 /* We need to get to check the child of the inside the area */
3335 bin_child = GTK_BIN (area)->child;
3337 /* we check if we added a viewport */
3338 if (GTK_IS_VIEWPORT (bin_child)) {
3339 bin_child = GTK_BIN (bin_child)->child;
3342 if (gtk_widget_translate_coordinates (child, bin_child, 0, 0, &x, &y))
3343 hildon_pannable_area_scroll_to (area, x, y);
3347 * hildon_pannable_area_jump_to_child:
3348 * @area: A #HildonPannableArea.
3349 * @child: A #GtkWidget, descendant of @area.
3351 * Jumps to make sure @child is visible inside @area. @child must
3352 * be a descendant of @area. If you want to move inside a scrollable
3353 * widget, like, #GtkTreeview, see hildon_pannable_area_scroll_to().
3355 * There is a precondition to this function: the widget must be
3356 * already realized. You can control if the widget is ready with the
3357 * GTK_WIDGET_REALIZED macro. If you want to call this function during
3358 * the initialization process of the widget do it inside a callback to
3359 * the ::realize signal, using g_signal_connect_after() function.
3364 hildon_pannable_area_jump_to_child (HildonPannableArea *area, GtkWidget *child)
3366 GtkWidget *bin_child;
3369 g_return_if_fail (GTK_WIDGET_REALIZED (area));
3370 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3371 g_return_if_fail (GTK_IS_WIDGET (child));
3372 g_return_if_fail (gtk_widget_is_ancestor (child, GTK_WIDGET (area)));
3374 if (gtk_bin_get_child (GTK_BIN (area)) == NULL)
3377 /* We need to get to check the child of the inside the area */
3378 bin_child = gtk_bin_get_child (GTK_BIN (area));
3380 /* we check if we added a viewport */
3381 if (GTK_IS_VIEWPORT (bin_child)) {
3382 bin_child = gtk_bin_get_child (GTK_BIN (bin_child));
3385 if (gtk_widget_translate_coordinates (child, bin_child, 0, 0, &x, &y))
3386 hildon_pannable_area_jump_to (area, x, y);
3390 * hildon_pannable_get_child_widget_at:
3391 * @area: A #HildonPannableArea.
3392 * @x: horizontal coordinate of the point
3393 * @y: vertical coordinate of the point
3395 * Get the widget at the point (x, y) inside the pannable area. In
3396 * case no widget found it returns NULL.
3398 * returns: the #GtkWidget if we find a widget, NULL in any other case
3403 hildon_pannable_get_child_widget_at (HildonPannableArea *area,
3404 gdouble x, gdouble y)
3406 GdkWindow *window = NULL;
3407 GtkWidget *child_widget = NULL;
3409 window = hildon_pannable_area_get_topmost
3410 (gtk_bin_get_child (GTK_BIN (area))->window,
3411 x, y, NULL, NULL, GDK_ALL_EVENTS_MASK);
3413 gdk_window_get_user_data (window, (gpointer) &child_widget);
3415 return child_widget;
3420 * hildon_pannable_area_get_hadjustment:
3421 * @area: A #HildonPannableArea.
3423 * Returns the horizontal adjustment. This adjustment is the internal
3424 * widget adjustment used to control the animations. Do not modify it
3425 * directly to change the position of the pannable, to do that use the
3426 * pannable API. If you modify the object directly it could cause
3427 * artifacts in the animations.
3429 * returns: The horizontal #GtkAdjustment
3434 hildon_pannable_area_get_hadjustment (HildonPannableArea *area)
3437 g_return_val_if_fail (HILDON_IS_PANNABLE_AREA (area), NULL);
3439 return area->priv->hadjust;
3443 * hildon_pannable_area_get_vadjustment:
3444 * @area: A #HildonPannableArea.
3446 * Returns the vertical adjustment. This adjustment is the internal
3447 * widget adjustment used to control the animations. Do not modify it
3448 * directly to change the position of the pannable, to do that use the
3449 * pannable API. If you modify the object directly it could cause
3450 * artifacts in the animations.
3452 * returns: The vertical #GtkAdjustment
3457 hildon_pannable_area_get_vadjustment (HildonPannableArea *area)
3459 g_return_val_if_fail (HILDON_IS_PANNABLE_AREA (area), NULL);
3461 return area->priv->vadjust;
3466 * hildon_pannable_area_get_size_request_policy:
3467 * @area: A #HildonPannableArea.
3469 * This function returns the current size request policy of the
3470 * widget. That policy controls the way the size_request is done in
3471 * the pannable area. Check
3472 * hildon_pannable_area_set_size_request_policy() for a more detailed
3475 * returns: the policy is currently being used in the widget
3476 * #HildonSizeRequestPolicy.
3480 HildonSizeRequestPolicy
3481 hildon_pannable_area_get_size_request_policy (HildonPannableArea *area)
3483 HildonPannableAreaPrivate *priv;
3485 g_return_val_if_fail (HILDON_IS_PANNABLE_AREA (area), FALSE);
3489 return priv->size_request_policy;
3493 * hildon_pannable_area_set_size_request_policy:
3494 * @area: A #HildonPannableArea.
3495 * @size_request_policy: One of the allowed #HildonSizeRequestPolicy
3497 * This function sets the pannable area size request policy. That
3498 * policy controls the way the size_request is done in the pannable
3499 * area. Pannable can use the size request of its children
3500 * (#HILDON_SIZE_REQUEST_CHILDREN) or the minimum size required for
3501 * the area itself (#HILDON_SIZE_REQUEST_MINIMUM), the latter is the
3502 * default. Recall this size depends on the scrolling policy you are
3503 * requesting to the pannable area, if you set #GTK_POLICY_NEVER this
3504 * parameter will not have any effect with
3505 * #HILDON_SIZE_REQUEST_MINIMUM set.
3509 * Deprecated: This method and the policy request is deprecated, DO
3510 * NOT use it in future code, the only policy properly supported in
3511 * gtk+ nowadays is the minimum size. Use #gtk_window_set_default_size
3512 * or #gtk_window_set_geometry_hints with the proper size in your case
3513 * to define the height of your dialogs.
3516 hildon_pannable_area_set_size_request_policy (HildonPannableArea *area,
3517 HildonSizeRequestPolicy size_request_policy)
3519 HildonPannableAreaPrivate *priv;
3521 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3525 if (priv->size_request_policy == size_request_policy)
3528 priv->size_request_policy = size_request_policy;
3530 gtk_widget_queue_resize (GTK_WIDGET (area));
3532 g_object_notify (G_OBJECT (area), "size-request-policy");
3536 * hildon_pannable_area_get_center_on_child_focus
3537 * @area: A #HildonPannableArea
3539 * Gets the @area #HildonPannableArea:center-on-child-focus property
3542 * See #HildonPannableArea:center-on-child-focus for more information.
3544 * Returns: the @area #HildonPannableArea:center-on-child-focus value
3549 hildon_pannable_area_get_center_on_child_focus (HildonPannableArea *area)
3551 g_return_val_if_fail (HILDON_IS_PANNABLE_AREA (area), FALSE);
3553 return area->priv->center_on_child_focus;
3557 * hildon_pannable_area_set_center_on_child_focus
3558 * @area: A #HildonPannableArea
3559 * @value: the new value
3561 * Sets the @area #HildonPannableArea:center-on-child-focus property
3564 * See #HildonPannableArea:center-on-child-focus for more information.
3569 hildon_pannable_area_set_center_on_child_focus (HildonPannableArea *area,
3572 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3574 area->priv->center_on_child_focus = value;