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 += GTK_CONTAINER (widget)->border_width;
712 requisition->height += 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_window_get_geometry (child, &wx, &wy, &width, &height, NULL);
1242 if (((x >= wx) && (x < (wx + width)) && (y >= wy)
1243 && (y < (wy + height))) && (gdk_window_is_visible (child))) {
1250 if (window == old_window)
1266 synth_crossing (GdkWindow * child,
1268 gint x_root, gint y_root,
1269 guint32 time, gboolean in)
1271 GdkEventCrossing *crossing_event;
1272 GdkEventType type = in ? GDK_ENTER_NOTIFY : GDK_LEAVE_NOTIFY;
1274 /* Send synthetic enter event */
1275 crossing_event = (GdkEventCrossing *) gdk_event_new (type);
1276 ((GdkEventAny *) crossing_event)->type = type;
1277 ((GdkEventAny *) crossing_event)->window = g_object_ref (child);
1278 ((GdkEventAny *) crossing_event)->send_event = FALSE;
1279 crossing_event->subwindow = g_object_ref (child);
1280 crossing_event->time = time;
1281 crossing_event->x = x;
1282 crossing_event->y = y;
1283 crossing_event->x_root = x_root;
1284 crossing_event->y_root = y_root;
1285 crossing_event->mode = GDK_CROSSING_NORMAL;
1286 crossing_event->detail = GDK_NOTIFY_UNKNOWN;
1287 crossing_event->focus = FALSE;
1288 crossing_event->state = 0;
1289 gdk_event_put ((GdkEvent *) crossing_event);
1290 gdk_event_free ((GdkEvent *) crossing_event);
1294 hildon_pannable_area_button_press_cb (GtkWidget * widget,
1295 GdkEventButton * event)
1298 HildonPannableAreaPrivate *priv = PANNABLE_AREA_PRIVATE (widget);
1300 if ((!priv->enabled) || (event->button != 1) ||
1301 ((event->time == priv->last_time) &&
1302 (priv->last_type == 1)) || (gtk_bin_get_child (GTK_BIN (widget)) == NULL))
1305 priv->scroll_indicator_event_interrupt = 1;
1306 priv->scroll_delay_counter = SCROLLBAR_FADE_DELAY;
1308 if (!priv->scroll_indicator_timeout){
1309 priv->scroll_indicator_timeout = g_timeout_add
1310 ((gint) (1000.0 / (gdouble) (priv->sps*2)),
1311 (GSourceFunc) hildon_pannable_area_scroll_indicator_fade, widget);
1314 priv->last_time = event->time;
1315 priv->last_type = 1;
1317 priv->scroll_to_x = -1;
1318 priv->scroll_to_y = -1;
1320 if (priv->clicked && priv->child) {
1321 /* Widget stole focus on last click, send crossing-out event */
1322 synth_crossing (priv->child, 0, 0, event->x_root, event->y_root,
1323 event->time, FALSE);
1331 /* Don't allow a click if we're still moving fast */
1332 if ((ABS (priv->vel_x) <= (priv->vmax * priv->vfast_factor)) &&
1333 (ABS (priv->vel_y) <= (priv->vmax * priv->vfast_factor)))
1335 hildon_pannable_area_get_topmost (gtk_bin_get_child (GTK_BIN (widget))->window,
1336 event->x, event->y, &x, &y);
1340 priv->clicked = TRUE;
1342 /* Stop scrolling on mouse-down (so you can flick, then hold to stop) */
1346 if ((priv->child) && (priv->child != gtk_bin_get_child (GTK_BIN (widget))->window)) {
1348 g_object_add_weak_pointer ((GObject *) priv->child,
1349 (gpointer) & priv->child);
1351 event = (GdkEventButton *) gdk_event_copy ((GdkEvent *) event);
1357 synth_crossing (priv->child, x, y, event->x_root,
1358 event->y_root, event->time, TRUE);
1360 /* Send synthetic click (button press/release) event */
1361 ((GdkEventAny *) event)->window = g_object_ref (priv->child);
1363 gdk_event_put ((GdkEvent *) event);
1364 gdk_event_free ((GdkEvent *) event);
1372 hildon_pannable_area_refresh (HildonPannableArea * area)
1374 HildonPannableAreaPrivate *priv = PANNABLE_AREA_PRIVATE (area);
1375 gboolean prev_hscroll_visible, prev_vscroll_visible;
1377 if (!gtk_bin_get_child (GTK_BIN (area))) {
1378 priv->vscroll_visible = FALSE;
1379 priv->hscroll_visible = FALSE;
1383 prev_hscroll_visible = priv->hscroll_visible;
1384 prev_vscroll_visible = priv->vscroll_visible;
1386 switch (priv->hscrollbar_policy) {
1387 case GTK_POLICY_ALWAYS:
1388 priv->hscroll_visible = TRUE;
1390 case GTK_POLICY_NEVER:
1391 priv->hscroll_visible = FALSE;
1394 priv->hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
1395 priv->hadjust->page_size);
1398 switch (priv->vscrollbar_policy) {
1399 case GTK_POLICY_ALWAYS:
1400 priv->vscroll_visible = TRUE;
1402 case GTK_POLICY_NEVER:
1403 priv->vscroll_visible = FALSE;
1406 priv->vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
1407 priv->vadjust->page_size);
1410 /* Store the vscroll/hscroll areas for redrawing */
1411 if (priv->vscroll_visible) {
1412 GtkAllocation *allocation = >K_WIDGET (area)->allocation;
1413 priv->vscroll_rect.x = allocation->x + allocation->width -
1415 priv->vscroll_rect.y = allocation->y;
1416 priv->vscroll_rect.width = priv->area_width;
1417 priv->vscroll_rect.height = allocation->height -
1418 (priv->hscroll_visible ? priv->area_width : 0);
1420 if (priv->hscroll_visible) {
1421 GtkAllocation *allocation = >K_WIDGET (area)->allocation;
1422 priv->hscroll_rect.y = allocation->y + allocation->height -
1424 priv->hscroll_rect.x = allocation->x;
1425 priv->hscroll_rect.height = priv->area_width;
1426 priv->hscroll_rect.width = allocation->width -
1427 (priv->vscroll_visible ? priv->area_width : 0);
1430 if (GTK_WIDGET_DRAWABLE (area)) {
1431 if (priv->hscroll_visible != prev_hscroll_visible) {
1432 gtk_widget_queue_resize (GTK_WIDGET (area));
1435 if (priv->vscroll_visible != prev_vscroll_visible) {
1436 gtk_widget_queue_resize (GTK_WIDGET (area));
1442 /* Scroll by a particular amount (in pixels). Optionally, return if
1443 * the scroll on a particular axis was successful.
1446 hildon_pannable_axis_scroll (HildonPannableArea *area,
1447 GtkAdjustment *adjust,
1451 gint *overshot_dist,
1457 HildonPannableAreaPrivate *priv = PANNABLE_AREA_PRIVATE (area);
1459 dist = gtk_adjustment_get_value (adjust) - inc;
1462 * We use overshot_dist to define the distance of the current overshoot,
1463 * and overshooting to define the direction/whether or not we are overshot
1465 if (!(*overshooting)) {
1467 /* Initiation of the overshoot happens when the finger is released
1468 * and the current position of the pannable contents are out of range
1470 if (dist < adjust->lower) {
1473 dist = adjust->lower;
1475 if (overshoot_max!=0) {
1478 *overshot_dist = CLAMP (*overshot_dist + *vel, 0, overshoot_max);
1479 gtk_widget_queue_resize (GTK_WIDGET (area));
1483 } else if (dist > adjust->upper - adjust->page_size) {
1486 dist = adjust->upper - adjust->page_size;
1488 if (overshoot_max!=0) {
1491 *overshot_dist = CLAMP (*overshot_dist + *vel, -overshoot_max, 0);
1492 gtk_widget_queue_resize (GTK_WIDGET (area));
1497 if ((*scroll_to) != -1) {
1498 if (((inc < 0)&&(*scroll_to <= dist))||
1499 ((inc > 0)&&(*scroll_to >= dist))) {
1507 gtk_adjustment_set_value (adjust, dist);
1509 if (!priv->clicked) {
1511 /* When the overshoot has started we continue for BOUNCE_STEPS more steps into the overshoot
1512 * before we reverse direction. The deceleration factor is calculated based on
1513 * the percentage distance from the first item with each iteration, therefore always
1514 * returning us to the top/bottom most element
1516 if (*overshot_dist > 0) {
1518 if ((*overshooting < BOUNCE_STEPS) && (*vel > 0)) {
1520 *vel = (((gdouble)*overshot_dist)/overshoot_max) * (*vel);
1521 } else if ((*overshooting >= BOUNCE_STEPS) && (*vel > 0)) {
1524 } else if ((*overshooting > 1) && (*vel < 0)) {
1526 /* we add the MAX in order to avoid very small speeds */
1527 *vel = MIN ((((gdouble)*overshot_dist)/overshoot_max) * (*vel), -10.0);
1530 *overshot_dist = CLAMP (*overshot_dist + *vel, 0, overshoot_max);
1532 gtk_widget_queue_resize (GTK_WIDGET (area));
1534 } else if (*overshot_dist < 0) {
1536 if ((*overshooting < BOUNCE_STEPS) && (*vel < 0)) {
1538 *vel = (((gdouble)*overshot_dist)/overshoot_max) * (*vel) * -1;
1539 } else if ((*overshooting >= BOUNCE_STEPS) && (*vel < 0)) {
1542 } else if ((*overshooting > 1) && (*vel > 0)) {
1544 /* we add the MIN in order to avoid very small speeds */
1545 *vel = MAX ((((gdouble)*overshot_dist)/overshoot_max) * (*vel) * -1, 10.0);
1548 *overshot_dist = CLAMP (*overshot_dist + (*vel), -overshoot_max, 0);
1550 gtk_widget_queue_resize (GTK_WIDGET (area));
1555 gtk_widget_queue_resize (GTK_WIDGET (area));
1558 if (*overshot_dist > 0) {
1559 *overshot_dist = CLAMP ((*overshot_dist) + inc, 0, overshoot_max);
1560 } else if (*overshot_dist < 0) {
1561 *overshot_dist = CLAMP ((*overshot_dist) + inc, -1 * overshoot_max, 0);
1564 gtk_adjustment_set_value (adjust, dist);
1566 gtk_widget_queue_resize (GTK_WIDGET (area));
1572 hildon_pannable_area_scroll (HildonPannableArea *area,
1573 gdouble x, gdouble y)
1576 HildonPannableAreaPrivate *priv;
1577 gboolean hscroll_visible, vscroll_visible;
1579 priv = PANNABLE_AREA_PRIVATE (area);
1581 if (gtk_bin_get_child (GTK_BIN (area)) == NULL)
1584 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
1585 priv->vadjust->page_size);
1586 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
1587 priv->hadjust->page_size);
1592 if (vscroll_visible) {
1593 hildon_pannable_axis_scroll (area, priv->vadjust, &priv->vel_y, y,
1594 &priv->overshooting_y, &priv->overshot_dist_y,
1595 &priv->scroll_to_y, priv->vovershoot_max, &sy);
1598 if (hscroll_visible) {
1599 hildon_pannable_axis_scroll (area, priv->hadjust, &priv->vel_x, x,
1600 &priv->overshooting_x, &priv->overshot_dist_x,
1601 &priv->scroll_to_x, priv->hovershoot_max, &sx);
1604 /* If the scroll on a particular axis wasn't succesful, reset the
1605 * initial scroll position to the new mouse co-ordinate. This means
1606 * when you get to the top of the page, dragging down works immediately.
1619 hildon_pannable_area_timeout (HildonPannableArea * area)
1621 HildonPannableAreaPrivate *priv;
1623 GDK_THREADS_ENTER ();
1625 priv = PANNABLE_AREA_PRIVATE (area);
1627 if ((!priv->enabled) || (priv->mode == HILDON_PANNABLE_AREA_MODE_PUSH)) {
1630 GDK_THREADS_LEAVE ();
1635 if (!priv->clicked) {
1636 /* Decelerate gradually when pointer is raised */
1637 if ((!priv->overshot_dist_y) &&
1638 (!priv->overshot_dist_x)) {
1640 /* in case we move to a specific point do not decelerate when arriving */
1641 if ((priv->scroll_to_x != -1)||(priv->scroll_to_y != -1)) {
1643 if (ABS (priv->vel_x) >= 1.5) {
1644 priv->vel_x *= priv->decel;
1647 if (ABS (priv->vel_y) >= 1.5) {
1648 priv->vel_y *= priv->decel;
1652 priv->vel_x *= priv->decel;
1653 priv->vel_y *= priv->decel;
1655 if ((ABS (priv->vel_x) < 1.0) && (ABS (priv->vel_y) < 1.0)) {
1660 GDK_THREADS_LEAVE ();
1666 } else if (priv->mode == HILDON_PANNABLE_AREA_MODE_AUTO) {
1669 GDK_THREADS_LEAVE ();
1674 hildon_pannable_area_scroll (area, priv->vel_x, priv->vel_y);
1676 GDK_THREADS_LEAVE ();
1682 hildon_pannable_area_calculate_velocity (gdouble *vel,
1690 if (ABS (dist) >= 0.00001) {
1691 rawvel = ((dist / ABS (delta)) *
1692 (gdouble) sps) * FORCE;
1693 *vel = *vel * (1 - SMOOTH_FACTOR) +
1694 rawvel * SMOOTH_FACTOR;
1695 *vel = *vel > 0 ? MIN (*vel, vmax)
1696 : MAX (dist, -1 * vmax);
1701 hildon_pannable_area_motion_notify_cb (GtkWidget * widget,
1702 GdkEventMotion * event)
1704 HildonPannableArea *area = HILDON_PANNABLE_AREA (widget);
1705 HildonPannableAreaPrivate *priv = PANNABLE_AREA_PRIVATE (area);
1710 if (gtk_bin_get_child (GTK_BIN (widget)) == NULL)
1713 if ((!priv->enabled) || (!priv->clicked) ||
1714 ((event->time == priv->last_time) && (priv->last_type == 2))) {
1715 gdk_window_get_pointer (widget->window, NULL, NULL, 0);
1719 if (priv->last_type == 1) {
1720 priv->first_drag = TRUE;
1723 /* Only start the scroll if the mouse cursor passes beyond the
1724 * DnD threshold for dragging.
1726 g_object_get (G_OBJECT (gtk_settings_get_default ()),
1727 "gtk-dnd-drag-threshold", &dnd_threshold, NULL);
1728 x = event->x - priv->x;
1729 y = event->y - priv->y;
1731 if (priv->first_drag && (!priv->moved) &&
1732 ((ABS (x) > (dnd_threshold+DND_THRESHOLD_INC))
1733 || (ABS (y) > (dnd_threshold+DND_THRESHOLD_INC)))) {
1738 if (priv->first_drag) {
1740 if (ABS (priv->iy - event->y) >=
1741 ABS (priv->ix - event->x)) {
1742 gboolean vscroll_visible;
1744 g_signal_emit (area,
1745 pannable_area_signals[VERTICAL_MOVEMENT],
1746 0, (priv->iy > event->y) ?
1747 HILDON_MOVEMENT_UP :
1748 HILDON_MOVEMENT_DOWN,
1749 (gdouble)priv->ix, (gdouble)priv->iy);
1751 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
1752 priv->vadjust->page_size);
1754 if (!((vscroll_visible)&&
1755 (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT)))
1756 priv->moved = FALSE;
1759 gboolean hscroll_visible;
1761 g_signal_emit (area,
1762 pannable_area_signals[HORIZONTAL_MOVEMENT],
1763 0, (priv->ix > event->x) ?
1764 HILDON_MOVEMENT_LEFT :
1765 HILDON_MOVEMENT_RIGHT,
1766 (gdouble)priv->ix, (gdouble)priv->iy);
1768 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
1769 priv->hadjust->page_size);
1771 if (!((hscroll_visible)&&
1772 (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ)))
1773 priv->moved = FALSE;
1777 priv->first_drag = FALSE;
1779 if ((priv->mode != HILDON_PANNABLE_AREA_MODE_PUSH) &&
1780 (priv->mode != HILDON_PANNABLE_AREA_MODE_AUTO)) {
1783 priv->idle_id = g_timeout_add ((gint)
1784 (1000.0 / (gdouble) priv->sps),
1786 hildon_pannable_area_timeout, area);
1791 switch (priv->mode) {
1792 case HILDON_PANNABLE_AREA_MODE_PUSH:
1793 /* Scroll by the amount of pixels the cursor has moved
1794 * since the last motion event.
1796 hildon_pannable_area_scroll (area, x, y);
1800 case HILDON_PANNABLE_AREA_MODE_ACCEL:
1801 /* Set acceleration relative to the initial click */
1802 priv->ex = event->x;
1803 priv->ey = event->y;
1804 priv->vel_x = ((x > 0) ? 1 : -1) *
1806 (gdouble) widget->allocation.width) *
1807 (priv->vmax - priv->vmin)) + priv->vmin);
1808 priv->vel_y = ((y > 0) ? 1 : -1) *
1810 (gdouble) widget->allocation.height) *
1811 (priv->vmax - priv->vmin)) + priv->vmin);
1813 case HILDON_PANNABLE_AREA_MODE_AUTO:
1815 delta = event->time - priv->last_time;
1817 if (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT) {
1818 gdouble dist = event->y - priv->y;
1820 hildon_pannable_area_calculate_velocity (&priv->vel_y,
1831 if (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ) {
1832 gdouble dist = event->x - priv->x;
1834 hildon_pannable_area_calculate_velocity (&priv->vel_x,
1844 hildon_pannable_area_scroll (area, x, y);
1846 if (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ)
1848 if (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT)
1859 /* Send motion notify to child */
1860 priv->last_time = event->time;
1861 priv->last_type = 2;
1862 event = (GdkEventMotion *) gdk_event_copy ((GdkEvent *) event);
1863 event->x = priv->cx + (event->x - priv->ix);
1864 event->y = priv->cy + (event->y - priv->iy);
1865 event->window = g_object_ref (priv->child);
1866 gdk_event_put ((GdkEvent *) event);
1867 gdk_event_free ((GdkEvent *) event);
1870 gdk_window_get_pointer (widget->window, NULL, NULL, 0);
1876 hildon_pannable_area_button_release_cb (GtkWidget * widget,
1877 GdkEventButton * event)
1879 HildonPannableAreaPrivate *priv = PANNABLE_AREA_PRIVATE (widget);
1883 if (gtk_bin_get_child (GTK_BIN (widget)) == NULL)
1886 priv->scroll_indicator_event_interrupt = 0;
1887 priv->scroll_delay_counter = SCROLLBAR_FADE_DELAY;
1889 if ((ABS (priv->vel_y) > 1.0)||
1890 (ABS (priv->vel_x) > 1.0)) {
1891 priv->scroll_indicator_alpha = 1.0;
1894 if (!priv->scroll_indicator_timeout) {
1895 priv->scroll_indicator_timeout = g_timeout_add
1896 ((gint) (1000.0 / (gdouble) priv->sps),
1897 (GSourceFunc) hildon_pannable_area_scroll_indicator_fade, widget);
1900 if ((!priv->clicked) || (!priv->enabled) || (event->button != 1) ||
1901 ((event->time == priv->last_time) && (priv->last_type == 3)))
1904 priv->clicked = FALSE;
1906 if (priv->mode == HILDON_PANNABLE_AREA_MODE_AUTO ||
1907 priv->mode == HILDON_PANNABLE_AREA_MODE_ACCEL) {
1909 /* If overshoot has been initiated with a finger down, on release set max speed */
1910 if (priv->overshot_dist_y != 0) {
1911 priv->overshooting_y = BOUNCE_STEPS; /* Hack to stop a bounce in the finger down case */
1912 priv->vel_y = priv->vmax;
1915 if (priv->overshot_dist_x != 0) {
1916 priv->overshooting_x = BOUNCE_STEPS; /* Hack to stop a bounce in the finger down case */
1917 priv->vel_x = priv->vmax;
1921 priv->idle_id = g_timeout_add ((gint) (1000.0 / (gdouble) priv->sps),
1923 hildon_pannable_area_timeout, widget);
1926 priv->last_time = event->time;
1927 priv->last_type = 3;
1930 priv->moved = FALSE;
1935 hildon_pannable_area_get_topmost (gtk_bin_get_child (GTK_BIN (widget))->window,
1936 event->x, event->y, &x, &y);
1938 event = (GdkEventButton *) gdk_event_copy ((GdkEvent *) event);
1942 /* Leave the widget if we've moved - This doesn't break selection,
1943 * but stops buttons from being clicked.
1945 if ((child != priv->child) || (priv->moved)) {
1946 /* Send synthetic leave event */
1947 synth_crossing (priv->child, x, y, event->x_root,
1948 event->y_root, event->time, FALSE);
1949 /* Send synthetic button release event */
1950 ((GdkEventAny *) event)->window = g_object_ref (priv->child);
1951 gdk_event_put ((GdkEvent *) event);
1953 /* Send synthetic button release event */
1954 ((GdkEventAny *) event)->window = g_object_ref (child);
1955 gdk_event_put ((GdkEvent *) event);
1956 /* Send synthetic leave event */
1957 synth_crossing (priv->child, x, y, event->x_root,
1958 event->y_root, event->time, FALSE);
1960 g_object_remove_weak_pointer ((GObject *) priv->child,
1961 (gpointer) & priv->child);
1963 priv->moved = FALSE;
1964 gdk_event_free ((GdkEvent *) event);
1969 /* utility event handler */
1971 hildon_pannable_area_scroll_cb (GtkWidget *widget,
1972 GdkEventScroll *event)
1974 GtkAdjustment *adj = NULL;
1975 HildonPannableAreaPrivate *priv = PANNABLE_AREA_PRIVATE (widget);
1977 if ((!priv->enabled) ||
1978 (gtk_bin_get_child (GTK_BIN (widget)) == NULL))
1981 priv->scroll_indicator_event_interrupt = 0;
1982 priv->scroll_indicator_alpha = 1.0;
1983 priv->scroll_delay_counter = SCROLLBAR_FADE_DELAY + 20;
1985 if (!priv->scroll_indicator_timeout) {
1986 priv->scroll_indicator_timeout = g_timeout_add
1987 ((gint) (1000.0 / (gdouble) (priv->sps*2)),
1988 (GSourceFunc) hildon_pannable_area_scroll_indicator_fade, widget);
1991 /* Stop inertial scrolling */
1992 if (priv->idle_id) {
1995 priv->overshooting_x = 0;
1996 priv->overshooting_y = 0;
1998 if ((priv->overshot_dist_x>0)||(priv->overshot_dist_y>0)) {
1999 priv->overshot_dist_x = 0;
2000 priv->overshot_dist_y = 0;
2002 gtk_widget_queue_resize (GTK_WIDGET (widget));
2005 g_source_remove (priv->idle_id);
2009 if (event->direction == GDK_SCROLL_UP || event->direction == GDK_SCROLL_DOWN)
2010 adj = priv->vadjust;
2012 adj = priv->hadjust;
2016 gdouble delta, new_value;
2018 /* from gtkrange.c calculate delta*/
2019 delta = pow (adj->page_size, 2.0 / 3.0);
2021 if (event->direction == GDK_SCROLL_UP ||
2022 event->direction == GDK_SCROLL_LEFT)
2025 new_value = CLAMP (adj->value + delta, adj->lower, adj->upper - adj->page_size);
2027 gtk_adjustment_set_value (adj, new_value);
2035 hildon_pannable_area_add (GtkContainer *container, GtkWidget *child)
2037 HildonPannableAreaPrivate *priv = PANNABLE_AREA_PRIVATE (container);
2039 g_return_if_fail (gtk_bin_get_child (GTK_BIN (container)) == NULL);
2041 gtk_widget_set_parent (child, GTK_WIDGET (container));
2042 GTK_BIN (container)->child = child;
2044 if (!gtk_widget_set_scroll_adjustments (child, priv->hadjust, priv->vadjust)) {
2045 g_warning ("%s: cannot add non scrollable widget, "
2046 "wrap it in a viewport", __FUNCTION__);
2051 hildon_pannable_area_remove (GtkContainer *container, GtkWidget *child)
2053 g_return_if_fail (HILDON_IS_PANNABLE_AREA (container));
2054 g_return_if_fail (child != NULL);
2055 g_return_if_fail (gtk_bin_get_child (GTK_BIN (container)) == child);
2057 gtk_widget_set_scroll_adjustments (child, NULL, NULL);
2059 /* chain parent class handler to remove child */
2060 GTK_CONTAINER_CLASS (hildon_pannable_area_parent_class)->remove (container, child);
2064 hildon_pannable_calculate_vel_factor (HildonPannableArea * self)
2066 HildonPannableAreaPrivate *priv = PANNABLE_AREA_PRIVATE (self);
2071 n = ceil (priv->sps * priv->scroll_time);
2073 for (i = 0; i < n && fct_i >= RATIO_TOLERANCE; i++) {
2074 fct_i *= priv->decel;
2078 priv->vel_factor = fct;
2082 * hildon_pannable_area_new:
2084 * Create a new pannable area widget
2086 * Returns: the newly created #HildonPannableArea
2090 hildon_pannable_area_new (void)
2092 return g_object_new (HILDON_TYPE_PANNABLE_AREA, NULL);
2096 * hildon_pannable_area_new_full:
2097 * @mode: #HildonPannableAreaMode
2098 * @enabled: Value for the enabled property
2099 * @vel_min: Value for the velocity-min property
2100 * @vel_max: Value for the velocity-max property
2101 * @decel: Value for the deceleration property
2102 * @sps: Value for the sps property
2104 * Create a new #HildonPannableArea widget and set various properties
2106 * returns: the newly create #HildonPannableArea
2110 hildon_pannable_area_new_full (gint mode, gboolean enabled,
2111 gdouble vel_min, gdouble vel_max,
2112 gdouble decel, guint sps)
2114 return g_object_new (HILDON_TYPE_PANNABLE_AREA,
2117 "velocity_min", vel_min,
2118 "velocity_max", vel_max,
2119 "deceleration", decel, "sps", sps, NULL);
2123 * hildon_pannable_area_add_with_viewport:
2124 * @area: A #HildonPannableArea
2125 * @child: Child widget to add to the viewport
2127 * Convenience function used to add a child to a #GtkViewport, and add the
2128 * viewport to the scrolled window.
2132 hildon_pannable_area_add_with_viewport (HildonPannableArea * area,
2135 GtkWidget *viewport = gtk_viewport_new (NULL, NULL);
2136 gtk_viewport_set_shadow_type (GTK_VIEWPORT (viewport), GTK_SHADOW_NONE);
2137 gtk_container_add (GTK_CONTAINER (viewport), child);
2138 gtk_widget_show (viewport);
2139 gtk_container_add (GTK_CONTAINER (area), viewport);
2143 * hildon_pannable_area_scroll_to:
2144 * @area: A #HildonPannableArea.
2145 * @x: The x coordinate of the destination point or -1 to ignore this axis.
2146 * @y: The y coordinate of the destination point or -1 to ignore this axis.
2148 * Smoothly scrolls @area to ensure that (@x, @y) is a visible point
2149 * on the widget. To move in only one coordinate, you must set the other one
2150 * to -1. Notice that, in %HILDON_PANNABLE_AREA_MODE_PUSH mode, this function
2151 * works just like hildon_pannable_area_jump_to().
2153 * This function is useful if you need to present the user with a particular
2154 * element inside a scrollable widget, like #GtkTreeView. For instance,
2155 * the following example shows how to scroll inside a #GtkTreeView to
2156 * make visible an item, indicated by the #GtkTreeIter @iter.
2160 * GtkTreePath *path;
2161 * GdkRectangle *rect;
2163 * path = gtk_tree_model_get_path (model, &iter);
2164 * gtk_tree_view_get_background_area (GTK_TREE_VIEW (treeview),
2165 * path, NULL, &rect);
2166 * gtk_tree_view_convert_bin_window_to_tree_coords (GTK_TREE_VIEW (treeview),
2167 * 0, rect.y, NULL, &y);
2168 * hildon_pannable_area_scroll_to (panarea, -1, y);
2169 * gtk_tree_path_free (path);
2173 * If you want to present a child widget in simpler scenarios,
2174 * use hildon_pannable_area_scroll_to_child() instead.
2178 hildon_pannable_area_scroll_to (HildonPannableArea *area,
2179 const gint x, const gint y)
2181 HildonPannableAreaPrivate *priv;
2183 gint dist_x, dist_y;
2185 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
2187 priv = PANNABLE_AREA_PRIVATE (area);
2189 if (priv->mode == HILDON_PANNABLE_AREA_MODE_PUSH)
2190 hildon_pannable_area_jump_to (area, x, y);
2192 g_return_if_fail (x >= -1 && y >= -1);
2194 if (x == -1 && y == -1) {
2198 width = priv->hadjust->upper - priv->hadjust->lower;
2199 height = priv->vadjust->upper - priv->vadjust->lower;
2201 g_return_if_fail (x < width || y < height);
2204 priv->scroll_to_x = x - priv->hadjust->page_size/2;
2205 dist_x = priv->scroll_to_x - priv->hadjust->value;
2207 priv->scroll_to_x = -1;
2209 priv->vel_x = - dist_x/priv->vel_factor;
2212 priv->scroll_to_x = -1;
2216 priv->scroll_to_y = y - priv->vadjust->page_size/2;
2217 dist_y = priv->scroll_to_y - priv->vadjust->value;
2219 priv->scroll_to_y = -1;
2221 priv->vel_y = - dist_y/priv->vel_factor;
2224 priv->scroll_to_y = y;
2227 if ((priv->scroll_to_y == -1) && (priv->scroll_to_y == -1)) {
2231 priv->scroll_indicator_alpha = 1.0;
2233 if (!priv->scroll_indicator_timeout)
2234 priv->scroll_indicator_timeout = g_timeout_add
2235 ((gint) (1000.0 / (gdouble) priv->sps),
2236 (GSourceFunc) hildon_pannable_area_scroll_indicator_fade, area);
2239 priv->idle_id = g_timeout_add ((gint) (1000.0 / (gdouble) priv->sps),
2241 hildon_pannable_area_timeout, area);
2245 * hildon_pannable_area_jump_to:
2246 * @area: A #HildonPannableArea.
2247 * @x: The x coordinate of the destination point or -1 to ignore this axis.
2248 * @y: The y coordinate of the destination point or -1 to ignore this axis.
2250 * Jumps the position of @area to ensure that (@x, @y) is a visible
2251 * point in the widget. In order to move in only one coordinate, you
2252 * must set the other one to -1. See hildon_pannable_area_scroll_to()
2253 * function for an example of how to calculate the position of
2254 * children in scrollable widgets like #GtkTreeview.
2258 hildon_pannable_area_jump_to (HildonPannableArea *area,
2259 const gint x, const gint y)
2261 HildonPannableAreaPrivate *priv;
2264 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
2265 g_return_if_fail (x >= -1 && y >= -1);
2267 if (x == -1 && y == -1) {
2271 priv = PANNABLE_AREA_PRIVATE (area);
2273 width = priv->hadjust->upper - priv->hadjust->lower;
2274 height = priv->vadjust->upper - priv->vadjust->lower;
2276 g_return_if_fail (x < width || y < height);
2279 gdouble jump_to = x - priv->hadjust->page_size/2;
2281 if (jump_to > priv->hadjust->upper - priv->hadjust->page_size) {
2282 jump_to = priv->hadjust->upper - priv->hadjust->page_size;
2285 gtk_adjustment_set_value (priv->hadjust, jump_to);
2289 gdouble jump_to = y - priv->vadjust->page_size/2;
2291 if (jump_to > priv->vadjust->upper - priv->vadjust->page_size) {
2292 jump_to = priv->vadjust->upper - priv->vadjust->page_size;
2295 gtk_adjustment_set_value (priv->vadjust, jump_to);
2298 priv->scroll_indicator_alpha = 1.0;
2300 if (priv->scroll_indicator_timeout) {
2301 g_source_remove (priv->scroll_indicator_timeout);
2302 priv->scroll_indicator_timeout = 0;
2305 if (priv->idle_id) {
2308 priv->overshooting_x = 0;
2309 priv->overshooting_y = 0;
2311 if ((priv->overshot_dist_x>0)||(priv->overshot_dist_y>0)) {
2312 priv->overshot_dist_x = 0;
2313 priv->overshot_dist_y = 0;
2315 gtk_widget_queue_resize (GTK_WIDGET (area));
2318 g_source_remove (priv->idle_id);
2324 * hildon_pannable_area_scroll_to_child:
2325 * @area: A #HildonPannableArea.
2326 * @child: A #GtkWidget, descendant of @area.
2328 * Smoothly scrolls until @child is visible inside @area. @child must
2329 * be a descendant of @area. If you need to scroll inside a scrollable
2330 * widget, e.g., #GtkTreeview, see hildon_pannable_area_scroll_to().
2334 hildon_pannable_area_scroll_to_child (HildonPannableArea *area, GtkWidget *child)
2336 GtkWidget *bin_child;
2339 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
2340 g_return_if_fail (GTK_IS_WIDGET (child));
2341 g_return_if_fail (gtk_widget_is_ancestor (child, GTK_WIDGET (area)));
2343 if (GTK_BIN (area)->child == NULL)
2346 /* We need to get to check the child of the inside the area */
2347 bin_child = GTK_BIN (area)->child;
2349 /* we check if we added a viewport */
2350 if (GTK_IS_VIEWPORT (bin_child)) {
2351 bin_child = GTK_BIN (bin_child)->child;
2354 if (gtk_widget_translate_coordinates (child, bin_child, 0, 0, &x, &y))
2355 hildon_pannable_area_scroll_to (area, x, y);
2359 * hildon_pannable_area_jump_to_child:
2360 * @area: A #HildonPannableArea.
2361 * @child: A #GtkWidget, descendant of @area.
2363 * Jumps to make sure @child is visible inside @area. @child must
2364 * be a descendant of @area. If you want to move inside a scrollable
2365 * widget, like, #GtkTreeview, see hildon_pannable_area_scroll_to().
2369 hildon_pannable_area_jump_to_child (HildonPannableArea *area, GtkWidget *child)
2371 GtkWidget *bin_child;
2374 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
2375 g_return_if_fail (GTK_IS_WIDGET (child));
2376 g_return_if_fail (gtk_widget_is_ancestor (child, GTK_WIDGET (area)));
2378 if (gtk_bin_get_child (GTK_BIN (area)) == NULL)
2381 /* We need to get to check the child of the inside the area */
2382 bin_child = gtk_bin_get_child (GTK_BIN (area));
2384 /* we check if we added a viewport */
2385 if (GTK_IS_VIEWPORT (bin_child)) {
2386 bin_child = gtk_bin_get_child (GTK_BIN (bin_child));
2389 if (gtk_widget_translate_coordinates (child, bin_child, 0, 0, &x, &y))
2390 hildon_pannable_area_jump_to (area, x, y);
2394 * hildon_pannable_get_child_widget_at:
2395 * @area: A #HildonPannableArea.
2396 * @x: horizontal coordinate of the point
2397 * @y: vertical coordinate of the point
2399 * Get the widget at the point (x, y) inside the pannable area. In
2400 * case no widget found it returns NULL.
2402 * returns: the #GtkWidget if we find a widget, NULL in any other case
2405 hildon_pannable_get_child_widget_at (HildonPannableArea *area,
2406 gdouble x, gdouble y)
2408 GdkWindow *window = NULL;
2409 GtkWidget *child_widget = NULL;
2411 window = hildon_pannable_area_get_topmost
2412 (gtk_bin_get_child (GTK_BIN (area))->window,
2415 gdk_window_get_user_data (window, (gpointer) &child_widget);
2417 return child_widget;