2 * This file is a part of hildon
4 * Copyright (C) 2008 Nokia Corporation, all rights reserved.
6 * Contact: Karl Lattimer <karl.lattimer@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 implements a scrolled window designed to be used with a
30 * touch screen interface. The user scrolls the child widget by activating the
31 * pointing device and dragging it over the widget.
39 #include "hildon-pannable-area.h"
40 #include "hildon-marshalers.h"
41 #include "hildon-enum-types.h"
43 #define SMOOTH_FACTOR 0.85
45 #define BOUNCE_STEPS 6
46 #define SCROLL_BAR_MIN_SIZE 5
47 #define RATIO_TOLERANCE 0.000001
48 #define DND_THRESHOLD_INC 20
49 #define SCROLLBAR_FADE_DELAY 30
51 G_DEFINE_TYPE (HildonPannableArea, hildon_pannable_area, GTK_TYPE_BIN)
53 #define PANNABLE_AREA_PRIVATE(o) \
54 (G_TYPE_INSTANCE_GET_PRIVATE ((o), HILDON_TYPE_PANNABLE_AREA, \
55 HildonPannableAreaPrivate))
57 typedef struct _HildonPannableAreaPrivate HildonPannableAreaPrivate;
59 struct _HildonPannableAreaPrivate {
60 HildonPannableAreaMode mode;
61 HildonMovementMode mov_mode;
62 GdkWindow *event_window;
63 gdouble x; /* Used to store mouse co-ordinates of the first or */
64 gdouble y; /* previous events in a press-motion pair */
65 gdouble ex; /* Used to store mouse co-ordinates of the last */
66 gdouble ey; /* motion event in acceleration mode */
69 guint32 last_time; /* Last event time, to stop infinite loops */
82 gint ix; /* Initial click mouse co-ordinates */
84 gint cx; /* Initial click child window mouse co-ordinates */
93 gdouble scroll_indicator_alpha;
94 gint scroll_indicator_timeout;
95 gint scroll_indicator_event_interrupt;
96 gint scroll_delay_counter;
99 gboolean initial_hint;
100 gboolean initial_effect;
103 gboolean hscroll_visible;
104 gboolean vscroll_visible;
105 GdkRectangle hscroll_rect;
106 GdkRectangle vscroll_rect;
109 GtkAdjustment *hadjust;
110 GtkAdjustment *vadjust;
112 GtkPolicyType vscrollbar_policy;
113 GtkPolicyType hscrollbar_policy;
123 static guint pannable_area_signals [LAST_SIGNAL] = { 0 };
131 PROP_VELOCITY_FAST_FACTOR,
134 PROP_VSCROLLBAR_POLICY,
135 PROP_HSCROLLBAR_POLICY,
142 static void hildon_pannable_area_class_init (HildonPannableAreaClass * klass);
143 static void hildon_pannable_area_init (HildonPannableArea * area);
144 static void hildon_pannable_area_get_property (GObject * object,
148 static void hildon_pannable_area_set_property (GObject * object,
150 const GValue * value,
152 static void hildon_pannable_area_dispose (GObject * object);
153 static void hildon_pannable_area_realize (GtkWidget * widget);
154 static void hildon_pannable_area_unrealize (GtkWidget * widget);
155 static void hildon_pannable_area_size_request (GtkWidget * widget,
156 GtkRequisition * requisition);
157 static void hildon_pannable_area_size_allocate (GtkWidget * widget,
158 GtkAllocation * allocation);
159 static void hildon_pannable_area_style_set (GtkWidget * widget,
160 GtkStyle * previous_style);
161 static void hildon_pannable_area_map (GtkWidget * widget);
162 static void hildon_pannable_area_unmap (GtkWidget * widget);
163 static void hildon_pannable_area_grab_notify (GtkWidget *widget,
164 gboolean was_grabbed,
166 static void rgb_from_gdkcolor (GdkColor *color, gdouble *r, gdouble *g, gdouble *b);
167 static void hildon_pannable_draw_vscroll (GtkWidget * widget,
168 GdkColor *back_color,
169 GdkColor *scroll_color);
170 static void hildon_pannable_draw_hscroll (GtkWidget * widget,
171 GdkColor *back_color,
172 GdkColor *scroll_color);
173 static void hildon_pannable_area_initial_effect (GtkWidget * widget);
174 static void hildon_pannable_area_redraw (HildonPannableArea * area);
175 static gboolean hildon_pannable_area_scroll_indicator_fade(HildonPannableArea * area);
176 static gboolean hildon_pannable_area_expose_event (GtkWidget * widget,
177 GdkEventExpose * event);
178 static GdkWindow * hildon_pannable_area_get_topmost (GdkWindow * window,
180 gint * tx, gint * ty);
181 static void synth_crossing (GdkWindow * child,
183 gint x_root, gint y_root,
184 guint32 time, gboolean in);
185 static gboolean hildon_pannable_area_button_press_cb (GtkWidget * widget,
186 GdkEventButton * event);
187 static void hildon_pannable_area_refresh (HildonPannableArea * area);
188 static void hildon_pannable_axis_scroll (HildonPannableArea *area,
189 GtkAdjustment *adjust,
197 static void hildon_pannable_area_scroll (HildonPannableArea *area,
198 gdouble x, gdouble y);
199 static gboolean hildon_pannable_area_timeout (HildonPannableArea * area);
200 static gboolean hildon_pannable_area_motion_notify_cb (GtkWidget * widget,
201 GdkEventMotion * event);
202 static gboolean hildon_pannable_area_button_release_cb (GtkWidget * widget,
203 GdkEventButton * event);
204 static gboolean hildon_pannable_area_scroll_cb (GtkWidget *widget,
205 GdkEventScroll *event);
206 static void hildon_pannable_area_add (GtkContainer *container, GtkWidget *child);
207 static void hildon_pannable_area_remove (GtkContainer *container, GtkWidget *child);
208 static void hildon_pannable_calculate_vel_factor (HildonPannableArea * self);
212 hildon_pannable_area_class_init (HildonPannableAreaClass * klass)
214 GObjectClass *object_class = G_OBJECT_CLASS (klass);
215 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
216 GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
219 g_type_class_add_private (klass, sizeof (HildonPannableAreaPrivate));
221 object_class->get_property = hildon_pannable_area_get_property;
222 object_class->set_property = hildon_pannable_area_set_property;
223 object_class->dispose = hildon_pannable_area_dispose;
225 widget_class->realize = hildon_pannable_area_realize;
226 widget_class->unrealize = hildon_pannable_area_unrealize;
227 widget_class->map = hildon_pannable_area_map;
228 widget_class->unmap = hildon_pannable_area_unmap;
229 widget_class->size_request = hildon_pannable_area_size_request;
230 widget_class->size_allocate = hildon_pannable_area_size_allocate;
231 widget_class->expose_event = hildon_pannable_area_expose_event;
232 widget_class->style_set = hildon_pannable_area_style_set;
233 widget_class->button_press_event = hildon_pannable_area_button_press_cb;
234 widget_class->button_release_event = hildon_pannable_area_button_release_cb;
235 widget_class->motion_notify_event = hildon_pannable_area_motion_notify_cb;
236 widget_class->scroll_event = hildon_pannable_area_scroll_cb;
238 container_class->add = hildon_pannable_area_add;
239 container_class->remove = hildon_pannable_area_remove;
241 klass->horizontal_movement = NULL;
242 klass->vertical_movement = NULL;
244 g_object_class_install_property (object_class,
246 g_param_spec_boolean ("enabled",
248 "Enable or disable finger-scroll.",
253 g_object_class_install_property (object_class,
254 PROP_VSCROLLBAR_POLICY,
255 g_param_spec_enum ("vscrollbar_policy",
257 "Visual policy of the vertical scrollbar",
258 GTK_TYPE_POLICY_TYPE,
259 GTK_POLICY_AUTOMATIC,
263 g_object_class_install_property (object_class,
264 PROP_HSCROLLBAR_POLICY,
265 g_param_spec_enum ("hscrollbar_policy",
267 "Visual policy of the horizontal scrollbar",
268 GTK_TYPE_POLICY_TYPE,
269 GTK_POLICY_AUTOMATIC,
273 g_object_class_install_property (object_class,
275 g_param_spec_enum ("mode",
277 "Change the finger-scrolling mode.",
278 HILDON_TYPE_PANNABLE_AREA_MODE,
279 HILDON_PANNABLE_AREA_MODE_AUTO,
283 g_object_class_install_property (object_class,
285 g_param_spec_flags ("mov_mode",
286 "Scroll movement mode",
287 "Controls if the widget can scroll vertically, horizontally or both",
288 HILDON_TYPE_MOVEMENT_MODE,
289 HILDON_MOVEMENT_MODE_BOTH,
293 g_object_class_install_property (object_class,
295 g_param_spec_double ("velocity_min",
296 "Minimum scroll velocity",
297 "Minimum distance the child widget should scroll "
298 "per 'frame', in pixels.",
303 g_object_class_install_property (object_class,
305 g_param_spec_double ("velocity_max",
306 "Maximum scroll velocity",
307 "Maximum distance the child widget should scroll "
308 "per 'frame', in pixels.",
313 g_object_class_install_property (object_class,
314 PROP_VELOCITY_FAST_FACTOR,
315 g_param_spec_double ("velocity_fast_factor",
316 "Fast velocity factor",
317 "Minimum velocity that is considered 'fast': "
318 "children widgets won't receive button presses. "
319 "Expressed as a fraction of the maximum velocity.",
324 g_object_class_install_property (object_class,
326 g_param_spec_double ("deceleration",
327 "Deceleration multiplier",
328 "The multiplier used when decelerating when in "
329 "acceleration scrolling mode.",
334 g_object_class_install_property (object_class,
336 g_param_spec_uint ("sps",
337 "Scrolls per second",
338 "Amount of scroll events to generate per second.",
343 g_object_class_install_property (object_class,
345 g_param_spec_int ("vovershoot_max",
346 "Vertical overshoot distance",
347 "Space we allow the widget to pass over its vertical limits when hitting the edges, set 0 in order to deactivate overshooting.",
352 g_object_class_install_property (object_class,
354 g_param_spec_int ("hovershoot_max",
355 "Horizontal overshoot distance",
356 "Space we allow the widget to pass over its horizontal limits when hitting the edges, set 0 in order to deactivate overshooting.",
361 g_object_class_install_property (object_class,
363 g_param_spec_double ("scroll_time",
364 "Time to scroll to a position",
365 "The time to scroll to a position when calling the hildon_pannable_scroll_to function"
366 "acceleration scrolling mode.",
371 g_object_class_install_property (object_class,
373 g_param_spec_boolean ("initial-hint",
375 "Whether to hint the user about the pannability of the container.",
380 gtk_widget_class_install_style_property (widget_class,
383 "Width of the scroll indicators",
384 "Pixel width used to draw the scroll indicators.",
388 pannable_area_signals[HORIZONTAL_MOVEMENT] =
389 g_signal_new ("horizontal_movement",
390 G_TYPE_FROM_CLASS (object_class),
391 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
392 G_STRUCT_OFFSET (HildonPannableAreaClass, horizontal_movement),
394 _hildon_marshal_VOID__INT_DOUBLE_DOUBLE,
400 pannable_area_signals[VERTICAL_MOVEMENT] =
401 g_signal_new ("vertical_movement",
402 G_TYPE_FROM_CLASS (object_class),
403 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
404 G_STRUCT_OFFSET (HildonPannableAreaClass, vertical_movement),
406 _hildon_marshal_VOID__INT_DOUBLE_DOUBLE,
416 hildon_pannable_area_init (HildonPannableArea * area)
418 HildonPannableAreaPrivate *priv = PANNABLE_AREA_PRIVATE (area);
421 priv->clicked = FALSE;
424 priv->vscroll_visible = TRUE;
425 priv->hscroll_visible = TRUE;
426 priv->area_width = 6;
427 priv->overshot_dist_x = 0;
428 priv->overshot_dist_y = 0;
429 priv->overshooting_y = 0;
430 priv->overshooting_x = 0;
434 priv->scroll_indicator_alpha = 0.0;
435 priv->scroll_indicator_timeout = 0;
436 priv->scroll_indicator_event_interrupt = 0;
437 priv->scroll_delay_counter = SCROLLBAR_FADE_DELAY;
438 priv->scroll_to_x = -1;
439 priv->scroll_to_y = -1;
440 priv->first_drag = TRUE;
441 priv->initial_effect = TRUE;
443 hildon_pannable_calculate_vel_factor (area);
445 gtk_widget_add_events (GTK_WIDGET (area), GDK_POINTER_MOTION_HINT_MASK);
448 GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
450 GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
452 g_object_ref_sink (G_OBJECT (priv->hadjust));
453 g_object_ref_sink (G_OBJECT (priv->vadjust));
455 g_signal_connect_swapped (G_OBJECT (priv->hadjust), "value-changed",
456 G_CALLBACK (hildon_pannable_area_redraw), area);
457 g_signal_connect_swapped (G_OBJECT (priv->vadjust), "value-changed",
458 G_CALLBACK (hildon_pannable_area_redraw), area);
459 g_signal_connect (G_OBJECT (area), "grab-notify",
460 G_CALLBACK (hildon_pannable_area_grab_notify), NULL);
464 hildon_pannable_area_get_property (GObject * object,
469 HildonPannableAreaPrivate *priv = PANNABLE_AREA_PRIVATE (object);
471 switch (property_id) {
473 g_value_set_boolean (value, priv->enabled);
476 g_value_set_enum (value, priv->mode);
478 case PROP_MOVEMENT_MODE:
479 g_value_set_flags (value, priv->mov_mode);
481 case PROP_VELOCITY_MIN:
482 g_value_set_double (value, priv->vmin);
484 case PROP_VELOCITY_MAX:
485 g_value_set_double (value, priv->vmax);
487 case PROP_VELOCITY_FAST_FACTOR:
488 g_value_set_double (value, priv->vfast_factor);
490 case PROP_DECELERATION:
491 g_value_set_double (value, priv->decel);
494 g_value_set_uint (value, priv->sps);
496 case PROP_VSCROLLBAR_POLICY:
497 g_value_set_enum (value, priv->vscrollbar_policy);
499 case PROP_HSCROLLBAR_POLICY:
500 g_value_set_enum (value, priv->hscrollbar_policy);
502 case PROP_VOVERSHOOT_MAX:
503 g_value_set_int (value, priv->vovershoot_max);
505 case PROP_HOVERSHOOT_MAX:
506 g_value_set_int (value, priv->hovershoot_max);
508 case PROP_SCROLL_TIME:
509 g_value_set_double (value, priv->scroll_time);
511 case PROP_INITIAL_HINT:
512 g_value_set_boolean (value, priv->initial_hint);
516 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
521 hildon_pannable_area_set_property (GObject * object,
523 const GValue * value,
526 HildonPannableAreaPrivate *priv = PANNABLE_AREA_PRIVATE (object);
529 switch (property_id) {
531 enabled = g_value_get_boolean (value);
533 if ((priv->enabled != enabled) && (GTK_WIDGET_REALIZED (object))) {
535 gdk_window_raise (priv->event_window);
537 gdk_window_lower (priv->event_window);
540 priv->enabled = enabled;
543 priv->mode = g_value_get_enum (value);
545 case PROP_MOVEMENT_MODE:
546 priv->mov_mode = g_value_get_flags (value);
548 case PROP_VELOCITY_MIN:
549 priv->vmin = g_value_get_double (value);
551 case PROP_VELOCITY_MAX:
552 priv->vmax = g_value_get_double (value);
554 case PROP_VELOCITY_FAST_FACTOR:
555 priv->vfast_factor = g_value_get_double (value);
557 case PROP_DECELERATION:
558 hildon_pannable_calculate_vel_factor (HILDON_PANNABLE_AREA (object));
560 priv->decel = g_value_get_double (value);
563 priv->sps = g_value_get_uint (value);
565 case PROP_VSCROLLBAR_POLICY:
566 priv->vscrollbar_policy = g_value_get_enum (value);
568 gtk_widget_queue_resize (GTK_WIDGET (object));
570 case PROP_HSCROLLBAR_POLICY:
571 priv->hscrollbar_policy = g_value_get_enum (value);
573 gtk_widget_queue_resize (GTK_WIDGET (object));
575 case PROP_VOVERSHOOT_MAX:
576 priv->vovershoot_max = g_value_get_int (value);
578 case PROP_HOVERSHOOT_MAX:
579 priv->hovershoot_max = g_value_get_int (value);
581 case PROP_SCROLL_TIME:
582 priv->scroll_time = g_value_get_double (value);
584 hildon_pannable_calculate_vel_factor (HILDON_PANNABLE_AREA (object));
586 case PROP_INITIAL_HINT:
587 priv->initial_hint = g_value_get_boolean (value);
591 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
596 hildon_pannable_area_dispose (GObject * object)
598 HildonPannableAreaPrivate *priv = PANNABLE_AREA_PRIVATE (object);
601 g_source_remove (priv->idle_id);
605 if (priv->scroll_indicator_timeout){
606 g_source_remove (priv->scroll_indicator_timeout);
607 priv->scroll_indicator_timeout = 0;
611 g_object_unref (priv->hadjust);
612 priv->hadjust = NULL;
615 g_object_unref (priv->vadjust);
616 priv->vadjust = NULL;
619 if (G_OBJECT_CLASS (hildon_pannable_area_parent_class)->dispose)
620 G_OBJECT_CLASS (hildon_pannable_area_parent_class)->dispose (object);
624 hildon_pannable_area_realize (GtkWidget * widget)
626 GdkWindowAttr attributes;
627 gint attributes_mask;
629 HildonPannableAreaPrivate *priv;
631 priv = PANNABLE_AREA_PRIVATE (widget);
633 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
635 border_width = GTK_CONTAINER (widget)->border_width;
637 attributes.x = widget->allocation.x + border_width;
638 attributes.y = widget->allocation.y + border_width;
639 attributes.width = MAX (widget->allocation.width - 2 * border_width -
640 (priv->vscroll_visible ? priv->vscroll_rect.width : 0), 0);
641 attributes.height = MAX (widget->allocation.height - 2 * border_width -
642 (priv->hscroll_visible ? priv->hscroll_rect.height : 0), 0);
643 attributes.window_type = GDK_WINDOW_CHILD;
644 attributes.event_mask = gtk_widget_get_events (widget)
645 | GDK_BUTTON_MOTION_MASK
646 | GDK_BUTTON_PRESS_MASK
647 | GDK_BUTTON_RELEASE_MASK
649 | GDK_EXPOSURE_MASK | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK;
651 widget->window = gtk_widget_get_parent_window (widget);
652 g_object_ref (widget->window);
654 attributes.wclass = GDK_INPUT_ONLY;
655 attributes_mask = GDK_WA_X | GDK_WA_Y;
657 priv->event_window = gdk_window_new (widget->window,
658 &attributes, attributes_mask);
659 gdk_window_set_user_data (priv->event_window, widget);
661 widget->style = gtk_style_attach (widget->style, widget->window);
665 hildon_pannable_area_unrealize (GtkWidget * widget)
667 HildonPannableAreaPrivate *priv;
669 priv = PANNABLE_AREA_PRIVATE (widget);
671 if (priv->event_window != NULL) {
672 gdk_window_set_user_data (priv->event_window, NULL);
673 gdk_window_destroy (priv->event_window);
674 priv->event_window = NULL;
677 if (GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->unrealize)
678 (*GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->unrealize)(widget);
682 hildon_pannable_area_size_request (GtkWidget * widget,
683 GtkRequisition * requisition)
685 GtkRequisition child_requisition;
686 HildonPannableAreaPrivate *priv = PANNABLE_AREA_PRIVATE (widget);
687 GtkWidget *child = gtk_bin_get_child (GTK_BIN (widget));
689 if (child && GTK_WIDGET_VISIBLE (child))
691 gtk_widget_size_request (child, &child_requisition);
694 if (priv->hscrollbar_policy == GTK_POLICY_NEVER) {
695 requisition->width += child_requisition.width;
697 requisition->width += priv->area_width;
700 if (priv->vscrollbar_policy == GTK_POLICY_NEVER) {
701 requisition->height += child_requisition.height;
703 requisition->height += priv->area_width;
706 requisition->width += GTK_CONTAINER (widget)->border_width;
707 requisition->height += GTK_CONTAINER (widget)->border_width;
711 hildon_pannable_area_size_allocate (GtkWidget * widget,
712 GtkAllocation * allocation)
714 GtkAllocation child_allocation;
715 HildonPannableAreaPrivate *priv;
716 GtkWidget *child = gtk_bin_get_child (GTK_BIN (widget));
718 widget->allocation = *allocation;
720 priv = PANNABLE_AREA_PRIVATE (widget);
722 child_allocation.x = allocation->x + GTK_CONTAINER (widget)->border_width;
723 child_allocation.y = allocation->y + GTK_CONTAINER (widget)->border_width;
724 child_allocation.width = MAX (allocation->width -
725 2 * GTK_CONTAINER (widget)->border_width -
726 (priv->vscroll_visible ? priv->vscroll_rect.width : 0), 0);
727 child_allocation.height = MAX (allocation->height -
728 2 * GTK_CONTAINER (widget)->border_width -
729 (priv->hscroll_visible ? priv->hscroll_rect.height : 0), 0);
731 if (GTK_WIDGET_REALIZED (widget)) {
732 if (priv->event_window != NULL)
733 gdk_window_move_resize (priv->event_window,
736 child_allocation.width,
737 child_allocation.height);
740 if (priv->overshot_dist_y > 0) {
741 child_allocation.y = MIN (child_allocation.y + priv->overshot_dist_y,
742 allocation->y + child_allocation.height);
743 child_allocation.height = MAX (child_allocation.height - priv->overshot_dist_y, 0);
744 } else if (priv->overshot_dist_y < 0) {
745 child_allocation.height = MAX (child_allocation.height + priv->overshot_dist_y, 0);
748 if (priv->overshot_dist_x > 0) {
749 child_allocation.x = MIN (child_allocation.x + priv->overshot_dist_x,
750 allocation->x + child_allocation.width);
751 child_allocation.width = MAX (child_allocation.width - priv->overshot_dist_x, 0);
752 } else if (priv->overshot_dist_x < 0) {
753 child_allocation.width = MAX (child_allocation.width + priv->overshot_dist_x, 0);
757 gtk_widget_size_allocate (child, &child_allocation);
759 /* we have to do this after child size_allocate because page_size is
760 * changed when we allocate the size of the children */
761 if (priv->overshot_dist_y < 0) {
762 gtk_adjustment_set_value (priv->vadjust, priv->vadjust->upper -
763 priv->vadjust->page_size);
766 if (priv->overshot_dist_x < 0) {
767 gtk_adjustment_set_value (priv->hadjust, priv->hadjust->upper -
768 priv->hadjust->page_size);
771 hildon_pannable_area_refresh (HILDON_PANNABLE_AREA (widget));
775 hildon_pannable_area_style_set (GtkWidget * widget,
776 GtkStyle * previous_style)
778 HildonPannableAreaPrivate *priv = PANNABLE_AREA_PRIVATE (widget);
780 GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->
781 style_set (widget, previous_style);
783 gtk_widget_style_get (widget, "indicator-width", &priv->area_width, NULL);
787 hildon_pannable_area_map (GtkWidget * widget)
789 HildonPannableAreaPrivate *priv;
791 priv = PANNABLE_AREA_PRIVATE (widget);
793 if (priv->event_window != NULL && !priv->enabled)
794 gdk_window_show (priv->event_window);
796 (*GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->map) (widget);
798 if (priv->event_window != NULL && priv->enabled)
799 gdk_window_show (priv->event_window);
803 hildon_pannable_area_unmap (GtkWidget * widget)
805 HildonPannableAreaPrivate *priv;
807 priv = PANNABLE_AREA_PRIVATE (widget);
809 if (priv->event_window != NULL)
810 gdk_window_hide (priv->event_window);
812 (*GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->unmap) (widget);
816 hildon_pannable_area_grab_notify (GtkWidget *widget,
817 gboolean was_grabbed,
820 /* an internal widget has grabbed the focus and now has returned it,
821 we have to do some release actions */
823 HildonPannableAreaPrivate *priv = PANNABLE_AREA_PRIVATE (widget);
825 priv->scroll_indicator_event_interrupt = 0;
826 priv->scroll_delay_counter = SCROLLBAR_FADE_DELAY;
828 if (!priv->scroll_indicator_timeout) {
829 priv->scroll_indicator_timeout = g_timeout_add
830 ((gint) (1000.0 / (gdouble) priv->sps),
831 (GSourceFunc) hildon_pannable_area_scroll_indicator_fade, widget);
840 rgb_from_gdkcolor (GdkColor *color, gdouble *r, gdouble *g, gdouble *b)
842 *r = (color->red >> 8) / 255.0;
843 *g = (color->green >> 8) / 255.0;
844 *b = (color->blue >> 8) / 255.0;
848 hildon_pannable_draw_vscroll (GtkWidget * widget,
849 GdkColor *back_color,
850 GdkColor *scroll_color)
852 HildonPannableAreaPrivate *priv = PANNABLE_AREA_PRIVATE (widget);
855 cairo_pattern_t *pattern;
857 gint radius = (priv->vscroll_rect.width/2) - 1;
859 cr = gdk_cairo_create(widget->window);
861 /* Draw the background */
862 rgb_from_gdkcolor (back_color, &r, &g, &b);
863 cairo_set_source_rgb (cr, r, g, b);
864 cairo_rectangle(cr, priv->vscroll_rect.x, priv->vscroll_rect.y,
865 priv->vscroll_rect.width,
866 priv->vscroll_rect.height);
867 cairo_fill_preserve (cr);
870 /* Calculate the scroll bar height and position */
871 y = widget->allocation.y +
872 ((priv->vadjust->value / priv->vadjust->upper) *
873 (widget->allocation.height -
874 (priv->hscroll_visible ? priv->area_width : 0)));
875 height = (widget->allocation.y +
876 (((priv->vadjust->value +
877 priv->vadjust->page_size) /
878 priv->vadjust->upper) *
879 (widget->allocation.height -
880 (priv->hscroll_visible ? priv->area_width : 0)))) - y;
882 /* Set a minimum height */
883 height = MAX (SCROLL_BAR_MIN_SIZE, height);
885 /* Check the max y position */
886 y = MIN (y, widget->allocation.y + widget->allocation.height -
887 (priv->hscroll_visible ? priv->hscroll_rect.height : 0) -
890 /* Draw the scrollbar */
891 rgb_from_gdkcolor (scroll_color, &r, &g, &b);
893 pattern = cairo_pattern_create_linear(radius+1, y, radius+1,y + height);
894 cairo_pattern_add_color_stop_rgb(pattern, 0, r, g, b);
895 cairo_pattern_add_color_stop_rgb(pattern, 1, r/2, g/2, b/2);
896 cairo_set_source(cr, pattern);
898 cairo_pattern_destroy(pattern);
900 cairo_arc(cr, priv->vscroll_rect.x + radius + 1, y + radius + 1, radius, G_PI, 0);
901 cairo_line_to(cr, priv->vscroll_rect.x + (radius * 2) + 1, y + height - radius);
902 cairo_arc(cr, priv->vscroll_rect.x + radius + 1, y + height - radius, radius, 0, G_PI);
903 cairo_line_to(cr, priv->vscroll_rect.x + 1, y + height - radius);
906 cairo_paint_with_alpha(cr, priv->scroll_indicator_alpha);
912 hildon_pannable_draw_hscroll (GtkWidget * widget,
913 GdkColor *back_color,
914 GdkColor *scroll_color)
916 HildonPannableAreaPrivate *priv = PANNABLE_AREA_PRIVATE (widget);
919 cairo_pattern_t *pattern;
921 gint radius = (priv->hscroll_rect.height/2) - 1;
923 cr = gdk_cairo_create(widget->window);
925 /* Draw the background */
926 rgb_from_gdkcolor (back_color, &r, &g, &b);
927 cairo_set_source_rgb (cr, r, g, b);
928 cairo_rectangle(cr, priv->hscroll_rect.x, priv->hscroll_rect.y,
929 priv->hscroll_rect.width,
930 priv->hscroll_rect.height);
931 cairo_fill_preserve (cr);
934 /* calculate the scrollbar width and position */
935 x = widget->allocation.x +
936 ((priv->hadjust->value / priv->hadjust->upper) *
937 (widget->allocation.width - (priv->vscroll_visible ? priv->area_width : 0)));
939 (widget->allocation.x +
940 (((priv->hadjust->value +
941 priv->hadjust->page_size) / priv->hadjust->upper) *
942 (widget->allocation.width -
943 (priv->vscroll_visible ? priv->area_width : 0)))) - x;
945 /* Set a minimum width */
946 width = MAX (SCROLL_BAR_MIN_SIZE, width);
948 /* Check the max x position */
949 x = MIN (x, widget->allocation.x + widget->allocation.width -
950 (priv->vscroll_visible ? priv->vscroll_rect.width : 0) -
953 /* Draw the scrollbar */
954 rgb_from_gdkcolor (scroll_color, &r, &g, &b);
956 pattern = cairo_pattern_create_linear(x, radius+1, x+width, radius+1);
957 cairo_pattern_add_color_stop_rgb(pattern, 0, r, g, b);
958 cairo_pattern_add_color_stop_rgb(pattern, 1, r/2, g/2, b/2);
959 cairo_set_source(cr, pattern);
961 cairo_pattern_destroy(pattern);
963 cairo_arc_negative(cr, x + radius + 1, priv->hscroll_rect.y + radius + 1, radius, 3*G_PI_2, G_PI_2);
964 cairo_line_to(cr, x + width - radius, priv->hscroll_rect.y + (radius * 2) + 1);
965 cairo_arc_negative(cr, x + width - radius, priv->hscroll_rect.y + radius + 1, radius, G_PI_2, 3*G_PI_2);
966 cairo_line_to(cr, x + width - radius, priv->hscroll_rect.y + 1);
969 cairo_paint_with_alpha(cr, priv->scroll_indicator_alpha);
975 hildon_pannable_area_initial_effect (GtkWidget * widget)
977 HildonPannableAreaPrivate *priv = PANNABLE_AREA_PRIVATE (widget);
978 gboolean hscroll_visible, vscroll_visible;
980 if (priv->initial_hint) {
981 if (((priv->vovershoot_max != 0)||(priv->hovershoot_max != 0)) &&
982 ((priv->mode == HILDON_PANNABLE_AREA_MODE_AUTO) ||
983 (priv->mode == HILDON_PANNABLE_AREA_MODE_ACCEL))) {
984 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
985 priv->vadjust->page_size);
986 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
987 priv->hadjust->page_size);
988 /* If scrolling is possible in both axes, only hint about scrolling in
990 if ((vscroll_visible)&&(priv->vovershoot_max != 0)) {
991 priv->overshot_dist_y = priv->vovershoot_max;
992 priv->vel_y = priv->vmax * 0.1;
993 } else if ((hscroll_visible)&&(priv->hovershoot_max != 0)) {
994 priv->overshot_dist_x = priv->hovershoot_max;
995 priv->vel_x = priv->vmax * 0.1;
998 if (vscroll_visible || hscroll_visible) {
999 priv->idle_id = g_timeout_add ((gint) (1000.0 / (gdouble) priv->sps),
1001 hildon_pannable_area_timeout, widget);
1005 if (priv->vscroll_visible || priv->hscroll_visible) {
1006 priv->scroll_indicator_alpha = 1.0;
1008 priv->scroll_indicator_timeout =
1009 g_timeout_add ((gint) (1000.0 / (gdouble) priv->sps),
1010 (GSourceFunc) hildon_pannable_area_scroll_indicator_fade,
1017 hildon_pannable_area_redraw (HildonPannableArea * area)
1019 HildonPannableAreaPrivate *priv = PANNABLE_AREA_PRIVATE (area);
1021 /* Redraw scroll indicators */
1022 if (GTK_WIDGET_DRAWABLE (area)) {
1023 if (priv->hscroll_visible) {
1024 gdk_window_invalidate_rect (GTK_WIDGET (area)->window,
1025 &priv->hscroll_rect, FALSE);
1028 if (priv->vscroll_visible) {
1029 gdk_window_invalidate_rect (GTK_WIDGET (area)->window,
1030 &priv->vscroll_rect, FALSE);
1036 hildon_pannable_area_scroll_indicator_fade(HildonPannableArea * area)
1039 HildonPannableAreaPrivate *priv;
1041 GDK_THREADS_ENTER ();
1043 priv = PANNABLE_AREA_PRIVATE (area);
1045 /* if moving do not fade out */
1046 if (((ABS (priv->vel_y)>1.0)||
1047 (ABS (priv->vel_x)>1.0))&&(!priv->clicked)) {
1049 GDK_THREADS_LEAVE ();
1054 if (!priv->scroll_indicator_timeout) {
1056 GDK_THREADS_LEAVE ();
1061 if (priv->scroll_indicator_event_interrupt) {
1062 /* Stop a fade out, and fade back in */
1063 if (priv->scroll_indicator_alpha >= 0.9) {
1064 priv->scroll_indicator_timeout = 0;
1065 priv->scroll_indicator_alpha = 1.0;
1068 priv->scroll_indicator_alpha += 0.2;
1071 hildon_pannable_area_redraw (area);
1074 if ((priv->scroll_indicator_alpha > 0.9) &&
1075 (priv->scroll_delay_counter > 0)) {
1076 priv->scroll_delay_counter--;
1078 GDK_THREADS_LEAVE ();
1083 if (!priv->scroll_indicator_event_interrupt) {
1084 /* Continue fade out */
1085 if (priv->scroll_indicator_alpha <= 0.1) {
1086 priv->scroll_indicator_timeout = 0;
1087 priv->scroll_delay_counter = SCROLLBAR_FADE_DELAY;
1088 priv->scroll_indicator_alpha = 0.0;
1091 priv->scroll_indicator_alpha -= 0.2;
1094 hildon_pannable_area_redraw (area);
1097 GDK_THREADS_LEAVE ();
1103 hildon_pannable_area_expose_event (GtkWidget * widget,
1104 GdkEventExpose * event)
1107 HildonPannableAreaPrivate *priv = PANNABLE_AREA_PRIVATE (widget);
1108 GdkColor back_color = widget->style->bg[GTK_STATE_NORMAL];
1109 GdkColor scroll_color = widget->style->base[GTK_STATE_SELECTED];
1111 if (gtk_bin_get_child (GTK_BIN (widget))) {
1113 if (priv->scroll_indicator_alpha > 0.1) {
1114 if (priv->vscroll_visible) {
1115 hildon_pannable_draw_vscroll (widget, &back_color, &scroll_color);
1117 if (priv->hscroll_visible) {
1118 hildon_pannable_draw_hscroll (widget, &back_color, &scroll_color);
1122 /* draw overshooting rectangles */
1123 if (priv->overshot_dist_y > 0) {
1124 gint overshot_height;
1126 overshot_height = MIN (priv->overshot_dist_y, widget->allocation.height -
1127 (priv->hscroll_visible ? priv->hscroll_rect.height : 0));
1129 gdk_draw_rectangle (widget->window,
1130 widget->style->bg_gc[GTK_STATE_NORMAL],
1132 widget->allocation.x,
1133 widget->allocation.y,
1134 widget->allocation.width -
1135 (priv->vscroll_visible ? priv->vscroll_rect.width : 0),
1137 } else if (priv->overshot_dist_y < 0) {
1138 gint overshot_height;
1142 MAX (priv->overshot_dist_y,
1143 -(widget->allocation.height -
1144 (priv->hscroll_visible ? priv->hscroll_rect.height : 0)));
1146 overshot_y = MAX (widget->allocation.y +
1147 widget->allocation.height +
1149 (priv->hscroll_visible ? priv->hscroll_rect.height : 0), 0);
1151 gdk_draw_rectangle (widget->window,
1152 widget->style->bg_gc[GTK_STATE_NORMAL],
1154 widget->allocation.x,
1156 widget->allocation.width -
1157 priv->vscroll_rect.width,
1161 if (priv->overshot_dist_x > 0) {
1162 gint overshot_width;
1164 overshot_width = MIN (priv->overshot_dist_x, widget->allocation.width -
1165 (priv->vscroll_visible ? priv->vscroll_rect.width : 0));
1167 gdk_draw_rectangle (widget->window,
1168 widget->style->bg_gc[GTK_STATE_NORMAL],
1170 widget->allocation.x,
1171 widget->allocation.y,
1173 widget->allocation.height -
1174 (priv->hscroll_visible ? priv->hscroll_rect.height : 0));
1175 } else if (priv->overshot_dist_x < 0) {
1176 gint overshot_width;
1180 MAX (priv->overshot_dist_x,
1181 -(widget->allocation.width -
1182 (priv->vscroll_visible ? priv->vscroll_rect.width : 0)));
1184 overshot_x = MAX (widget->allocation.x +
1185 widget->allocation.width +
1187 (priv->vscroll_visible ? priv->vscroll_rect.width : 0), 0);
1189 gdk_draw_rectangle (widget->window,
1190 widget->style->bg_gc[GTK_STATE_NORMAL],
1193 widget->allocation.y,
1195 widget->allocation.height -
1196 priv->hscroll_rect.height);
1201 if (G_UNLIKELY (priv->initial_effect)) {
1203 hildon_pannable_area_initial_effect (widget);
1205 priv->initial_effect = FALSE;
1208 return GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->expose_event (widget, event);
1212 hildon_pannable_area_get_topmost (GdkWindow * window,
1214 gint * tx, gint * ty)
1216 /* Find the GdkWindow at the given point, by recursing from a given
1217 * parent GdkWindow. Optionally return the co-ordinates transformed
1218 * relative to the child window.
1222 gdk_drawable_get_size (GDK_DRAWABLE (window), &width, &height);
1223 if ((x < 0) || (x >= width) || (y < 0) || (y >= height))
1227 gint child_x = 0, child_y = 0;
1228 GList *c, *children = gdk_window_peek_children (window);
1229 GdkWindow *old_window = window;
1231 for (c = children; c; c = c->next) {
1232 GdkWindow *child = (GdkWindow *) c->data;
1235 gdk_window_get_geometry (child, &wx, &wy, &width, &height, NULL);
1237 if (((x >= wx) && (x < (wx + width)) && (y >= wy)
1238 && (y < (wy + height))) && (gdk_window_is_visible (child))) {
1245 if (window == old_window)
1261 synth_crossing (GdkWindow * child,
1263 gint x_root, gint y_root,
1264 guint32 time, gboolean in)
1266 GdkEventCrossing *crossing_event;
1267 GdkEventType type = in ? GDK_ENTER_NOTIFY : GDK_LEAVE_NOTIFY;
1269 /* Send synthetic enter event */
1270 crossing_event = (GdkEventCrossing *) gdk_event_new (type);
1271 ((GdkEventAny *) crossing_event)->type = type;
1272 ((GdkEventAny *) crossing_event)->window = g_object_ref (child);
1273 ((GdkEventAny *) crossing_event)->send_event = FALSE;
1274 crossing_event->subwindow = g_object_ref (child);
1275 crossing_event->time = time;
1276 crossing_event->x = x;
1277 crossing_event->y = y;
1278 crossing_event->x_root = x_root;
1279 crossing_event->y_root = y_root;
1280 crossing_event->mode = GDK_CROSSING_NORMAL;
1281 crossing_event->detail = GDK_NOTIFY_UNKNOWN;
1282 crossing_event->focus = FALSE;
1283 crossing_event->state = 0;
1284 gdk_event_put ((GdkEvent *) crossing_event);
1285 gdk_event_free ((GdkEvent *) crossing_event);
1289 hildon_pannable_area_button_press_cb (GtkWidget * widget,
1290 GdkEventButton * event)
1293 HildonPannableAreaPrivate *priv = PANNABLE_AREA_PRIVATE (widget);
1295 if ((!priv->enabled) || (event->button != 1) ||
1296 ((event->time == priv->last_time) &&
1297 (priv->last_type == 1)) || (gtk_bin_get_child (GTK_BIN (widget)) == NULL))
1300 priv->scroll_indicator_event_interrupt = 1;
1301 priv->scroll_delay_counter = SCROLLBAR_FADE_DELAY;
1303 if (!priv->scroll_indicator_timeout){
1304 priv->scroll_indicator_timeout = g_timeout_add
1305 ((gint) (1000.0 / (gdouble) (priv->sps*2)),
1306 (GSourceFunc) hildon_pannable_area_scroll_indicator_fade, widget);
1309 priv->last_time = event->time;
1310 priv->last_type = 1;
1312 priv->scroll_to_x = -1;
1313 priv->scroll_to_y = -1;
1315 if (priv->clicked && priv->child) {
1316 /* Widget stole focus on last click, send crossing-out event */
1317 synth_crossing (priv->child, 0, 0, event->x_root, event->y_root,
1318 event->time, FALSE);
1326 /* Don't allow a click if we're still moving fast */
1327 if ((ABS (priv->vel_x) <= (priv->vmax * priv->vfast_factor)) &&
1328 (ABS (priv->vel_y) <= (priv->vmax * priv->vfast_factor)))
1330 hildon_pannable_area_get_topmost (gtk_bin_get_child (GTK_BIN (widget))->window,
1331 event->x, event->y, &x, &y);
1335 priv->clicked = TRUE;
1337 /* Stop scrolling on mouse-down (so you can flick, then hold to stop) */
1341 if ((priv->child) && (priv->child != gtk_bin_get_child (GTK_BIN (widget))->window)) {
1343 g_object_add_weak_pointer ((GObject *) priv->child,
1344 (gpointer) & priv->child);
1346 event = (GdkEventButton *) gdk_event_copy ((GdkEvent *) event);
1352 synth_crossing (priv->child, x, y, event->x_root,
1353 event->y_root, event->time, TRUE);
1355 /* Send synthetic click (button press/release) event */
1356 ((GdkEventAny *) event)->window = g_object_ref (priv->child);
1358 gdk_event_put ((GdkEvent *) event);
1359 gdk_event_free ((GdkEvent *) event);
1367 hildon_pannable_area_refresh (HildonPannableArea * area)
1369 HildonPannableAreaPrivate *priv = PANNABLE_AREA_PRIVATE (area);
1370 gboolean prev_hscroll_visible, prev_vscroll_visible;
1372 if (!gtk_bin_get_child (GTK_BIN (area))) {
1373 priv->vscroll_visible = FALSE;
1374 priv->hscroll_visible = FALSE;
1378 prev_hscroll_visible = priv->hscroll_visible;
1379 prev_vscroll_visible = priv->vscroll_visible;
1381 switch (priv->hscrollbar_policy) {
1382 case GTK_POLICY_ALWAYS:
1383 priv->hscroll_visible = TRUE;
1385 case GTK_POLICY_NEVER:
1386 priv->hscroll_visible = FALSE;
1389 priv->hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
1390 priv->hadjust->page_size);
1393 switch (priv->vscrollbar_policy) {
1394 case GTK_POLICY_ALWAYS:
1395 priv->vscroll_visible = TRUE;
1397 case GTK_POLICY_NEVER:
1398 priv->vscroll_visible = FALSE;
1401 priv->vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
1402 priv->vadjust->page_size);
1405 /* Store the vscroll/hscroll areas for redrawing */
1406 if (priv->vscroll_visible) {
1407 GtkAllocation *allocation = >K_WIDGET (area)->allocation;
1408 priv->vscroll_rect.x = allocation->x + allocation->width -
1410 priv->vscroll_rect.y = allocation->y;
1411 priv->vscroll_rect.width = priv->area_width;
1412 priv->vscroll_rect.height = allocation->height -
1413 (priv->hscroll_visible ? priv->area_width : 0);
1415 if (priv->hscroll_visible) {
1416 GtkAllocation *allocation = >K_WIDGET (area)->allocation;
1417 priv->hscroll_rect.y = allocation->y + allocation->height -
1419 priv->hscroll_rect.x = allocation->x;
1420 priv->hscroll_rect.height = priv->area_width;
1421 priv->hscroll_rect.width = allocation->width -
1422 (priv->vscroll_visible ? priv->area_width : 0);
1425 if (GTK_WIDGET_DRAWABLE (area)) {
1426 if (priv->hscroll_visible != prev_hscroll_visible) {
1427 gtk_widget_queue_resize (GTK_WIDGET (area));
1430 if (priv->vscroll_visible != prev_vscroll_visible) {
1431 gtk_widget_queue_resize (GTK_WIDGET (area));
1437 /* Scroll by a particular amount (in pixels). Optionally, return if
1438 * the scroll on a particular axis was successful.
1441 hildon_pannable_axis_scroll (HildonPannableArea *area,
1442 GtkAdjustment *adjust,
1446 gint *overshot_dist,
1452 HildonPannableAreaPrivate *priv = PANNABLE_AREA_PRIVATE (area);
1454 dist = gtk_adjustment_get_value (adjust) - inc;
1457 * We use overshot_dist to define the distance of the current overshoot,
1458 * and overshooting to define the direction/whether or not we are overshot
1460 if (!(*overshooting)) {
1462 /* Initiation of the overshoot happens when the finger is released
1463 * and the current position of the pannable contents are out of range
1465 if (dist < adjust->lower) {
1468 dist = adjust->lower;
1470 if (overshoot_max!=0) {
1473 *overshot_dist = CLAMP (*overshot_dist + *vel, 0, overshoot_max);
1474 gtk_widget_queue_resize (GTK_WIDGET (area));
1478 } else if (dist > adjust->upper - adjust->page_size) {
1481 dist = adjust->upper - adjust->page_size;
1483 if (overshoot_max!=0) {
1486 *overshot_dist = CLAMP (*overshot_dist + *vel, -overshoot_max, 0);
1487 gtk_widget_queue_resize (GTK_WIDGET (area));
1492 if ((*scroll_to) != -1) {
1493 if (((inc < 0)&&(*scroll_to <= dist))||
1494 ((inc > 0)&&(*scroll_to >= dist))) {
1502 gtk_adjustment_set_value (adjust, dist);
1504 if (!priv->clicked) {
1506 /* When the overshoot has started we continue for BOUNCE_STEPS more steps into the overshoot
1507 * before we reverse direction. The deceleration factor is calculated based on
1508 * the percentage distance from the first item with each iteration, therefore always
1509 * returning us to the top/bottom most element
1511 if (*overshot_dist > 0) {
1513 if ((*overshooting < BOUNCE_STEPS) && (*vel > 0)) {
1515 *vel = (((gdouble)*overshot_dist)/overshoot_max) * (*vel);
1516 } else if ((*overshooting >= BOUNCE_STEPS) && (*vel > 0)) {
1519 } else if ((*overshooting > 1) && (*vel < 0)) {
1521 /* we add the MAX in order to avoid very small speeds */
1522 *vel = MIN ((((gdouble)*overshot_dist)/overshoot_max) * (*vel), -10.0);
1525 *overshot_dist = CLAMP (*overshot_dist + *vel, 0, overshoot_max);
1527 gtk_widget_queue_resize (GTK_WIDGET (area));
1529 } else if (*overshot_dist < 0) {
1531 if ((*overshooting < BOUNCE_STEPS) && (*vel < 0)) {
1533 *vel = (((gdouble)*overshot_dist)/overshoot_max) * (*vel) * -1;
1534 } else if ((*overshooting >= BOUNCE_STEPS) && (*vel < 0)) {
1537 } else if ((*overshooting > 1) && (*vel > 0)) {
1539 /* we add the MIN in order to avoid very small speeds */
1540 *vel = MAX ((((gdouble)*overshot_dist)/overshoot_max) * (*vel) * -1, 10.0);
1543 *overshot_dist = CLAMP (*overshot_dist + (*vel), -overshoot_max, 0);
1545 gtk_widget_queue_resize (GTK_WIDGET (area));
1550 gtk_widget_queue_resize (GTK_WIDGET (area));
1553 if (*overshot_dist > 0) {
1554 *overshot_dist = CLAMP ((*overshot_dist) + inc, 0, overshoot_max);
1555 } else if (*overshot_dist < 0) {
1556 *overshot_dist = CLAMP ((*overshot_dist) + inc, -1 * overshoot_max, 0);
1559 gtk_adjustment_set_value (adjust, dist);
1561 gtk_widget_queue_resize (GTK_WIDGET (area));
1567 hildon_pannable_area_scroll (HildonPannableArea *area,
1568 gdouble x, gdouble y)
1571 HildonPannableAreaPrivate *priv;
1572 gboolean hscroll_visible, vscroll_visible;
1574 priv = PANNABLE_AREA_PRIVATE (area);
1576 if (gtk_bin_get_child (GTK_BIN (area)) == NULL)
1579 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
1580 priv->vadjust->page_size);
1581 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
1582 priv->hadjust->page_size);
1587 if (vscroll_visible) {
1588 hildon_pannable_axis_scroll (area, priv->vadjust, &priv->vel_y, y,
1589 &priv->overshooting_y, &priv->overshot_dist_y,
1590 &priv->scroll_to_y, priv->vovershoot_max, &sy);
1593 if (hscroll_visible) {
1594 hildon_pannable_axis_scroll (area, priv->hadjust, &priv->vel_x, x,
1595 &priv->overshooting_x, &priv->overshot_dist_x,
1596 &priv->scroll_to_x, priv->hovershoot_max, &sx);
1599 /* If the scroll on a particular axis wasn't succesful, reset the
1600 * initial scroll position to the new mouse co-ordinate. This means
1601 * when you get to the top of the page, dragging down works immediately.
1614 hildon_pannable_area_timeout (HildonPannableArea * area)
1616 HildonPannableAreaPrivate *priv;
1618 GDK_THREADS_ENTER ();
1620 priv = PANNABLE_AREA_PRIVATE (area);
1622 if ((!priv->enabled) || (priv->mode == HILDON_PANNABLE_AREA_MODE_PUSH)) {
1625 GDK_THREADS_LEAVE ();
1630 if (!priv->clicked) {
1631 /* Decelerate gradually when pointer is raised */
1632 if ((!priv->overshot_dist_y) &&
1633 (!priv->overshot_dist_x)) {
1635 /* in case we move to a specific point do not decelerate when arriving */
1636 if ((priv->scroll_to_x != -1)||(priv->scroll_to_y != -1)) {
1638 if (ABS (priv->vel_x) >= 1.5) {
1639 priv->vel_x *= priv->decel;
1642 if (ABS (priv->vel_y) >= 1.5) {
1643 priv->vel_y *= priv->decel;
1647 priv->vel_x *= priv->decel;
1648 priv->vel_y *= priv->decel;
1650 if ((ABS (priv->vel_x) < 1.0) && (ABS (priv->vel_y) < 1.0)) {
1655 GDK_THREADS_LEAVE ();
1661 } else if (priv->mode == HILDON_PANNABLE_AREA_MODE_AUTO) {
1664 GDK_THREADS_LEAVE ();
1669 hildon_pannable_area_scroll (area, priv->vel_x, priv->vel_y);
1671 GDK_THREADS_LEAVE ();
1677 hildon_pannable_area_motion_notify_cb (GtkWidget * widget,
1678 GdkEventMotion * event)
1680 HildonPannableArea *area = HILDON_PANNABLE_AREA (widget);
1681 HildonPannableAreaPrivate *priv = PANNABLE_AREA_PRIVATE (area);
1684 gdouble delta, rawvel_x, rawvel_y;
1685 gint direction_x, direction_y;
1687 if (gtk_bin_get_child (GTK_BIN (widget)) == NULL)
1690 if ((!priv->enabled) || (!priv->clicked) ||
1691 ((event->time == priv->last_time) && (priv->last_type == 2))) {
1692 gdk_window_get_pointer (widget->window, NULL, NULL, 0);
1696 if (priv->last_type == 1) {
1697 priv->first_drag = TRUE;
1700 /* Only start the scroll if the mouse cursor passes beyond the
1701 * DnD threshold for dragging.
1703 g_object_get (G_OBJECT (gtk_settings_get_default ()),
1704 "gtk-dnd-drag-threshold", &dnd_threshold, NULL);
1705 x = event->x - priv->x;
1706 y = event->y - priv->y;
1708 if (priv->first_drag && (!priv->moved) &&
1709 ((ABS (x) > (dnd_threshold+DND_THRESHOLD_INC))
1710 || (ABS (y) > (dnd_threshold+DND_THRESHOLD_INC)))) {
1715 if (priv->first_drag) {
1717 if (ABS (priv->iy - event->y) >=
1718 ABS (priv->ix - event->x)) {
1719 gboolean vscroll_visible;
1721 g_signal_emit (area,
1722 pannable_area_signals[VERTICAL_MOVEMENT],
1723 0, (priv->iy > event->y) ?
1724 HILDON_MOVEMENT_UP :
1725 HILDON_MOVEMENT_DOWN,
1726 (gdouble)priv->ix, (gdouble)priv->iy);
1728 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
1729 priv->vadjust->page_size);
1731 if (!((vscroll_visible)&&
1732 (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT)))
1733 priv->moved = FALSE;
1736 gboolean hscroll_visible;
1738 g_signal_emit (area,
1739 pannable_area_signals[HORIZONTAL_MOVEMENT],
1740 0, (priv->ix > event->x) ?
1741 HILDON_MOVEMENT_LEFT :
1742 HILDON_MOVEMENT_RIGHT,
1743 (gdouble)priv->ix, (gdouble)priv->iy);
1745 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
1746 priv->hadjust->page_size);
1748 if (!((hscroll_visible)&&
1749 (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ)))
1750 priv->moved = FALSE;
1754 priv->first_drag = FALSE;
1756 if ((priv->mode != HILDON_PANNABLE_AREA_MODE_PUSH) &&
1757 (priv->mode != HILDON_PANNABLE_AREA_MODE_AUTO)) {
1760 priv->idle_id = g_timeout_add ((gint)
1761 (1000.0 / (gdouble) priv->sps),
1763 hildon_pannable_area_timeout, area);
1768 switch (priv->mode) {
1769 case HILDON_PANNABLE_AREA_MODE_PUSH:
1770 /* Scroll by the amount of pixels the cursor has moved
1771 * since the last motion event.
1773 hildon_pannable_area_scroll (area, x, y);
1777 case HILDON_PANNABLE_AREA_MODE_ACCEL:
1778 /* Set acceleration relative to the initial click */
1779 priv->ex = event->x;
1780 priv->ey = event->y;
1781 priv->vel_x = ((x > 0) ? 1 : -1) *
1783 (gdouble) widget->allocation.width) *
1784 (priv->vmax - priv->vmin)) + priv->vmin);
1785 priv->vel_y = ((y > 0) ? 1 : -1) *
1787 (gdouble) widget->allocation.height) *
1788 (priv->vmax - priv->vmin)) + priv->vmin);
1790 case HILDON_PANNABLE_AREA_MODE_AUTO:
1792 delta = event->time - priv->last_time;
1794 if (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ) {
1795 rawvel_x = (((event->x - priv->x) / ABS (delta)) *
1796 (gdouble) priv->sps) * FORCE;
1797 /* we store the direction and after the calculation we
1798 change it, this reduces the ifs for the calculation */
1799 direction_x = rawvel_x < 0 ? -1 : 1;
1800 rawvel_x = ABS (rawvel_x);
1801 priv->vel_x = priv->vel_x * (1 - SMOOTH_FACTOR) +
1802 direction_x * rawvel_x * SMOOTH_FACTOR;
1803 priv->vel_x = priv->vel_x > 0 ? MIN (priv->vel_x, priv->vmax)
1804 : MAX (priv->vel_x, -1 * priv->vmax);
1810 if (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT) {
1811 rawvel_y = (((event->y - priv->y) / ABS (delta)) *
1812 (gdouble) priv->sps) * FORCE;
1813 direction_y = rawvel_y < 0 ? -1 : 1;
1814 rawvel_y = ABS (rawvel_y);
1815 priv->vel_y = priv->vel_y * (1 - SMOOTH_FACTOR) +
1816 direction_y * rawvel_y * SMOOTH_FACTOR;
1817 priv->vel_y = priv->vel_y > 0 ? MIN (priv->vel_y, priv->vmax)
1818 : MAX (priv->vel_y, -1 * priv->vmax);
1824 hildon_pannable_area_scroll (area, x, y);
1826 if (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ)
1828 if (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT)
1839 /* Send motion notify to child */
1840 priv->last_time = event->time;
1841 priv->last_type = 2;
1842 event = (GdkEventMotion *) gdk_event_copy ((GdkEvent *) event);
1843 event->x = priv->cx + (event->x - priv->ix);
1844 event->y = priv->cy + (event->y - priv->iy);
1845 event->window = g_object_ref (priv->child);
1846 gdk_event_put ((GdkEvent *) event);
1847 gdk_event_free ((GdkEvent *) event);
1850 gdk_window_get_pointer (widget->window, NULL, NULL, 0);
1856 hildon_pannable_area_button_release_cb (GtkWidget * widget,
1857 GdkEventButton * event)
1859 HildonPannableAreaPrivate *priv = PANNABLE_AREA_PRIVATE (widget);
1863 if (gtk_bin_get_child (GTK_BIN (widget)) == NULL)
1866 priv->scroll_indicator_event_interrupt = 0;
1867 priv->scroll_delay_counter = SCROLLBAR_FADE_DELAY;
1869 if ((ABS (priv->vel_y) > 1.0)||
1870 (ABS (priv->vel_x) > 1.0)) {
1871 priv->scroll_indicator_alpha = 1.0;
1874 if (!priv->scroll_indicator_timeout) {
1875 priv->scroll_indicator_timeout = g_timeout_add
1876 ((gint) (1000.0 / (gdouble) priv->sps),
1877 (GSourceFunc) hildon_pannable_area_scroll_indicator_fade, widget);
1880 if ((!priv->clicked) || (!priv->enabled) || (event->button != 1) ||
1881 ((event->time == priv->last_time) && (priv->last_type == 3)))
1884 priv->clicked = FALSE;
1886 if (priv->mode == HILDON_PANNABLE_AREA_MODE_AUTO ||
1887 priv->mode == HILDON_PANNABLE_AREA_MODE_ACCEL) {
1889 /* If overshoot has been initiated with a finger down, on release set max speed */
1890 if (priv->overshot_dist_y != 0) {
1891 priv->overshooting_y = BOUNCE_STEPS; /* Hack to stop a bounce in the finger down case */
1892 priv->vel_y = priv->vmax;
1895 if (priv->overshot_dist_x != 0) {
1896 priv->overshooting_x = BOUNCE_STEPS; /* Hack to stop a bounce in the finger down case */
1897 priv->vel_x = priv->vmax;
1901 priv->idle_id = g_timeout_add ((gint) (1000.0 / (gdouble) priv->sps),
1903 hildon_pannable_area_timeout, widget);
1906 priv->last_time = event->time;
1907 priv->last_type = 3;
1910 priv->moved = FALSE;
1915 hildon_pannable_area_get_topmost (gtk_bin_get_child (GTK_BIN (widget))->window,
1916 event->x, event->y, &x, &y);
1918 event = (GdkEventButton *) gdk_event_copy ((GdkEvent *) event);
1922 /* Leave the widget if we've moved - This doesn't break selection,
1923 * but stops buttons from being clicked.
1925 if ((child != priv->child) || (priv->moved)) {
1926 /* Send synthetic leave event */
1927 synth_crossing (priv->child, x, y, event->x_root,
1928 event->y_root, event->time, FALSE);
1929 /* Send synthetic button release event */
1930 ((GdkEventAny *) event)->window = g_object_ref (priv->child);
1931 gdk_event_put ((GdkEvent *) event);
1933 /* Send synthetic button release event */
1934 ((GdkEventAny *) event)->window = g_object_ref (child);
1935 gdk_event_put ((GdkEvent *) event);
1936 /* Send synthetic leave event */
1937 synth_crossing (priv->child, x, y, event->x_root,
1938 event->y_root, event->time, FALSE);
1940 g_object_remove_weak_pointer ((GObject *) priv->child,
1941 (gpointer) & priv->child);
1943 priv->moved = FALSE;
1944 gdk_event_free ((GdkEvent *) event);
1949 /* utility event handler */
1951 hildon_pannable_area_scroll_cb (GtkWidget *widget,
1952 GdkEventScroll *event)
1954 GtkAdjustment *adj = NULL;
1955 HildonPannableAreaPrivate *priv = PANNABLE_AREA_PRIVATE (widget);
1957 if ((!priv->enabled) ||
1958 (gtk_bin_get_child (GTK_BIN (widget)) == NULL))
1961 priv->scroll_indicator_event_interrupt = 0;
1962 priv->scroll_indicator_alpha = 1.0;
1963 priv->scroll_delay_counter = SCROLLBAR_FADE_DELAY + 20;
1965 if (!priv->scroll_indicator_timeout) {
1966 priv->scroll_indicator_timeout = g_timeout_add
1967 ((gint) (1000.0 / (gdouble) (priv->sps*2)),
1968 (GSourceFunc) hildon_pannable_area_scroll_indicator_fade, widget);
1971 /* Stop inertial scrolling */
1972 if (priv->idle_id) {
1975 priv->overshooting_x = 0;
1976 priv->overshooting_y = 0;
1978 if ((priv->overshot_dist_x>0)||(priv->overshot_dist_y>0)) {
1979 priv->overshot_dist_x = 0;
1980 priv->overshot_dist_y = 0;
1982 gtk_widget_queue_resize (GTK_WIDGET (widget));
1985 g_source_remove (priv->idle_id);
1989 if (event->direction == GDK_SCROLL_UP || event->direction == GDK_SCROLL_DOWN)
1990 adj = priv->vadjust;
1992 adj = priv->hadjust;
1996 gdouble delta, new_value;
1998 /* from gtkrange.c calculate delta*/
1999 delta = pow (adj->page_size, 2.0 / 3.0);
2001 if (event->direction == GDK_SCROLL_UP ||
2002 event->direction == GDK_SCROLL_LEFT)
2005 new_value = CLAMP (adj->value + delta, adj->lower, adj->upper - adj->page_size);
2007 gtk_adjustment_set_value (adj, new_value);
2015 hildon_pannable_area_add (GtkContainer *container, GtkWidget *child)
2017 HildonPannableAreaPrivate *priv = PANNABLE_AREA_PRIVATE (container);
2019 g_return_if_fail (gtk_bin_get_child (GTK_BIN (container)) == NULL);
2021 gtk_widget_set_parent (child, GTK_WIDGET (container));
2022 GTK_BIN (container)->child = child;
2024 if (!gtk_widget_set_scroll_adjustments (child, priv->hadjust, priv->vadjust)) {
2025 g_warning ("%s: cannot add non scrollable widget, "
2026 "wrap it in a viewport", __FUNCTION__);
2031 hildon_pannable_area_remove (GtkContainer *container, GtkWidget *child)
2033 g_return_if_fail (HILDON_IS_PANNABLE_AREA (container));
2034 g_return_if_fail (child != NULL);
2035 g_return_if_fail (gtk_bin_get_child (GTK_BIN (container)) == child);
2037 gtk_widget_set_scroll_adjustments (child, NULL, NULL);
2039 /* chain parent class handler to remove child */
2040 GTK_CONTAINER_CLASS (hildon_pannable_area_parent_class)->remove (container, child);
2044 hildon_pannable_calculate_vel_factor (HildonPannableArea * self)
2046 HildonPannableAreaPrivate *priv = PANNABLE_AREA_PRIVATE (self);
2051 n = ceil (priv->sps * priv->scroll_time);
2053 for (i = 0; i < n && fct_i >= RATIO_TOLERANCE; i++) {
2054 fct_i *= priv->decel;
2058 priv->vel_factor = fct;
2062 * hildon_pannable_area_new:
2064 * Create a new pannable area widget
2066 * Returns: the newly created #HildonPannableArea
2070 hildon_pannable_area_new (void)
2072 return g_object_new (HILDON_TYPE_PANNABLE_AREA, NULL);
2076 * hildon_pannable_area_new_full:
2077 * @mode: #HildonPannableAreaMode
2078 * @enabled: Value for the enabled property
2079 * @vel_min: Value for the velocity-min property
2080 * @vel_max: Value for the velocity-max property
2081 * @decel: Value for the deceleration property
2082 * @sps: Value for the sps property
2084 * Create a new #HildonPannableArea widget and set various properties
2086 * returns: the newly create #HildonPannableArea
2090 hildon_pannable_area_new_full (gint mode, gboolean enabled,
2091 gdouble vel_min, gdouble vel_max,
2092 gdouble decel, guint sps)
2094 return g_object_new (HILDON_TYPE_PANNABLE_AREA,
2097 "velocity_min", vel_min,
2098 "velocity_max", vel_max,
2099 "deceleration", decel, "sps", sps, NULL);
2103 * hildon_pannable_area_add_with_viewport:
2104 * @area: A #HildonPannableArea
2105 * @child: Child widget to add to the viewport
2107 * Convenience function used to add a child to a #GtkViewport, and add the
2108 * viewport to the scrolled window.
2112 hildon_pannable_area_add_with_viewport (HildonPannableArea * area,
2115 GtkWidget *viewport = gtk_viewport_new (NULL, NULL);
2116 gtk_viewport_set_shadow_type (GTK_VIEWPORT (viewport), GTK_SHADOW_NONE);
2117 gtk_container_add (GTK_CONTAINER (viewport), child);
2118 gtk_widget_show (viewport);
2119 gtk_container_add (GTK_CONTAINER (area), viewport);
2123 * hildon_pannable_area_scroll_to:
2124 * @area: A #HildonPannableArea.
2125 * @x: The x coordinate of the destination point or -1 to ignore this axis.
2126 * @y: The y coordinate of the destination point or -1 to ignore this axis.
2128 * Smoothly scrolls @area to ensure that (@x, @y) is a visible point
2129 * on the widget. To move in only one coordinate, you must set the other one
2130 * to -1. Notice that, in %HILDON_PANNABLE_AREA_MODE_PUSH mode, this function
2131 * works just like hildon_pannable_area_jump_to().
2133 * This function is useful if you need to present the user with a particular
2134 * element inside a scrollable widget, like #GtkTreeView. For instance,
2135 * the following example shows how to scroll inside a #GtkTreeView to
2136 * make visible an item, indicated by the #GtkTreeIter @iter.
2140 * GtkTreePath *path;
2141 * GdkRectangle *rect;
2143 * path = gtk_tree_model_get_path (model, &iter);
2144 * gtk_tree_view_get_background_area (GTK_TREE_VIEW (treeview),
2145 * path, NULL, &rect);
2146 * gtk_tree_view_convert_bin_window_to_tree_coords (GTK_TREE_VIEW (treeview),
2147 * 0, rect.y, NULL, &y);
2148 * hildon_pannable_area_scroll_to (panarea, -1, y);
2149 * gtk_tree_path_free (path);
2153 * If you want to present a child widget in simpler scenarios,
2154 * use hildon_pannable_area_scroll_to_child() instead.
2158 hildon_pannable_area_scroll_to (HildonPannableArea *area,
2159 const gint x, const gint y)
2161 HildonPannableAreaPrivate *priv;
2163 gint dist_x, dist_y;
2165 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
2167 priv = PANNABLE_AREA_PRIVATE (area);
2169 if (priv->mode == HILDON_PANNABLE_AREA_MODE_PUSH)
2170 hildon_pannable_area_jump_to (area, x, y);
2172 g_return_if_fail (x >= -1 && y >= -1);
2174 if (x == -1 && y == -1) {
2178 width = priv->hadjust->upper - priv->hadjust->lower;
2179 height = priv->vadjust->upper - priv->vadjust->lower;
2181 g_return_if_fail (x < width || y < height);
2184 priv->scroll_to_x = x - priv->hadjust->page_size/2;
2185 dist_x = priv->scroll_to_x - priv->hadjust->value;
2187 priv->scroll_to_x = -1;
2189 priv->vel_x = - dist_x/priv->vel_factor;
2192 priv->scroll_to_x = -1;
2196 priv->scroll_to_y = y - priv->vadjust->page_size/2;
2197 dist_y = priv->scroll_to_y - priv->vadjust->value;
2199 priv->scroll_to_y = -1;
2201 priv->vel_y = - dist_y/priv->vel_factor;
2204 priv->scroll_to_y = y;
2207 if ((priv->scroll_to_y == -1) && (priv->scroll_to_y == -1)) {
2211 priv->scroll_indicator_alpha = 1.0;
2213 if (!priv->scroll_indicator_timeout)
2214 priv->scroll_indicator_timeout = g_timeout_add
2215 ((gint) (1000.0 / (gdouble) priv->sps),
2216 (GSourceFunc) hildon_pannable_area_scroll_indicator_fade, area);
2219 priv->idle_id = g_timeout_add ((gint) (1000.0 / (gdouble) priv->sps),
2221 hildon_pannable_area_timeout, area);
2225 * hildon_pannable_area_jump_to:
2226 * @area: A #HildonPannableArea.
2227 * @x: The x coordinate of the destination point or -1 to ignore this axis.
2228 * @y: The y coordinate of the destination point or -1 to ignore this axis.
2230 * Jumps the position of @area to ensure that (@x, @y) is a visible
2231 * point in the widget. In order to move in only one coordinate, you
2232 * must set the other one to -1. See hildon_pannable_area_scroll_to()
2233 * function for an example of how to calculate the position of
2234 * children in scrollable widgets like #GtkTreeview.
2238 hildon_pannable_area_jump_to (HildonPannableArea *area,
2239 const gint x, const gint y)
2241 HildonPannableAreaPrivate *priv;
2244 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
2245 g_return_if_fail (x >= -1 && y >= -1);
2247 if (x == -1 && y == -1) {
2251 priv = PANNABLE_AREA_PRIVATE (area);
2253 width = priv->hadjust->upper - priv->hadjust->lower;
2254 height = priv->vadjust->upper - priv->vadjust->lower;
2256 g_return_if_fail (x < width || y < height);
2259 gdouble jump_to = x - priv->hadjust->page_size/2;
2261 if (jump_to > priv->hadjust->upper - priv->hadjust->page_size) {
2262 jump_to = priv->hadjust->upper - priv->hadjust->page_size;
2265 gtk_adjustment_set_value (priv->hadjust, jump_to);
2269 gdouble jump_to = y - priv->vadjust->page_size/2;
2271 if (jump_to > priv->vadjust->upper - priv->vadjust->page_size) {
2272 jump_to = priv->vadjust->upper - priv->vadjust->page_size;
2275 gtk_adjustment_set_value (priv->vadjust, jump_to);
2278 priv->scroll_indicator_alpha = 1.0;
2280 if (priv->scroll_indicator_timeout) {
2281 g_source_remove (priv->scroll_indicator_timeout);
2282 priv->scroll_indicator_timeout = 0;
2285 if (priv->idle_id) {
2288 priv->overshooting_x = 0;
2289 priv->overshooting_y = 0;
2291 if ((priv->overshot_dist_x>0)||(priv->overshot_dist_y>0)) {
2292 priv->overshot_dist_x = 0;
2293 priv->overshot_dist_y = 0;
2295 gtk_widget_queue_resize (GTK_WIDGET (area));
2298 g_source_remove (priv->idle_id);
2304 * hildon_pannable_area_scroll_to_child:
2305 * @area: A #HildonPannableArea.
2306 * @child: A #GtkWidget, descendant of @area.
2308 * Smoothly scrolls until @child is visible inside @area. @child must
2309 * be a descendant of @area. If you need to scroll inside a scrollable
2310 * widget, e.g., #GtkTreeview, see hildon_pannable_area_scroll_to().
2314 hildon_pannable_area_scroll_to_child (HildonPannableArea *area, GtkWidget *child)
2316 GtkWidget *bin_child;
2319 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
2320 g_return_if_fail (GTK_IS_WIDGET (child));
2321 g_return_if_fail (gtk_widget_is_ancestor (child, GTK_WIDGET (area)));
2323 if (GTK_BIN (area)->child == NULL)
2326 /* We need to get to check the child of the inside the area */
2327 bin_child = GTK_BIN (area)->child;
2329 /* we check if we added a viewport */
2330 if (GTK_IS_VIEWPORT (bin_child)) {
2331 bin_child = GTK_BIN (bin_child)->child;
2334 if (gtk_widget_translate_coordinates (child, bin_child, 0, 0, &x, &y))
2335 hildon_pannable_area_scroll_to (area, x, y);
2339 * hildon_pannable_area_jump_to_child:
2340 * @area: A #HildonPannableArea.
2341 * @child: A #GtkWidget, descendant of @area.
2343 * Jumps to make sure @child is visible inside @area. @child must
2344 * be a descendant of @area. If you want to move inside a scrollable
2345 * widget, like, #GtkTreeview, see hildon_pannable_area_scroll_to().
2349 hildon_pannable_area_jump_to_child (HildonPannableArea *area, GtkWidget *child)
2351 GtkWidget *bin_child;
2354 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
2355 g_return_if_fail (GTK_IS_WIDGET (child));
2356 g_return_if_fail (gtk_widget_is_ancestor (child, GTK_WIDGET (area)));
2358 if (gtk_bin_get_child (GTK_BIN (area)) == NULL)
2361 /* We need to get to check the child of the inside the area */
2362 bin_child = gtk_bin_get_child (GTK_BIN (area));
2364 /* we check if we added a viewport */
2365 if (GTK_IS_VIEWPORT (bin_child)) {
2366 bin_child = gtk_bin_get_child (GTK_BIN (bin_child));
2369 if (gtk_widget_translate_coordinates (child, bin_child, 0, 0, &x, &y))
2370 hildon_pannable_area_jump_to (area, x, y);
2374 * hildon_pannable_get_child_widget_at:
2375 * @area: A #HildonPannableArea.
2376 * @x: horizontal coordinate of the point
2377 * @y: vertical coordinate of the point
2379 * Get the widget at the point (x, y) inside the pannable area. In
2380 * case no widget found it returns NULL.
2382 * returns: the #GtkWidget if we find a widget, NULL in any other case
2385 hildon_pannable_get_child_widget_at (HildonPannableArea *area,
2386 gdouble x, gdouble y)
2388 GdkWindow *window = NULL;
2389 GtkWidget *child_widget = NULL;
2391 window = hildon_pannable_area_get_topmost
2392 (gtk_bin_get_child (GTK_BIN (area))->window,
2395 gdk_window_get_user_data (window, (gpointer) &child_widget);
2397 return child_widget;