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 void hildon_pannable_area_calculate_velocity (gdouble *vel,
205 static gboolean hildon_pannable_area_motion_notify_cb (GtkWidget * widget,
206 GdkEventMotion * event);
207 static gboolean hildon_pannable_area_button_release_cb (GtkWidget * widget,
208 GdkEventButton * event);
209 static gboolean hildon_pannable_area_scroll_cb (GtkWidget *widget,
210 GdkEventScroll *event);
211 static void hildon_pannable_area_add (GtkContainer *container, GtkWidget *child);
212 static void hildon_pannable_area_remove (GtkContainer *container, GtkWidget *child);
213 static void hildon_pannable_calculate_vel_factor (HildonPannableArea * self);
217 hildon_pannable_area_class_init (HildonPannableAreaClass * klass)
219 GObjectClass *object_class = G_OBJECT_CLASS (klass);
220 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
221 GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
224 g_type_class_add_private (klass, sizeof (HildonPannableAreaPrivate));
226 object_class->get_property = hildon_pannable_area_get_property;
227 object_class->set_property = hildon_pannable_area_set_property;
228 object_class->dispose = hildon_pannable_area_dispose;
230 widget_class->realize = hildon_pannable_area_realize;
231 widget_class->unrealize = hildon_pannable_area_unrealize;
232 widget_class->map = hildon_pannable_area_map;
233 widget_class->unmap = hildon_pannable_area_unmap;
234 widget_class->size_request = hildon_pannable_area_size_request;
235 widget_class->size_allocate = hildon_pannable_area_size_allocate;
236 widget_class->expose_event = hildon_pannable_area_expose_event;
237 widget_class->style_set = hildon_pannable_area_style_set;
238 widget_class->button_press_event = hildon_pannable_area_button_press_cb;
239 widget_class->button_release_event = hildon_pannable_area_button_release_cb;
240 widget_class->motion_notify_event = hildon_pannable_area_motion_notify_cb;
241 widget_class->scroll_event = hildon_pannable_area_scroll_cb;
243 container_class->add = hildon_pannable_area_add;
244 container_class->remove = hildon_pannable_area_remove;
246 klass->horizontal_movement = NULL;
247 klass->vertical_movement = NULL;
249 g_object_class_install_property (object_class,
251 g_param_spec_boolean ("enabled",
253 "Enable or disable finger-scroll.",
258 g_object_class_install_property (object_class,
259 PROP_VSCROLLBAR_POLICY,
260 g_param_spec_enum ("vscrollbar_policy",
262 "Visual policy of the vertical scrollbar",
263 GTK_TYPE_POLICY_TYPE,
264 GTK_POLICY_AUTOMATIC,
268 g_object_class_install_property (object_class,
269 PROP_HSCROLLBAR_POLICY,
270 g_param_spec_enum ("hscrollbar_policy",
272 "Visual policy of the horizontal scrollbar",
273 GTK_TYPE_POLICY_TYPE,
274 GTK_POLICY_AUTOMATIC,
278 g_object_class_install_property (object_class,
280 g_param_spec_enum ("mode",
282 "Change the finger-scrolling mode.",
283 HILDON_TYPE_PANNABLE_AREA_MODE,
284 HILDON_PANNABLE_AREA_MODE_AUTO,
288 g_object_class_install_property (object_class,
290 g_param_spec_flags ("mov_mode",
291 "Scroll movement mode",
292 "Controls if the widget can scroll vertically, horizontally or both",
293 HILDON_TYPE_MOVEMENT_MODE,
294 HILDON_MOVEMENT_MODE_BOTH,
298 g_object_class_install_property (object_class,
300 g_param_spec_double ("velocity_min",
301 "Minimum scroll velocity",
302 "Minimum distance the child widget should scroll "
303 "per 'frame', in pixels.",
308 g_object_class_install_property (object_class,
310 g_param_spec_double ("velocity_max",
311 "Maximum scroll velocity",
312 "Maximum distance the child widget should scroll "
313 "per 'frame', in pixels.",
318 g_object_class_install_property (object_class,
319 PROP_VELOCITY_FAST_FACTOR,
320 g_param_spec_double ("velocity_fast_factor",
321 "Fast velocity factor",
322 "Minimum velocity that is considered 'fast': "
323 "children widgets won't receive button presses. "
324 "Expressed as a fraction of the maximum velocity.",
329 g_object_class_install_property (object_class,
331 g_param_spec_double ("deceleration",
332 "Deceleration multiplier",
333 "The multiplier used when decelerating when in "
334 "acceleration scrolling mode.",
339 g_object_class_install_property (object_class,
341 g_param_spec_uint ("sps",
342 "Scrolls per second",
343 "Amount of scroll events to generate per second.",
348 g_object_class_install_property (object_class,
350 g_param_spec_int ("vovershoot_max",
351 "Vertical overshoot distance",
352 "Space we allow the widget to pass over its vertical limits when hitting the edges, set 0 in order to deactivate overshooting.",
357 g_object_class_install_property (object_class,
359 g_param_spec_int ("hovershoot_max",
360 "Horizontal overshoot distance",
361 "Space we allow the widget to pass over its horizontal limits when hitting the edges, set 0 in order to deactivate overshooting.",
366 g_object_class_install_property (object_class,
368 g_param_spec_double ("scroll_time",
369 "Time to scroll to a position",
370 "The time to scroll to a position when calling the hildon_pannable_scroll_to function"
371 "acceleration scrolling mode.",
376 g_object_class_install_property (object_class,
378 g_param_spec_boolean ("initial-hint",
380 "Whether to hint the user about the pannability of the container.",
385 gtk_widget_class_install_style_property (widget_class,
388 "Width of the scroll indicators",
389 "Pixel width used to draw the scroll indicators.",
393 pannable_area_signals[HORIZONTAL_MOVEMENT] =
394 g_signal_new ("horizontal_movement",
395 G_TYPE_FROM_CLASS (object_class),
396 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
397 G_STRUCT_OFFSET (HildonPannableAreaClass, horizontal_movement),
399 _hildon_marshal_VOID__INT_DOUBLE_DOUBLE,
405 pannable_area_signals[VERTICAL_MOVEMENT] =
406 g_signal_new ("vertical_movement",
407 G_TYPE_FROM_CLASS (object_class),
408 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
409 G_STRUCT_OFFSET (HildonPannableAreaClass, vertical_movement),
411 _hildon_marshal_VOID__INT_DOUBLE_DOUBLE,
421 hildon_pannable_area_init (HildonPannableArea * area)
423 HildonPannableAreaPrivate *priv = PANNABLE_AREA_PRIVATE (area);
426 priv->clicked = FALSE;
429 priv->vscroll_visible = TRUE;
430 priv->hscroll_visible = TRUE;
431 priv->area_width = 6;
432 priv->overshot_dist_x = 0;
433 priv->overshot_dist_y = 0;
434 priv->overshooting_y = 0;
435 priv->overshooting_x = 0;
439 priv->scroll_indicator_alpha = 0.0;
440 priv->scroll_indicator_timeout = 0;
441 priv->scroll_indicator_event_interrupt = 0;
442 priv->scroll_delay_counter = SCROLLBAR_FADE_DELAY;
443 priv->scroll_to_x = -1;
444 priv->scroll_to_y = -1;
445 priv->first_drag = TRUE;
446 priv->initial_effect = TRUE;
448 hildon_pannable_calculate_vel_factor (area);
450 gtk_widget_add_events (GTK_WIDGET (area), GDK_POINTER_MOTION_HINT_MASK);
453 GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
455 GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
457 g_object_ref_sink (G_OBJECT (priv->hadjust));
458 g_object_ref_sink (G_OBJECT (priv->vadjust));
460 g_signal_connect_swapped (G_OBJECT (priv->hadjust), "value-changed",
461 G_CALLBACK (hildon_pannable_area_redraw), area);
462 g_signal_connect_swapped (G_OBJECT (priv->vadjust), "value-changed",
463 G_CALLBACK (hildon_pannable_area_redraw), area);
464 g_signal_connect (G_OBJECT (area), "grab-notify",
465 G_CALLBACK (hildon_pannable_area_grab_notify), NULL);
469 hildon_pannable_area_get_property (GObject * object,
474 HildonPannableAreaPrivate *priv = PANNABLE_AREA_PRIVATE (object);
476 switch (property_id) {
478 g_value_set_boolean (value, priv->enabled);
481 g_value_set_enum (value, priv->mode);
483 case PROP_MOVEMENT_MODE:
484 g_value_set_flags (value, priv->mov_mode);
486 case PROP_VELOCITY_MIN:
487 g_value_set_double (value, priv->vmin);
489 case PROP_VELOCITY_MAX:
490 g_value_set_double (value, priv->vmax);
492 case PROP_VELOCITY_FAST_FACTOR:
493 g_value_set_double (value, priv->vfast_factor);
495 case PROP_DECELERATION:
496 g_value_set_double (value, priv->decel);
499 g_value_set_uint (value, priv->sps);
501 case PROP_VSCROLLBAR_POLICY:
502 g_value_set_enum (value, priv->vscrollbar_policy);
504 case PROP_HSCROLLBAR_POLICY:
505 g_value_set_enum (value, priv->hscrollbar_policy);
507 case PROP_VOVERSHOOT_MAX:
508 g_value_set_int (value, priv->vovershoot_max);
510 case PROP_HOVERSHOOT_MAX:
511 g_value_set_int (value, priv->hovershoot_max);
513 case PROP_SCROLL_TIME:
514 g_value_set_double (value, priv->scroll_time);
516 case PROP_INITIAL_HINT:
517 g_value_set_boolean (value, priv->initial_hint);
521 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
526 hildon_pannable_area_set_property (GObject * object,
528 const GValue * value,
531 HildonPannableAreaPrivate *priv = PANNABLE_AREA_PRIVATE (object);
534 switch (property_id) {
536 enabled = g_value_get_boolean (value);
538 if ((priv->enabled != enabled) && (GTK_WIDGET_REALIZED (object))) {
540 gdk_window_raise (priv->event_window);
542 gdk_window_lower (priv->event_window);
545 priv->enabled = enabled;
548 priv->mode = g_value_get_enum (value);
550 case PROP_MOVEMENT_MODE:
551 priv->mov_mode = g_value_get_flags (value);
553 case PROP_VELOCITY_MIN:
554 priv->vmin = g_value_get_double (value);
556 case PROP_VELOCITY_MAX:
557 priv->vmax = g_value_get_double (value);
559 case PROP_VELOCITY_FAST_FACTOR:
560 priv->vfast_factor = g_value_get_double (value);
562 case PROP_DECELERATION:
563 hildon_pannable_calculate_vel_factor (HILDON_PANNABLE_AREA (object));
565 priv->decel = g_value_get_double (value);
568 priv->sps = g_value_get_uint (value);
570 case PROP_VSCROLLBAR_POLICY:
571 priv->vscrollbar_policy = g_value_get_enum (value);
573 gtk_widget_queue_resize (GTK_WIDGET (object));
575 case PROP_HSCROLLBAR_POLICY:
576 priv->hscrollbar_policy = g_value_get_enum (value);
578 gtk_widget_queue_resize (GTK_WIDGET (object));
580 case PROP_VOVERSHOOT_MAX:
581 priv->vovershoot_max = g_value_get_int (value);
583 case PROP_HOVERSHOOT_MAX:
584 priv->hovershoot_max = g_value_get_int (value);
586 case PROP_SCROLL_TIME:
587 priv->scroll_time = g_value_get_double (value);
589 hildon_pannable_calculate_vel_factor (HILDON_PANNABLE_AREA (object));
591 case PROP_INITIAL_HINT:
592 priv->initial_hint = g_value_get_boolean (value);
596 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
601 hildon_pannable_area_dispose (GObject * object)
603 HildonPannableAreaPrivate *priv = PANNABLE_AREA_PRIVATE (object);
606 g_source_remove (priv->idle_id);
610 if (priv->scroll_indicator_timeout){
611 g_source_remove (priv->scroll_indicator_timeout);
612 priv->scroll_indicator_timeout = 0;
616 g_object_unref (priv->hadjust);
617 priv->hadjust = NULL;
620 g_object_unref (priv->vadjust);
621 priv->vadjust = NULL;
624 if (G_OBJECT_CLASS (hildon_pannable_area_parent_class)->dispose)
625 G_OBJECT_CLASS (hildon_pannable_area_parent_class)->dispose (object);
629 hildon_pannable_area_realize (GtkWidget * widget)
631 GdkWindowAttr attributes;
632 gint attributes_mask;
634 HildonPannableAreaPrivate *priv;
636 priv = PANNABLE_AREA_PRIVATE (widget);
638 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
640 border_width = GTK_CONTAINER (widget)->border_width;
642 attributes.x = widget->allocation.x + border_width;
643 attributes.y = widget->allocation.y + border_width;
644 attributes.width = MAX (widget->allocation.width - 2 * border_width -
645 (priv->vscroll_visible ? priv->vscroll_rect.width : 0), 0);
646 attributes.height = MAX (widget->allocation.height - 2 * border_width -
647 (priv->hscroll_visible ? priv->hscroll_rect.height : 0), 0);
648 attributes.window_type = GDK_WINDOW_CHILD;
649 attributes.event_mask = gtk_widget_get_events (widget)
650 | GDK_BUTTON_MOTION_MASK
651 | GDK_BUTTON_PRESS_MASK
652 | GDK_BUTTON_RELEASE_MASK
654 | GDK_EXPOSURE_MASK | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK;
656 widget->window = gtk_widget_get_parent_window (widget);
657 g_object_ref (widget->window);
659 attributes.wclass = GDK_INPUT_ONLY;
660 attributes_mask = GDK_WA_X | GDK_WA_Y;
662 priv->event_window = gdk_window_new (widget->window,
663 &attributes, attributes_mask);
664 gdk_window_set_user_data (priv->event_window, widget);
666 widget->style = gtk_style_attach (widget->style, widget->window);
670 hildon_pannable_area_unrealize (GtkWidget * widget)
672 HildonPannableAreaPrivate *priv;
674 priv = PANNABLE_AREA_PRIVATE (widget);
676 if (priv->event_window != NULL) {
677 gdk_window_set_user_data (priv->event_window, NULL);
678 gdk_window_destroy (priv->event_window);
679 priv->event_window = NULL;
682 if (GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->unrealize)
683 (*GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->unrealize)(widget);
687 hildon_pannable_area_size_request (GtkWidget * widget,
688 GtkRequisition * requisition)
690 GtkRequisition child_requisition;
691 HildonPannableAreaPrivate *priv = PANNABLE_AREA_PRIVATE (widget);
692 GtkWidget *child = gtk_bin_get_child (GTK_BIN (widget));
694 if (child && GTK_WIDGET_VISIBLE (child))
696 gtk_widget_size_request (child, &child_requisition);
699 if (priv->hscrollbar_policy == GTK_POLICY_NEVER) {
700 requisition->width = child_requisition.width;
702 requisition->width = priv->area_width;
705 if (priv->vscrollbar_policy == GTK_POLICY_NEVER) {
706 requisition->height = child_requisition.height;
708 requisition->height = priv->area_width;
711 requisition->width += 2 * GTK_CONTAINER (widget)->border_width;
712 requisition->height += 2 * GTK_CONTAINER (widget)->border_width;
716 hildon_pannable_area_size_allocate (GtkWidget * widget,
717 GtkAllocation * allocation)
719 GtkAllocation child_allocation;
720 HildonPannableAreaPrivate *priv;
721 GtkWidget *child = gtk_bin_get_child (GTK_BIN (widget));
723 widget->allocation = *allocation;
725 priv = PANNABLE_AREA_PRIVATE (widget);
727 child_allocation.x = allocation->x + GTK_CONTAINER (widget)->border_width;
728 child_allocation.y = allocation->y + GTK_CONTAINER (widget)->border_width;
729 child_allocation.width = MAX (allocation->width -
730 2 * GTK_CONTAINER (widget)->border_width -
731 (priv->vscroll_visible ? priv->vscroll_rect.width : 0), 0);
732 child_allocation.height = MAX (allocation->height -
733 2 * GTK_CONTAINER (widget)->border_width -
734 (priv->hscroll_visible ? priv->hscroll_rect.height : 0), 0);
736 if (GTK_WIDGET_REALIZED (widget)) {
737 if (priv->event_window != NULL)
738 gdk_window_move_resize (priv->event_window,
741 child_allocation.width,
742 child_allocation.height);
745 if (priv->overshot_dist_y > 0) {
746 child_allocation.y = MIN (child_allocation.y + priv->overshot_dist_y,
747 allocation->y + child_allocation.height);
748 child_allocation.height = MAX (child_allocation.height - priv->overshot_dist_y, 0);
749 } else if (priv->overshot_dist_y < 0) {
750 child_allocation.height = MAX (child_allocation.height + priv->overshot_dist_y, 0);
753 if (priv->overshot_dist_x > 0) {
754 child_allocation.x = MIN (child_allocation.x + priv->overshot_dist_x,
755 allocation->x + child_allocation.width);
756 child_allocation.width = MAX (child_allocation.width - priv->overshot_dist_x, 0);
757 } else if (priv->overshot_dist_x < 0) {
758 child_allocation.width = MAX (child_allocation.width + priv->overshot_dist_x, 0);
762 gtk_widget_size_allocate (child, &child_allocation);
764 /* we have to do this after child size_allocate because page_size is
765 * changed when we allocate the size of the children */
766 if (priv->overshot_dist_y < 0) {
767 gtk_adjustment_set_value (priv->vadjust, priv->vadjust->upper -
768 priv->vadjust->page_size);
771 if (priv->overshot_dist_x < 0) {
772 gtk_adjustment_set_value (priv->hadjust, priv->hadjust->upper -
773 priv->hadjust->page_size);
776 hildon_pannable_area_refresh (HILDON_PANNABLE_AREA (widget));
780 hildon_pannable_area_style_set (GtkWidget * widget,
781 GtkStyle * previous_style)
783 HildonPannableAreaPrivate *priv = PANNABLE_AREA_PRIVATE (widget);
785 GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->
786 style_set (widget, previous_style);
788 gtk_widget_style_get (widget, "indicator-width", &priv->area_width, NULL);
792 hildon_pannable_area_map (GtkWidget * widget)
794 HildonPannableAreaPrivate *priv;
796 priv = PANNABLE_AREA_PRIVATE (widget);
798 if (priv->event_window != NULL && !priv->enabled)
799 gdk_window_show (priv->event_window);
801 (*GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->map) (widget);
803 if (priv->event_window != NULL && priv->enabled)
804 gdk_window_show (priv->event_window);
808 hildon_pannable_area_unmap (GtkWidget * widget)
810 HildonPannableAreaPrivate *priv;
812 priv = PANNABLE_AREA_PRIVATE (widget);
814 if (priv->event_window != NULL)
815 gdk_window_hide (priv->event_window);
817 (*GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->unmap) (widget);
821 hildon_pannable_area_grab_notify (GtkWidget *widget,
822 gboolean was_grabbed,
825 /* an internal widget has grabbed the focus and now has returned it,
826 we have to do some release actions */
828 HildonPannableAreaPrivate *priv = PANNABLE_AREA_PRIVATE (widget);
830 priv->scroll_indicator_event_interrupt = 0;
831 priv->scroll_delay_counter = SCROLLBAR_FADE_DELAY;
833 if (!priv->scroll_indicator_timeout) {
834 priv->scroll_indicator_timeout = g_timeout_add
835 ((gint) (1000.0 / (gdouble) priv->sps),
836 (GSourceFunc) hildon_pannable_area_scroll_indicator_fade, widget);
845 rgb_from_gdkcolor (GdkColor *color, gdouble *r, gdouble *g, gdouble *b)
847 *r = (color->red >> 8) / 255.0;
848 *g = (color->green >> 8) / 255.0;
849 *b = (color->blue >> 8) / 255.0;
853 hildon_pannable_draw_vscroll (GtkWidget * widget,
854 GdkColor *back_color,
855 GdkColor *scroll_color)
857 HildonPannableAreaPrivate *priv = PANNABLE_AREA_PRIVATE (widget);
860 cairo_pattern_t *pattern;
862 gint radius = (priv->vscroll_rect.width/2) - 1;
864 cr = gdk_cairo_create(widget->window);
866 /* Draw the background */
867 rgb_from_gdkcolor (back_color, &r, &g, &b);
868 cairo_set_source_rgb (cr, r, g, b);
869 cairo_rectangle(cr, priv->vscroll_rect.x, priv->vscroll_rect.y,
870 priv->vscroll_rect.width,
871 priv->vscroll_rect.height);
872 cairo_fill_preserve (cr);
875 /* Calculate the scroll bar height and position */
876 y = widget->allocation.y +
877 ((priv->vadjust->value / priv->vadjust->upper) *
878 (widget->allocation.height -
879 (priv->hscroll_visible ? priv->area_width : 0)));
880 height = (widget->allocation.y +
881 (((priv->vadjust->value +
882 priv->vadjust->page_size) /
883 priv->vadjust->upper) *
884 (widget->allocation.height -
885 (priv->hscroll_visible ? priv->area_width : 0)))) - y;
887 /* Set a minimum height */
888 height = MAX (SCROLL_BAR_MIN_SIZE, height);
890 /* Check the max y position */
891 y = MIN (y, widget->allocation.y + widget->allocation.height -
892 (priv->hscroll_visible ? priv->hscroll_rect.height : 0) -
895 /* Draw the scrollbar */
896 rgb_from_gdkcolor (scroll_color, &r, &g, &b);
898 pattern = cairo_pattern_create_linear(radius+1, y, radius+1,y + height);
899 cairo_pattern_add_color_stop_rgb(pattern, 0, r, g, b);
900 cairo_pattern_add_color_stop_rgb(pattern, 1, r/2, g/2, b/2);
901 cairo_set_source(cr, pattern);
903 cairo_pattern_destroy(pattern);
905 cairo_arc(cr, priv->vscroll_rect.x + radius + 1, y + radius + 1, radius, G_PI, 0);
906 cairo_line_to(cr, priv->vscroll_rect.x + (radius * 2) + 1, y + height - radius);
907 cairo_arc(cr, priv->vscroll_rect.x + radius + 1, y + height - radius, radius, 0, G_PI);
908 cairo_line_to(cr, priv->vscroll_rect.x + 1, y + height - radius);
911 cairo_paint_with_alpha(cr, priv->scroll_indicator_alpha);
917 hildon_pannable_draw_hscroll (GtkWidget * widget,
918 GdkColor *back_color,
919 GdkColor *scroll_color)
921 HildonPannableAreaPrivate *priv = PANNABLE_AREA_PRIVATE (widget);
924 cairo_pattern_t *pattern;
926 gint radius = (priv->hscroll_rect.height/2) - 1;
928 cr = gdk_cairo_create(widget->window);
930 /* Draw the background */
931 rgb_from_gdkcolor (back_color, &r, &g, &b);
932 cairo_set_source_rgb (cr, r, g, b);
933 cairo_rectangle(cr, priv->hscroll_rect.x, priv->hscroll_rect.y,
934 priv->hscroll_rect.width,
935 priv->hscroll_rect.height);
936 cairo_fill_preserve (cr);
939 /* calculate the scrollbar width and position */
940 x = widget->allocation.x +
941 ((priv->hadjust->value / priv->hadjust->upper) *
942 (widget->allocation.width - (priv->vscroll_visible ? priv->area_width : 0)));
944 (widget->allocation.x +
945 (((priv->hadjust->value +
946 priv->hadjust->page_size) / priv->hadjust->upper) *
947 (widget->allocation.width -
948 (priv->vscroll_visible ? priv->area_width : 0)))) - x;
950 /* Set a minimum width */
951 width = MAX (SCROLL_BAR_MIN_SIZE, width);
953 /* Check the max x position */
954 x = MIN (x, widget->allocation.x + widget->allocation.width -
955 (priv->vscroll_visible ? priv->vscroll_rect.width : 0) -
958 /* Draw the scrollbar */
959 rgb_from_gdkcolor (scroll_color, &r, &g, &b);
961 pattern = cairo_pattern_create_linear(x, radius+1, x+width, radius+1);
962 cairo_pattern_add_color_stop_rgb(pattern, 0, r, g, b);
963 cairo_pattern_add_color_stop_rgb(pattern, 1, r/2, g/2, b/2);
964 cairo_set_source(cr, pattern);
966 cairo_pattern_destroy(pattern);
968 cairo_arc_negative(cr, x + radius + 1, priv->hscroll_rect.y + radius + 1, radius, 3*G_PI_2, G_PI_2);
969 cairo_line_to(cr, x + width - radius, priv->hscroll_rect.y + (radius * 2) + 1);
970 cairo_arc_negative(cr, x + width - radius, priv->hscroll_rect.y + radius + 1, radius, G_PI_2, 3*G_PI_2);
971 cairo_line_to(cr, x + width - radius, priv->hscroll_rect.y + 1);
974 cairo_paint_with_alpha(cr, priv->scroll_indicator_alpha);
980 hildon_pannable_area_initial_effect (GtkWidget * widget)
982 HildonPannableAreaPrivate *priv = PANNABLE_AREA_PRIVATE (widget);
983 gboolean hscroll_visible, vscroll_visible;
985 if (priv->initial_hint) {
986 if (((priv->vovershoot_max != 0)||(priv->hovershoot_max != 0)) &&
987 ((priv->mode == HILDON_PANNABLE_AREA_MODE_AUTO) ||
988 (priv->mode == HILDON_PANNABLE_AREA_MODE_ACCEL))) {
989 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
990 priv->vadjust->page_size);
991 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
992 priv->hadjust->page_size);
993 /* If scrolling is possible in both axes, only hint about scrolling in
995 if ((vscroll_visible)&&(priv->vovershoot_max != 0)) {
996 priv->overshot_dist_y = priv->vovershoot_max;
997 priv->vel_y = priv->vmax * 0.1;
998 } else if ((hscroll_visible)&&(priv->hovershoot_max != 0)) {
999 priv->overshot_dist_x = priv->hovershoot_max;
1000 priv->vel_x = priv->vmax * 0.1;
1003 if (vscroll_visible || hscroll_visible) {
1004 priv->idle_id = g_timeout_add ((gint) (1000.0 / (gdouble) priv->sps),
1006 hildon_pannable_area_timeout, widget);
1010 if (priv->vscroll_visible || priv->hscroll_visible) {
1011 priv->scroll_indicator_alpha = 1.0;
1013 priv->scroll_indicator_timeout =
1014 g_timeout_add ((gint) (1000.0 / (gdouble) priv->sps),
1015 (GSourceFunc) hildon_pannable_area_scroll_indicator_fade,
1022 hildon_pannable_area_redraw (HildonPannableArea * area)
1024 HildonPannableAreaPrivate *priv = PANNABLE_AREA_PRIVATE (area);
1026 /* Redraw scroll indicators */
1027 if (GTK_WIDGET_DRAWABLE (area)) {
1028 if (priv->hscroll_visible) {
1029 gdk_window_invalidate_rect (GTK_WIDGET (area)->window,
1030 &priv->hscroll_rect, FALSE);
1033 if (priv->vscroll_visible) {
1034 gdk_window_invalidate_rect (GTK_WIDGET (area)->window,
1035 &priv->vscroll_rect, FALSE);
1041 hildon_pannable_area_scroll_indicator_fade(HildonPannableArea * area)
1044 HildonPannableAreaPrivate *priv;
1046 GDK_THREADS_ENTER ();
1048 priv = PANNABLE_AREA_PRIVATE (area);
1050 /* if moving do not fade out */
1051 if (((ABS (priv->vel_y)>1.0)||
1052 (ABS (priv->vel_x)>1.0))&&(!priv->clicked)) {
1054 GDK_THREADS_LEAVE ();
1059 if (!priv->scroll_indicator_timeout) {
1061 GDK_THREADS_LEAVE ();
1066 if (priv->scroll_indicator_event_interrupt) {
1067 /* Stop a fade out, and fade back in */
1068 if (priv->scroll_indicator_alpha >= 0.9) {
1069 priv->scroll_indicator_timeout = 0;
1070 priv->scroll_indicator_alpha = 1.0;
1073 priv->scroll_indicator_alpha += 0.2;
1076 hildon_pannable_area_redraw (area);
1079 if ((priv->scroll_indicator_alpha > 0.9) &&
1080 (priv->scroll_delay_counter > 0)) {
1081 priv->scroll_delay_counter--;
1083 GDK_THREADS_LEAVE ();
1088 if (!priv->scroll_indicator_event_interrupt) {
1089 /* Continue fade out */
1090 if (priv->scroll_indicator_alpha <= 0.1) {
1091 priv->scroll_indicator_timeout = 0;
1092 priv->scroll_delay_counter = SCROLLBAR_FADE_DELAY;
1093 priv->scroll_indicator_alpha = 0.0;
1096 priv->scroll_indicator_alpha -= 0.2;
1099 hildon_pannable_area_redraw (area);
1102 GDK_THREADS_LEAVE ();
1108 hildon_pannable_area_expose_event (GtkWidget * widget,
1109 GdkEventExpose * event)
1112 HildonPannableAreaPrivate *priv = PANNABLE_AREA_PRIVATE (widget);
1113 GdkColor back_color = widget->style->bg[GTK_STATE_NORMAL];
1114 GdkColor scroll_color = widget->style->base[GTK_STATE_SELECTED];
1116 if (gtk_bin_get_child (GTK_BIN (widget))) {
1118 if (priv->scroll_indicator_alpha > 0.1) {
1119 if (priv->vscroll_visible) {
1120 hildon_pannable_draw_vscroll (widget, &back_color, &scroll_color);
1122 if (priv->hscroll_visible) {
1123 hildon_pannable_draw_hscroll (widget, &back_color, &scroll_color);
1127 /* draw overshooting rectangles */
1128 if (priv->overshot_dist_y > 0) {
1129 gint overshot_height;
1131 overshot_height = MIN (priv->overshot_dist_y, widget->allocation.height -
1132 (priv->hscroll_visible ? priv->hscroll_rect.height : 0));
1134 gdk_draw_rectangle (widget->window,
1135 widget->style->bg_gc[GTK_STATE_NORMAL],
1137 widget->allocation.x,
1138 widget->allocation.y,
1139 widget->allocation.width -
1140 (priv->vscroll_visible ? priv->vscroll_rect.width : 0),
1142 } else if (priv->overshot_dist_y < 0) {
1143 gint overshot_height;
1147 MAX (priv->overshot_dist_y,
1148 -(widget->allocation.height -
1149 (priv->hscroll_visible ? priv->hscroll_rect.height : 0)));
1151 overshot_y = MAX (widget->allocation.y +
1152 widget->allocation.height +
1154 (priv->hscroll_visible ? priv->hscroll_rect.height : 0), 0);
1156 gdk_draw_rectangle (widget->window,
1157 widget->style->bg_gc[GTK_STATE_NORMAL],
1159 widget->allocation.x,
1161 widget->allocation.width -
1162 priv->vscroll_rect.width,
1166 if (priv->overshot_dist_x > 0) {
1167 gint overshot_width;
1169 overshot_width = MIN (priv->overshot_dist_x, widget->allocation.width -
1170 (priv->vscroll_visible ? priv->vscroll_rect.width : 0));
1172 gdk_draw_rectangle (widget->window,
1173 widget->style->bg_gc[GTK_STATE_NORMAL],
1175 widget->allocation.x,
1176 widget->allocation.y,
1178 widget->allocation.height -
1179 (priv->hscroll_visible ? priv->hscroll_rect.height : 0));
1180 } else if (priv->overshot_dist_x < 0) {
1181 gint overshot_width;
1185 MAX (priv->overshot_dist_x,
1186 -(widget->allocation.width -
1187 (priv->vscroll_visible ? priv->vscroll_rect.width : 0)));
1189 overshot_x = MAX (widget->allocation.x +
1190 widget->allocation.width +
1192 (priv->vscroll_visible ? priv->vscroll_rect.width : 0), 0);
1194 gdk_draw_rectangle (widget->window,
1195 widget->style->bg_gc[GTK_STATE_NORMAL],
1198 widget->allocation.y,
1200 widget->allocation.height -
1201 priv->hscroll_rect.height);
1206 if (G_UNLIKELY (priv->initial_effect)) {
1208 hildon_pannable_area_initial_effect (widget);
1210 priv->initial_effect = FALSE;
1213 return GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->expose_event (widget, event);
1217 hildon_pannable_area_get_topmost (GdkWindow * window,
1219 gint * tx, gint * ty)
1221 /* Find the GdkWindow at the given point, by recursing from a given
1222 * parent GdkWindow. Optionally return the co-ordinates transformed
1223 * relative to the child window.
1227 gdk_drawable_get_size (GDK_DRAWABLE (window), &width, &height);
1228 if ((x < 0) || (x >= width) || (y < 0) || (y >= height))
1232 gint child_x = 0, child_y = 0;
1233 GList *c, *children = gdk_window_peek_children (window);
1234 GdkWindow *old_window = window;
1236 for (c = children; c; c = c->next) {
1237 GdkWindow *child = (GdkWindow *) c->data;
1240 gdk_drawable_get_size (GDK_DRAWABLE (child), &width, &height);
1241 gdk_window_get_position (child, &wx, &wy);
1243 if (((x >= wx) && (x < (wx + width)) && (y >= wy)
1244 && (y < (wy + height))) && (gdk_window_is_visible (child))) {
1251 if (window == old_window)
1267 synth_crossing (GdkWindow * child,
1269 gint x_root, gint y_root,
1270 guint32 time, gboolean in)
1272 GdkEventCrossing *crossing_event;
1273 GdkEventType type = in ? GDK_ENTER_NOTIFY : GDK_LEAVE_NOTIFY;
1275 /* Send synthetic enter event */
1276 crossing_event = (GdkEventCrossing *) gdk_event_new (type);
1277 ((GdkEventAny *) crossing_event)->type = type;
1278 ((GdkEventAny *) crossing_event)->window = g_object_ref (child);
1279 ((GdkEventAny *) crossing_event)->send_event = FALSE;
1280 crossing_event->subwindow = g_object_ref (child);
1281 crossing_event->time = time;
1282 crossing_event->x = x;
1283 crossing_event->y = y;
1284 crossing_event->x_root = x_root;
1285 crossing_event->y_root = y_root;
1286 crossing_event->mode = GDK_CROSSING_NORMAL;
1287 crossing_event->detail = GDK_NOTIFY_UNKNOWN;
1288 crossing_event->focus = FALSE;
1289 crossing_event->state = 0;
1290 gdk_event_put ((GdkEvent *) crossing_event);
1291 gdk_event_free ((GdkEvent *) crossing_event);
1295 hildon_pannable_area_button_press_cb (GtkWidget * widget,
1296 GdkEventButton * event)
1299 HildonPannableAreaPrivate *priv = PANNABLE_AREA_PRIVATE (widget);
1301 if ((!priv->enabled) || (event->button != 1) ||
1302 ((event->time == priv->last_time) &&
1303 (priv->last_type == 1)) || (gtk_bin_get_child (GTK_BIN (widget)) == NULL))
1306 priv->scroll_indicator_event_interrupt = 1;
1307 priv->scroll_delay_counter = SCROLLBAR_FADE_DELAY;
1309 if (!priv->scroll_indicator_timeout){
1310 priv->scroll_indicator_timeout = g_timeout_add
1311 ((gint) (1000.0 / (gdouble) (priv->sps*2)),
1312 (GSourceFunc) hildon_pannable_area_scroll_indicator_fade, widget);
1315 priv->last_time = event->time;
1316 priv->last_type = 1;
1318 priv->scroll_to_x = -1;
1319 priv->scroll_to_y = -1;
1321 if (priv->clicked && priv->child) {
1322 /* Widget stole focus on last click, send crossing-out event */
1323 synth_crossing (priv->child, 0, 0, event->x_root, event->y_root,
1324 event->time, FALSE);
1332 /* Don't allow a click if we're still moving fast */
1333 if ((ABS (priv->vel_x) <= (priv->vmax * priv->vfast_factor)) &&
1334 (ABS (priv->vel_y) <= (priv->vmax * priv->vfast_factor)))
1336 hildon_pannable_area_get_topmost (gtk_bin_get_child (GTK_BIN (widget))->window,
1337 event->x, event->y, &x, &y);
1341 priv->clicked = TRUE;
1343 /* Stop scrolling on mouse-down (so you can flick, then hold to stop) */
1347 if ((priv->child) && (priv->child != gtk_bin_get_child (GTK_BIN (widget))->window)) {
1349 g_object_add_weak_pointer ((GObject *) priv->child,
1350 (gpointer) & priv->child);
1352 event = (GdkEventButton *) gdk_event_copy ((GdkEvent *) event);
1358 synth_crossing (priv->child, x, y, event->x_root,
1359 event->y_root, event->time, TRUE);
1361 /* Send synthetic click (button press/release) event */
1362 ((GdkEventAny *) event)->window = g_object_ref (priv->child);
1364 gdk_event_put ((GdkEvent *) event);
1365 gdk_event_free ((GdkEvent *) event);
1373 hildon_pannable_area_refresh (HildonPannableArea * area)
1375 HildonPannableAreaPrivate *priv = PANNABLE_AREA_PRIVATE (area);
1376 gboolean prev_hscroll_visible, prev_vscroll_visible;
1378 if (!gtk_bin_get_child (GTK_BIN (area))) {
1379 priv->vscroll_visible = FALSE;
1380 priv->hscroll_visible = FALSE;
1384 prev_hscroll_visible = priv->hscroll_visible;
1385 prev_vscroll_visible = priv->vscroll_visible;
1387 switch (priv->hscrollbar_policy) {
1388 case GTK_POLICY_ALWAYS:
1389 priv->hscroll_visible = TRUE;
1391 case GTK_POLICY_NEVER:
1392 priv->hscroll_visible = FALSE;
1395 priv->hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
1396 priv->hadjust->page_size);
1399 switch (priv->vscrollbar_policy) {
1400 case GTK_POLICY_ALWAYS:
1401 priv->vscroll_visible = TRUE;
1403 case GTK_POLICY_NEVER:
1404 priv->vscroll_visible = FALSE;
1407 priv->vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
1408 priv->vadjust->page_size);
1411 /* Store the vscroll/hscroll areas for redrawing */
1412 if (priv->vscroll_visible) {
1413 GtkAllocation *allocation = >K_WIDGET (area)->allocation;
1414 priv->vscroll_rect.x = allocation->x + allocation->width -
1416 priv->vscroll_rect.y = allocation->y;
1417 priv->vscroll_rect.width = priv->area_width;
1418 priv->vscroll_rect.height = allocation->height -
1419 (priv->hscroll_visible ? priv->area_width : 0);
1421 if (priv->hscroll_visible) {
1422 GtkAllocation *allocation = >K_WIDGET (area)->allocation;
1423 priv->hscroll_rect.y = allocation->y + allocation->height -
1425 priv->hscroll_rect.x = allocation->x;
1426 priv->hscroll_rect.height = priv->area_width;
1427 priv->hscroll_rect.width = allocation->width -
1428 (priv->vscroll_visible ? priv->area_width : 0);
1431 if (GTK_WIDGET_DRAWABLE (area)) {
1432 if (priv->hscroll_visible != prev_hscroll_visible) {
1433 gtk_widget_queue_resize (GTK_WIDGET (area));
1436 if (priv->vscroll_visible != prev_vscroll_visible) {
1437 gtk_widget_queue_resize (GTK_WIDGET (area));
1443 /* Scroll by a particular amount (in pixels). Optionally, return if
1444 * the scroll on a particular axis was successful.
1447 hildon_pannable_axis_scroll (HildonPannableArea *area,
1448 GtkAdjustment *adjust,
1452 gint *overshot_dist,
1458 HildonPannableAreaPrivate *priv = PANNABLE_AREA_PRIVATE (area);
1460 dist = gtk_adjustment_get_value (adjust) - inc;
1463 * We use overshot_dist to define the distance of the current overshoot,
1464 * and overshooting to define the direction/whether or not we are overshot
1466 if (!(*overshooting)) {
1468 /* Initiation of the overshoot happens when the finger is released
1469 * and the current position of the pannable contents are out of range
1471 if (dist < adjust->lower) {
1474 dist = adjust->lower;
1476 if (overshoot_max!=0) {
1479 *overshot_dist = CLAMP (*overshot_dist + *vel, 0, overshoot_max);
1480 gtk_widget_queue_resize (GTK_WIDGET (area));
1484 } else if (dist > adjust->upper - adjust->page_size) {
1487 dist = adjust->upper - adjust->page_size;
1489 if (overshoot_max!=0) {
1492 *overshot_dist = CLAMP (*overshot_dist + *vel, -overshoot_max, 0);
1493 gtk_widget_queue_resize (GTK_WIDGET (area));
1498 if ((*scroll_to) != -1) {
1499 if (((inc < 0)&&(*scroll_to <= dist))||
1500 ((inc > 0)&&(*scroll_to >= dist))) {
1508 gtk_adjustment_set_value (adjust, dist);
1510 if (!priv->clicked) {
1512 /* When the overshoot has started we continue for BOUNCE_STEPS more steps into the overshoot
1513 * before we reverse direction. The deceleration factor is calculated based on
1514 * the percentage distance from the first item with each iteration, therefore always
1515 * returning us to the top/bottom most element
1517 if (*overshot_dist > 0) {
1519 if ((*overshooting < BOUNCE_STEPS) && (*vel > 0)) {
1521 *vel = (((gdouble)*overshot_dist)/overshoot_max) * (*vel);
1522 } else if ((*overshooting >= BOUNCE_STEPS) && (*vel > 0)) {
1525 } else if ((*overshooting > 1) && (*vel < 0)) {
1527 /* we add the MAX in order to avoid very small speeds */
1528 *vel = MIN ((((gdouble)*overshot_dist)/overshoot_max) * (*vel), -10.0);
1531 *overshot_dist = CLAMP (*overshot_dist + *vel, 0, overshoot_max);
1533 gtk_widget_queue_resize (GTK_WIDGET (area));
1535 } else if (*overshot_dist < 0) {
1537 if ((*overshooting < BOUNCE_STEPS) && (*vel < 0)) {
1539 *vel = (((gdouble)*overshot_dist)/overshoot_max) * (*vel) * -1;
1540 } else if ((*overshooting >= BOUNCE_STEPS) && (*vel < 0)) {
1543 } else if ((*overshooting > 1) && (*vel > 0)) {
1545 /* we add the MIN in order to avoid very small speeds */
1546 *vel = MAX ((((gdouble)*overshot_dist)/overshoot_max) * (*vel) * -1, 10.0);
1549 *overshot_dist = CLAMP (*overshot_dist + (*vel), -overshoot_max, 0);
1551 gtk_widget_queue_resize (GTK_WIDGET (area));
1556 gtk_widget_queue_resize (GTK_WIDGET (area));
1559 if (*overshot_dist > 0) {
1560 *overshot_dist = CLAMP ((*overshot_dist) + inc, 0, overshoot_max);
1561 } else if (*overshot_dist < 0) {
1562 *overshot_dist = CLAMP ((*overshot_dist) + inc, -1 * overshoot_max, 0);
1565 gtk_adjustment_set_value (adjust, dist);
1567 gtk_widget_queue_resize (GTK_WIDGET (area));
1573 hildon_pannable_area_scroll (HildonPannableArea *area,
1574 gdouble x, gdouble y)
1577 HildonPannableAreaPrivate *priv;
1578 gboolean hscroll_visible, vscroll_visible;
1580 priv = PANNABLE_AREA_PRIVATE (area);
1582 if (gtk_bin_get_child (GTK_BIN (area)) == NULL)
1585 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
1586 priv->vadjust->page_size);
1587 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
1588 priv->hadjust->page_size);
1593 if (vscroll_visible) {
1594 hildon_pannable_axis_scroll (area, priv->vadjust, &priv->vel_y, y,
1595 &priv->overshooting_y, &priv->overshot_dist_y,
1596 &priv->scroll_to_y, priv->vovershoot_max, &sy);
1601 if (hscroll_visible) {
1602 hildon_pannable_axis_scroll (area, priv->hadjust, &priv->vel_x, x,
1603 &priv->overshooting_x, &priv->overshot_dist_x,
1604 &priv->scroll_to_x, priv->hovershoot_max, &sx);
1609 /* If the scroll on a particular axis wasn't succesful, reset the
1610 * initial scroll position to the new mouse co-ordinate. This means
1611 * when you get to the top of the page, dragging down works immediately.
1624 hildon_pannable_area_timeout (HildonPannableArea * area)
1626 HildonPannableAreaPrivate *priv;
1628 GDK_THREADS_ENTER ();
1630 priv = PANNABLE_AREA_PRIVATE (area);
1632 if ((!priv->enabled) || (priv->mode == HILDON_PANNABLE_AREA_MODE_PUSH)) {
1635 GDK_THREADS_LEAVE ();
1640 if (!priv->clicked) {
1641 /* Decelerate gradually when pointer is raised */
1642 if ((!priv->overshot_dist_y) &&
1643 (!priv->overshot_dist_x)) {
1645 /* in case we move to a specific point do not decelerate when arriving */
1646 if ((priv->scroll_to_x != -1)||(priv->scroll_to_y != -1)) {
1648 if (ABS (priv->vel_x) >= 1.5) {
1649 priv->vel_x *= priv->decel;
1652 if (ABS (priv->vel_y) >= 1.5) {
1653 priv->vel_y *= priv->decel;
1657 priv->vel_x *= priv->decel;
1658 priv->vel_y *= priv->decel;
1660 if ((ABS (priv->vel_x) < 1.0) && (ABS (priv->vel_y) < 1.0)) {
1665 GDK_THREADS_LEAVE ();
1671 } else if (priv->mode == HILDON_PANNABLE_AREA_MODE_AUTO) {
1674 GDK_THREADS_LEAVE ();
1679 hildon_pannable_area_scroll (area, priv->vel_x, priv->vel_y);
1681 GDK_THREADS_LEAVE ();
1687 hildon_pannable_area_calculate_velocity (gdouble *vel,
1695 if (ABS (dist) >= 0.00001) {
1696 rawvel = ((dist / ABS (delta)) *
1697 (gdouble) sps) * FORCE;
1698 *vel = *vel * (1 - SMOOTH_FACTOR) +
1699 rawvel * SMOOTH_FACTOR;
1700 *vel = *vel > 0 ? MIN (*vel, vmax)
1701 : MAX (dist, -1 * vmax);
1706 hildon_pannable_area_motion_notify_cb (GtkWidget * widget,
1707 GdkEventMotion * event)
1709 HildonPannableArea *area = HILDON_PANNABLE_AREA (widget);
1710 HildonPannableAreaPrivate *priv = PANNABLE_AREA_PRIVATE (area);
1715 if (gtk_bin_get_child (GTK_BIN (widget)) == NULL)
1718 if ((!priv->enabled) || (!priv->clicked) ||
1719 ((event->time == priv->last_time) && (priv->last_type == 2))) {
1720 gdk_window_get_pointer (widget->window, NULL, NULL, 0);
1724 if (priv->last_type == 1) {
1725 priv->first_drag = TRUE;
1728 /* Only start the scroll if the mouse cursor passes beyond the
1729 * DnD threshold for dragging.
1731 g_object_get (G_OBJECT (gtk_settings_get_default ()),
1732 "gtk-dnd-drag-threshold", &dnd_threshold, NULL);
1733 x = event->x - priv->x;
1734 y = event->y - priv->y;
1736 if (priv->first_drag && (!priv->moved) &&
1737 ((ABS (x) > (dnd_threshold+DND_THRESHOLD_INC))
1738 || (ABS (y) > (dnd_threshold+DND_THRESHOLD_INC)))) {
1743 if (priv->first_drag) {
1745 if (ABS (priv->iy - event->y) >=
1746 ABS (priv->ix - event->x)) {
1747 gboolean vscroll_visible;
1749 g_signal_emit (area,
1750 pannable_area_signals[VERTICAL_MOVEMENT],
1751 0, (priv->iy > event->y) ?
1752 HILDON_MOVEMENT_UP :
1753 HILDON_MOVEMENT_DOWN,
1754 (gdouble)priv->ix, (gdouble)priv->iy);
1756 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
1757 priv->vadjust->page_size);
1759 if (!((vscroll_visible)&&
1760 (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT)))
1761 priv->moved = FALSE;
1764 gboolean hscroll_visible;
1766 g_signal_emit (area,
1767 pannable_area_signals[HORIZONTAL_MOVEMENT],
1768 0, (priv->ix > event->x) ?
1769 HILDON_MOVEMENT_LEFT :
1770 HILDON_MOVEMENT_RIGHT,
1771 (gdouble)priv->ix, (gdouble)priv->iy);
1773 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
1774 priv->hadjust->page_size);
1776 if (!((hscroll_visible)&&
1777 (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ)))
1778 priv->moved = FALSE;
1782 priv->first_drag = FALSE;
1784 if ((priv->mode != HILDON_PANNABLE_AREA_MODE_PUSH) &&
1785 (priv->mode != HILDON_PANNABLE_AREA_MODE_AUTO)) {
1788 priv->idle_id = g_timeout_add ((gint)
1789 (1000.0 / (gdouble) priv->sps),
1791 hildon_pannable_area_timeout, area);
1796 switch (priv->mode) {
1797 case HILDON_PANNABLE_AREA_MODE_PUSH:
1798 /* Scroll by the amount of pixels the cursor has moved
1799 * since the last motion event.
1801 hildon_pannable_area_scroll (area, x, y);
1805 case HILDON_PANNABLE_AREA_MODE_ACCEL:
1806 /* Set acceleration relative to the initial click */
1807 priv->ex = event->x;
1808 priv->ey = event->y;
1809 priv->vel_x = ((x > 0) ? 1 : -1) *
1811 (gdouble) widget->allocation.width) *
1812 (priv->vmax - priv->vmin)) + priv->vmin);
1813 priv->vel_y = ((y > 0) ? 1 : -1) *
1815 (gdouble) widget->allocation.height) *
1816 (priv->vmax - priv->vmin)) + priv->vmin);
1818 case HILDON_PANNABLE_AREA_MODE_AUTO:
1820 delta = event->time - priv->last_time;
1822 if (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT) {
1823 gdouble dist = event->y - priv->y;
1825 hildon_pannable_area_calculate_velocity (&priv->vel_y,
1836 if (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ) {
1837 gdouble dist = event->x - priv->x;
1839 hildon_pannable_area_calculate_velocity (&priv->vel_x,
1849 hildon_pannable_area_scroll (area, x, y);
1851 if (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ)
1853 if (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT)
1864 /* Send motion notify to child */
1865 priv->last_time = event->time;
1866 priv->last_type = 2;
1867 event = (GdkEventMotion *) gdk_event_copy ((GdkEvent *) event);
1868 event->x = priv->cx + (event->x - priv->ix);
1869 event->y = priv->cy + (event->y - priv->iy);
1870 event->window = g_object_ref (priv->child);
1871 gdk_event_put ((GdkEvent *) event);
1872 gdk_event_free ((GdkEvent *) event);
1875 gdk_window_get_pointer (widget->window, NULL, NULL, 0);
1881 hildon_pannable_area_button_release_cb (GtkWidget * widget,
1882 GdkEventButton * event)
1884 HildonPannableAreaPrivate *priv = PANNABLE_AREA_PRIVATE (widget);
1888 if (gtk_bin_get_child (GTK_BIN (widget)) == NULL)
1891 priv->scroll_indicator_event_interrupt = 0;
1892 priv->scroll_delay_counter = SCROLLBAR_FADE_DELAY;
1894 if ((ABS (priv->vel_y) > 1.0)||
1895 (ABS (priv->vel_x) > 1.0)) {
1896 priv->scroll_indicator_alpha = 1.0;
1899 if (!priv->scroll_indicator_timeout) {
1900 priv->scroll_indicator_timeout = g_timeout_add
1901 ((gint) (1000.0 / (gdouble) priv->sps),
1902 (GSourceFunc) hildon_pannable_area_scroll_indicator_fade, widget);
1905 if ((!priv->clicked) || (!priv->enabled) || (event->button != 1) ||
1906 ((event->time == priv->last_time) && (priv->last_type == 3)))
1909 priv->clicked = FALSE;
1911 if (priv->mode == HILDON_PANNABLE_AREA_MODE_AUTO ||
1912 priv->mode == HILDON_PANNABLE_AREA_MODE_ACCEL) {
1914 /* If overshoot has been initiated with a finger down, on release set max speed */
1915 if (priv->overshot_dist_y != 0) {
1916 priv->overshooting_y = BOUNCE_STEPS; /* Hack to stop a bounce in the finger down case */
1917 priv->vel_y = priv->vmax;
1920 if (priv->overshot_dist_x != 0) {
1921 priv->overshooting_x = BOUNCE_STEPS; /* Hack to stop a bounce in the finger down case */
1922 priv->vel_x = priv->vmax;
1926 priv->idle_id = g_timeout_add ((gint) (1000.0 / (gdouble) priv->sps),
1928 hildon_pannable_area_timeout, widget);
1931 priv->last_time = event->time;
1932 priv->last_type = 3;
1935 priv->moved = FALSE;
1940 hildon_pannable_area_get_topmost (gtk_bin_get_child (GTK_BIN (widget))->window,
1941 event->x, event->y, &x, &y);
1943 event = (GdkEventButton *) gdk_event_copy ((GdkEvent *) event);
1947 /* Leave the widget if we've moved - This doesn't break selection,
1948 * but stops buttons from being clicked.
1950 if ((child != priv->child) || (priv->moved)) {
1951 /* Send synthetic leave event */
1952 synth_crossing (priv->child, x, y, event->x_root,
1953 event->y_root, event->time, FALSE);
1954 /* Send synthetic button release event */
1955 ((GdkEventAny *) event)->window = g_object_ref (priv->child);
1956 gdk_event_put ((GdkEvent *) event);
1958 /* Send synthetic button release event */
1959 ((GdkEventAny *) event)->window = g_object_ref (child);
1960 gdk_event_put ((GdkEvent *) event);
1961 /* Send synthetic leave event */
1962 synth_crossing (priv->child, x, y, event->x_root,
1963 event->y_root, event->time, FALSE);
1965 g_object_remove_weak_pointer ((GObject *) priv->child,
1966 (gpointer) & priv->child);
1968 priv->moved = FALSE;
1969 gdk_event_free ((GdkEvent *) event);
1974 /* utility event handler */
1976 hildon_pannable_area_scroll_cb (GtkWidget *widget,
1977 GdkEventScroll *event)
1979 GtkAdjustment *adj = NULL;
1980 HildonPannableAreaPrivate *priv = PANNABLE_AREA_PRIVATE (widget);
1982 if ((!priv->enabled) ||
1983 (gtk_bin_get_child (GTK_BIN (widget)) == NULL))
1986 priv->scroll_indicator_event_interrupt = 0;
1987 priv->scroll_indicator_alpha = 1.0;
1988 priv->scroll_delay_counter = SCROLLBAR_FADE_DELAY + 20;
1990 if (!priv->scroll_indicator_timeout) {
1991 priv->scroll_indicator_timeout = g_timeout_add
1992 ((gint) (1000.0 / (gdouble) (priv->sps*2)),
1993 (GSourceFunc) hildon_pannable_area_scroll_indicator_fade, widget);
1996 /* Stop inertial scrolling */
1997 if (priv->idle_id) {
2000 priv->overshooting_x = 0;
2001 priv->overshooting_y = 0;
2003 if ((priv->overshot_dist_x>0)||(priv->overshot_dist_y>0)) {
2004 priv->overshot_dist_x = 0;
2005 priv->overshot_dist_y = 0;
2007 gtk_widget_queue_resize (GTK_WIDGET (widget));
2010 g_source_remove (priv->idle_id);
2014 if (event->direction == GDK_SCROLL_UP || event->direction == GDK_SCROLL_DOWN)
2015 adj = priv->vadjust;
2017 adj = priv->hadjust;
2021 gdouble delta, new_value;
2023 /* from gtkrange.c calculate delta*/
2024 delta = pow (adj->page_size, 2.0 / 3.0);
2026 if (event->direction == GDK_SCROLL_UP ||
2027 event->direction == GDK_SCROLL_LEFT)
2030 new_value = CLAMP (adj->value + delta, adj->lower, adj->upper - adj->page_size);
2032 gtk_adjustment_set_value (adj, new_value);
2040 hildon_pannable_area_add (GtkContainer *container, GtkWidget *child)
2042 HildonPannableAreaPrivate *priv = PANNABLE_AREA_PRIVATE (container);
2044 g_return_if_fail (gtk_bin_get_child (GTK_BIN (container)) == NULL);
2046 gtk_widget_set_parent (child, GTK_WIDGET (container));
2047 GTK_BIN (container)->child = child;
2049 if (!gtk_widget_set_scroll_adjustments (child, priv->hadjust, priv->vadjust)) {
2050 g_warning ("%s: cannot add non scrollable widget, "
2051 "wrap it in a viewport", __FUNCTION__);
2056 hildon_pannable_area_remove (GtkContainer *container, GtkWidget *child)
2058 g_return_if_fail (HILDON_IS_PANNABLE_AREA (container));
2059 g_return_if_fail (child != NULL);
2060 g_return_if_fail (gtk_bin_get_child (GTK_BIN (container)) == child);
2062 gtk_widget_set_scroll_adjustments (child, NULL, NULL);
2064 /* chain parent class handler to remove child */
2065 GTK_CONTAINER_CLASS (hildon_pannable_area_parent_class)->remove (container, child);
2069 hildon_pannable_calculate_vel_factor (HildonPannableArea * self)
2071 HildonPannableAreaPrivate *priv = PANNABLE_AREA_PRIVATE (self);
2076 n = ceil (priv->sps * priv->scroll_time);
2078 for (i = 0; i < n && fct_i >= RATIO_TOLERANCE; i++) {
2079 fct_i *= priv->decel;
2083 priv->vel_factor = fct;
2087 * hildon_pannable_area_new:
2089 * Create a new pannable area widget
2091 * Returns: the newly created #HildonPannableArea
2095 hildon_pannable_area_new (void)
2097 return g_object_new (HILDON_TYPE_PANNABLE_AREA, NULL);
2101 * hildon_pannable_area_new_full:
2102 * @mode: #HildonPannableAreaMode
2103 * @enabled: Value for the enabled property
2104 * @vel_min: Value for the velocity-min property
2105 * @vel_max: Value for the velocity-max property
2106 * @decel: Value for the deceleration property
2107 * @sps: Value for the sps property
2109 * Create a new #HildonPannableArea widget and set various properties
2111 * returns: the newly create #HildonPannableArea
2115 hildon_pannable_area_new_full (gint mode, gboolean enabled,
2116 gdouble vel_min, gdouble vel_max,
2117 gdouble decel, guint sps)
2119 return g_object_new (HILDON_TYPE_PANNABLE_AREA,
2122 "velocity_min", vel_min,
2123 "velocity_max", vel_max,
2124 "deceleration", decel, "sps", sps, NULL);
2128 * hildon_pannable_area_add_with_viewport:
2129 * @area: A #HildonPannableArea
2130 * @child: Child widget to add to the viewport
2132 * Convenience function used to add a child to a #GtkViewport, and add the
2133 * viewport to the scrolled window.
2137 hildon_pannable_area_add_with_viewport (HildonPannableArea * area,
2140 GtkWidget *viewport = gtk_viewport_new (NULL, NULL);
2141 gtk_viewport_set_shadow_type (GTK_VIEWPORT (viewport), GTK_SHADOW_NONE);
2142 gtk_container_add (GTK_CONTAINER (viewport), child);
2143 gtk_widget_show (viewport);
2144 gtk_container_add (GTK_CONTAINER (area), viewport);
2148 * hildon_pannable_area_scroll_to:
2149 * @area: A #HildonPannableArea.
2150 * @x: The x coordinate of the destination point or -1 to ignore this axis.
2151 * @y: The y coordinate of the destination point or -1 to ignore this axis.
2153 * Smoothly scrolls @area to ensure that (@x, @y) is a visible point
2154 * on the widget. To move in only one coordinate, you must set the other one
2155 * to -1. Notice that, in %HILDON_PANNABLE_AREA_MODE_PUSH mode, this function
2156 * works just like hildon_pannable_area_jump_to().
2158 * This function is useful if you need to present the user with a particular
2159 * element inside a scrollable widget, like #GtkTreeView. For instance,
2160 * the following example shows how to scroll inside a #GtkTreeView to
2161 * make visible an item, indicated by the #GtkTreeIter @iter.
2165 * GtkTreePath *path;
2166 * GdkRectangle *rect;
2168 * path = gtk_tree_model_get_path (model, &iter);
2169 * gtk_tree_view_get_background_area (GTK_TREE_VIEW (treeview),
2170 * path, NULL, &rect);
2171 * gtk_tree_view_convert_bin_window_to_tree_coords (GTK_TREE_VIEW (treeview),
2172 * 0, rect.y, NULL, &y);
2173 * hildon_pannable_area_scroll_to (panarea, -1, y);
2174 * gtk_tree_path_free (path);
2178 * If you want to present a child widget in simpler scenarios,
2179 * use hildon_pannable_area_scroll_to_child() instead.
2183 hildon_pannable_area_scroll_to (HildonPannableArea *area,
2184 const gint x, const gint y)
2186 HildonPannableAreaPrivate *priv;
2188 gint dist_x, dist_y;
2189 gboolean hscroll_visible, vscroll_visible;
2191 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
2193 priv = PANNABLE_AREA_PRIVATE (area);
2195 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
2196 priv->vadjust->page_size);
2197 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
2198 priv->hadjust->page_size);
2200 if (((!vscroll_visible)&&(!hscroll_visible)) ||
2201 (x == -1 && y == -1)) {
2205 if (priv->mode == HILDON_PANNABLE_AREA_MODE_PUSH)
2206 hildon_pannable_area_jump_to (area, x, y);
2208 width = priv->hadjust->upper - priv->hadjust->lower;
2209 height = priv->vadjust->upper - priv->vadjust->lower;
2211 g_return_if_fail (x < width || y < height);
2214 priv->scroll_to_x = x - priv->hadjust->page_size/2;
2215 dist_x = priv->scroll_to_x - priv->hadjust->value;
2217 priv->scroll_to_x = -1;
2219 priv->vel_x = - dist_x/priv->vel_factor;
2222 priv->scroll_to_x = -1;
2226 priv->scroll_to_y = y - priv->vadjust->page_size/2;
2227 dist_y = priv->scroll_to_y - priv->vadjust->value;
2229 priv->scroll_to_y = -1;
2231 priv->vel_y = - dist_y/priv->vel_factor;
2234 priv->scroll_to_y = y;
2237 if ((priv->scroll_to_y == -1) && (priv->scroll_to_y == -1)) {
2241 priv->scroll_indicator_alpha = 1.0;
2243 if (!priv->scroll_indicator_timeout)
2244 priv->scroll_indicator_timeout = g_timeout_add
2245 ((gint) (1000.0 / (gdouble) priv->sps),
2246 (GSourceFunc) hildon_pannable_area_scroll_indicator_fade, area);
2249 priv->idle_id = g_timeout_add ((gint) (1000.0 / (gdouble) priv->sps),
2251 hildon_pannable_area_timeout, area);
2255 * hildon_pannable_area_jump_to:
2256 * @area: A #HildonPannableArea.
2257 * @x: The x coordinate of the destination point or -1 to ignore this axis.
2258 * @y: The y coordinate of the destination point or -1 to ignore this axis.
2260 * Jumps the position of @area to ensure that (@x, @y) is a visible
2261 * point in the widget. In order to move in only one coordinate, you
2262 * must set the other one to -1. See hildon_pannable_area_scroll_to()
2263 * function for an example of how to calculate the position of
2264 * children in scrollable widgets like #GtkTreeview.
2268 hildon_pannable_area_jump_to (HildonPannableArea *area,
2269 const gint x, const gint y)
2271 HildonPannableAreaPrivate *priv;
2274 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
2275 g_return_if_fail (x >= -1 && y >= -1);
2277 if (x == -1 && y == -1) {
2281 priv = PANNABLE_AREA_PRIVATE (area);
2283 width = priv->hadjust->upper - priv->hadjust->lower;
2284 height = priv->vadjust->upper - priv->vadjust->lower;
2286 g_return_if_fail (x < width || y < height);
2289 gdouble jump_to = x - priv->hadjust->page_size/2;
2291 if (jump_to > priv->hadjust->upper - priv->hadjust->page_size) {
2292 jump_to = priv->hadjust->upper - priv->hadjust->page_size;
2295 gtk_adjustment_set_value (priv->hadjust, jump_to);
2299 gdouble jump_to = y - priv->vadjust->page_size/2;
2301 if (jump_to > priv->vadjust->upper - priv->vadjust->page_size) {
2302 jump_to = priv->vadjust->upper - priv->vadjust->page_size;
2305 gtk_adjustment_set_value (priv->vadjust, jump_to);
2308 priv->scroll_indicator_alpha = 1.0;
2310 if (priv->scroll_indicator_timeout) {
2311 g_source_remove (priv->scroll_indicator_timeout);
2312 priv->scroll_indicator_timeout = 0;
2315 if (priv->idle_id) {
2318 priv->overshooting_x = 0;
2319 priv->overshooting_y = 0;
2321 if ((priv->overshot_dist_x>0)||(priv->overshot_dist_y>0)) {
2322 priv->overshot_dist_x = 0;
2323 priv->overshot_dist_y = 0;
2325 gtk_widget_queue_resize (GTK_WIDGET (area));
2328 g_source_remove (priv->idle_id);
2334 * hildon_pannable_area_scroll_to_child:
2335 * @area: A #HildonPannableArea.
2336 * @child: A #GtkWidget, descendant of @area.
2338 * Smoothly scrolls until @child is visible inside @area. @child must
2339 * be a descendant of @area. If you need to scroll inside a scrollable
2340 * widget, e.g., #GtkTreeview, see hildon_pannable_area_scroll_to().
2344 hildon_pannable_area_scroll_to_child (HildonPannableArea *area, GtkWidget *child)
2346 GtkWidget *bin_child;
2349 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
2350 g_return_if_fail (GTK_IS_WIDGET (child));
2351 g_return_if_fail (gtk_widget_is_ancestor (child, GTK_WIDGET (area)));
2353 if (GTK_BIN (area)->child == NULL)
2356 /* We need to get to check the child of the inside the area */
2357 bin_child = GTK_BIN (area)->child;
2359 /* we check if we added a viewport */
2360 if (GTK_IS_VIEWPORT (bin_child)) {
2361 bin_child = GTK_BIN (bin_child)->child;
2364 if (gtk_widget_translate_coordinates (child, bin_child, 0, 0, &x, &y))
2365 hildon_pannable_area_scroll_to (area, x, y);
2369 * hildon_pannable_area_jump_to_child:
2370 * @area: A #HildonPannableArea.
2371 * @child: A #GtkWidget, descendant of @area.
2373 * Jumps to make sure @child is visible inside @area. @child must
2374 * be a descendant of @area. If you want to move inside a scrollable
2375 * widget, like, #GtkTreeview, see hildon_pannable_area_scroll_to().
2379 hildon_pannable_area_jump_to_child (HildonPannableArea *area, GtkWidget *child)
2381 GtkWidget *bin_child;
2384 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
2385 g_return_if_fail (GTK_IS_WIDGET (child));
2386 g_return_if_fail (gtk_widget_is_ancestor (child, GTK_WIDGET (area)));
2388 if (gtk_bin_get_child (GTK_BIN (area)) == NULL)
2391 /* We need to get to check the child of the inside the area */
2392 bin_child = gtk_bin_get_child (GTK_BIN (area));
2394 /* we check if we added a viewport */
2395 if (GTK_IS_VIEWPORT (bin_child)) {
2396 bin_child = gtk_bin_get_child (GTK_BIN (bin_child));
2399 if (gtk_widget_translate_coordinates (child, bin_child, 0, 0, &x, &y))
2400 hildon_pannable_area_jump_to (area, x, y);
2404 * hildon_pannable_get_child_widget_at:
2405 * @area: A #HildonPannableArea.
2406 * @x: horizontal coordinate of the point
2407 * @y: vertical coordinate of the point
2409 * Get the widget at the point (x, y) inside the pannable area. In
2410 * case no widget found it returns NULL.
2412 * returns: the #GtkWidget if we find a widget, NULL in any other case
2415 hildon_pannable_get_child_widget_at (HildonPannableArea *area,
2416 gdouble x, gdouble y)
2418 GdkWindow *window = NULL;
2419 GtkWidget *child_widget = NULL;
2421 window = hildon_pannable_area_get_topmost
2422 (gtk_bin_get_child (GTK_BIN (area))->window,
2425 gdk_window_get_user_data (window, (gpointer) &child_widget);
2427 return child_widget;