2 * This file is a part of hildon
4 * Copyright (C) 2008 Nokia Corporation, all rights reserved.
6 * Contact: Rodrigo Novo <rodrigo.novo@nokia.com>
8 * This widget is based on MokoFingerScroll from libmokoui
9 * OpenMoko Application Framework UI Library
10 * Authored by Chris Lord <chris@openedhand.com>
11 * Copyright (C) 2006-2007 OpenMoko Inc.
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU Lesser Public License as published by
15 * the Free Software Foundation; version 2 of the license.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU Lesser Public License for more details.
25 * SECTION: hildon-pannable-area
26 * @short_description: A scrolling widget designed for touch screens
27 * @see_also: #GtkScrolledWindow
29 * #HildonPannableArea 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 SCROLLBAR_FADE_DELAY 30
49 #define SCROLL_FADE_TIMEOUT 10
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 struct _HildonPannableAreaPrivate {
58 HildonPannableAreaMode mode;
59 HildonMovementMode mov_mode;
60 GdkWindow *event_window;
61 gdouble x; /* Used to store mouse co-ordinates of the first or */
62 gdouble y; /* previous events in a press-motion pair */
63 gdouble ex; /* Used to store mouse co-ordinates of the last */
64 gdouble ey; /* motion event in acceleration mode */
67 guint32 last_time; /* Last event time, to stop infinite loops */
80 gint ix; /* Initial click mouse co-ordinates */
82 gint cx; /* Initial click child window mouse co-ordinates */
91 gdouble scroll_indicator_alpha;
92 gint scroll_indicator_timeout;
93 gint scroll_indicator_event_interrupt;
94 gint scroll_delay_counter;
97 gboolean initial_hint;
98 gboolean initial_effect;
101 gboolean size_request_policy;
102 gboolean hscroll_visible;
103 gboolean vscroll_visible;
104 GdkRectangle hscroll_rect;
105 GdkRectangle vscroll_rect;
108 GtkAdjustment *hadjust;
109 GtkAdjustment *vadjust;
111 GtkPolicyType vscrollbar_policy;
112 GtkPolicyType hscrollbar_policy;
122 static guint pannable_area_signals [LAST_SIGNAL] = { 0 };
130 PROP_VELOCITY_FAST_FACTOR,
133 PROP_VSCROLLBAR_POLICY,
134 PROP_HSCROLLBAR_POLICY,
139 PROP_SIZE_REQUEST_POLICY,
145 static void hildon_pannable_area_class_init (HildonPannableAreaClass * klass);
146 static void hildon_pannable_area_init (HildonPannableArea * area);
147 static void hildon_pannable_area_get_property (GObject * object,
151 static void hildon_pannable_area_set_property (GObject * object,
153 const GValue * value,
155 static void hildon_pannable_area_dispose (GObject * object);
156 static void hildon_pannable_area_realize (GtkWidget * widget);
157 static void hildon_pannable_area_unrealize (GtkWidget * widget);
158 static void hildon_pannable_area_size_request (GtkWidget * widget,
159 GtkRequisition * requisition);
160 static void hildon_pannable_area_size_allocate (GtkWidget * widget,
161 GtkAllocation * allocation);
162 static void hildon_pannable_area_style_set (GtkWidget * widget,
163 GtkStyle * previous_style);
164 static void hildon_pannable_area_map (GtkWidget * widget);
165 static void hildon_pannable_area_unmap (GtkWidget * widget);
166 static void hildon_pannable_area_grab_notify (GtkWidget *widget,
167 gboolean was_grabbed,
169 static void rgb_from_gdkcolor (GdkColor *color, gdouble *r, gdouble *g, gdouble *b);
170 static void hildon_pannable_draw_vscroll (GtkWidget * widget,
171 GdkColor *back_color,
172 GdkColor *scroll_color);
173 static void hildon_pannable_draw_hscroll (GtkWidget * widget,
174 GdkColor *back_color,
175 GdkColor *scroll_color);
176 static void hildon_pannable_area_initial_effect (GtkWidget * widget);
177 static void hildon_pannable_area_redraw (HildonPannableArea * area);
178 static gboolean hildon_pannable_area_scroll_indicator_fade(HildonPannableArea * area);
179 static gboolean hildon_pannable_area_expose_event (GtkWidget * widget,
180 GdkEventExpose * event);
181 static GdkWindow * hildon_pannable_area_get_topmost (GdkWindow * window,
183 gint * tx, gint * ty);
184 static void synth_crossing (GdkWindow * child,
186 gint x_root, gint y_root,
187 guint32 time, gboolean in);
188 static gboolean hildon_pannable_area_button_press_cb (GtkWidget * widget,
189 GdkEventButton * event);
190 static void hildon_pannable_area_refresh (HildonPannableArea * area);
191 static void hildon_pannable_axis_scroll (HildonPannableArea *area,
192 GtkAdjustment *adjust,
200 static void hildon_pannable_area_scroll (HildonPannableArea *area,
201 gdouble x, gdouble y);
202 static gboolean hildon_pannable_area_timeout (HildonPannableArea * area);
203 static void hildon_pannable_area_calculate_velocity (gdouble *vel,
208 static gboolean hildon_pannable_area_motion_notify_cb (GtkWidget * widget,
209 GdkEventMotion * event);
210 static gboolean hildon_pannable_area_button_release_cb (GtkWidget * widget,
211 GdkEventButton * event);
212 static gboolean hildon_pannable_area_scroll_cb (GtkWidget *widget,
213 GdkEventScroll *event);
214 static void hildon_pannable_area_child_mapped (GtkWidget *widget,
217 static void hildon_pannable_area_add (GtkContainer *container, GtkWidget *child);
218 static void hildon_pannable_area_remove (GtkContainer *container, GtkWidget *child);
219 static void hildon_pannable_calculate_vel_factor (HildonPannableArea * self);
223 hildon_pannable_area_class_init (HildonPannableAreaClass * klass)
225 GObjectClass *object_class = G_OBJECT_CLASS (klass);
226 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
227 GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
230 g_type_class_add_private (klass, sizeof (HildonPannableAreaPrivate));
232 object_class->get_property = hildon_pannable_area_get_property;
233 object_class->set_property = hildon_pannable_area_set_property;
234 object_class->dispose = hildon_pannable_area_dispose;
236 widget_class->realize = hildon_pannable_area_realize;
237 widget_class->unrealize = hildon_pannable_area_unrealize;
238 widget_class->map = hildon_pannable_area_map;
239 widget_class->unmap = hildon_pannable_area_unmap;
240 widget_class->size_request = hildon_pannable_area_size_request;
241 widget_class->size_allocate = hildon_pannable_area_size_allocate;
242 widget_class->expose_event = hildon_pannable_area_expose_event;
243 widget_class->style_set = hildon_pannable_area_style_set;
244 widget_class->button_press_event = hildon_pannable_area_button_press_cb;
245 widget_class->button_release_event = hildon_pannable_area_button_release_cb;
246 widget_class->motion_notify_event = hildon_pannable_area_motion_notify_cb;
247 widget_class->scroll_event = hildon_pannable_area_scroll_cb;
249 container_class->add = hildon_pannable_area_add;
250 container_class->remove = hildon_pannable_area_remove;
252 klass->horizontal_movement = NULL;
253 klass->vertical_movement = NULL;
255 g_object_class_install_property (object_class,
257 g_param_spec_boolean ("enabled",
259 "Enable or disable finger-scroll.",
264 g_object_class_install_property (object_class,
265 PROP_VSCROLLBAR_POLICY,
266 g_param_spec_enum ("vscrollbar_policy",
268 "Visual policy of the vertical scrollbar",
269 GTK_TYPE_POLICY_TYPE,
270 GTK_POLICY_AUTOMATIC,
274 g_object_class_install_property (object_class,
275 PROP_HSCROLLBAR_POLICY,
276 g_param_spec_enum ("hscrollbar_policy",
278 "Visual policy of the horizontal scrollbar",
279 GTK_TYPE_POLICY_TYPE,
280 GTK_POLICY_AUTOMATIC,
284 g_object_class_install_property (object_class,
286 g_param_spec_enum ("mode",
288 "Change the finger-scrolling mode.",
289 HILDON_TYPE_PANNABLE_AREA_MODE,
290 HILDON_PANNABLE_AREA_MODE_AUTO,
294 g_object_class_install_property (object_class,
296 g_param_spec_flags ("mov_mode",
297 "Scroll movement mode",
298 "Controls if the widget can scroll vertically, horizontally or both",
299 HILDON_TYPE_MOVEMENT_MODE,
300 HILDON_MOVEMENT_MODE_VERT,
304 g_object_class_install_property (object_class,
306 g_param_spec_double ("velocity_min",
307 "Minimum scroll velocity",
308 "Minimum distance the child widget should scroll "
309 "per 'frame', in pixels.",
314 g_object_class_install_property (object_class,
316 g_param_spec_double ("velocity_max",
317 "Maximum scroll velocity",
318 "Maximum distance the child widget should scroll "
319 "per 'frame', in pixels.",
324 g_object_class_install_property (object_class,
325 PROP_VELOCITY_FAST_FACTOR,
326 g_param_spec_double ("velocity_fast_factor",
327 "Fast velocity factor",
328 "Minimum velocity that is considered 'fast': "
329 "children widgets won't receive button presses. "
330 "Expressed as a fraction of the maximum velocity.",
335 g_object_class_install_property (object_class,
337 g_param_spec_double ("deceleration",
338 "Deceleration multiplier",
339 "The multiplier used when decelerating when in "
340 "acceleration scrolling mode.",
345 g_object_class_install_property (object_class,
347 g_param_spec_uint ("sps",
348 "Scrolls per second",
349 "Amount of scroll events to generate per second.",
354 g_object_class_install_property (object_class,
356 g_param_spec_int ("vovershoot_max",
357 "Vertical overshoot distance",
358 "Space we allow the widget to pass over its vertical limits when hitting the edges, set 0 in order to deactivate overshooting.",
363 g_object_class_install_property (object_class,
365 g_param_spec_int ("hovershoot_max",
366 "Horizontal overshoot distance",
367 "Space we allow the widget to pass over its horizontal limits when hitting the edges, set 0 in order to deactivate overshooting.",
372 g_object_class_install_property (object_class,
374 g_param_spec_double ("scroll_time",
375 "Time to scroll to a position",
376 "The time to scroll to a position when calling the hildon_pannable_scroll_to function"
377 "acceleration scrolling mode.",
382 g_object_class_install_property (object_class,
384 g_param_spec_boolean ("initial-hint",
386 "Whether to hint the user about the pannability of the container.",
391 g_object_class_install_property (object_class,
392 PROP_SIZE_REQUEST_POLICY,
393 g_param_spec_enum ("size-request-policy",
394 "Size Requisition policy",
395 "Controls the size request policy of the widget",
396 HILDON_TYPE_SIZE_REQUEST_POLICY,
397 HILDON_SIZE_REQUEST_MINIMUM,
401 g_object_class_install_property (object_class,
403 g_param_spec_object ("hadjustment",
404 "Horizontal Adjustment",
405 "The GtkAdjustment for the horizontal position",
408 g_object_class_install_property (object_class,
410 g_param_spec_object ("vadjustment",
411 "Vertical Adjustment",
412 "The GtkAdjustment for the vertical position",
416 gtk_widget_class_install_style_property (widget_class,
419 "Width of the scroll indicators",
420 "Pixel width used to draw the scroll indicators.",
424 pannable_area_signals[HORIZONTAL_MOVEMENT] =
425 g_signal_new ("horizontal_movement",
426 G_TYPE_FROM_CLASS (object_class),
427 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
428 G_STRUCT_OFFSET (HildonPannableAreaClass, horizontal_movement),
430 _hildon_marshal_VOID__INT_DOUBLE_DOUBLE,
436 pannable_area_signals[VERTICAL_MOVEMENT] =
437 g_signal_new ("vertical_movement",
438 G_TYPE_FROM_CLASS (object_class),
439 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
440 G_STRUCT_OFFSET (HildonPannableAreaClass, vertical_movement),
442 _hildon_marshal_VOID__INT_DOUBLE_DOUBLE,
452 hildon_pannable_area_init (HildonPannableArea * area)
454 HildonPannableAreaPrivate *priv = PANNABLE_AREA_PRIVATE (area);
459 priv->clicked = FALSE;
462 priv->vscroll_visible = TRUE;
463 priv->hscroll_visible = TRUE;
464 priv->area_width = 6;
465 priv->overshot_dist_x = 0;
466 priv->overshot_dist_y = 0;
467 priv->overshooting_y = 0;
468 priv->overshooting_x = 0;
472 priv->scroll_indicator_alpha = 0.0;
473 priv->scroll_indicator_timeout = 0;
474 priv->scroll_indicator_event_interrupt = 0;
475 priv->scroll_delay_counter = SCROLLBAR_FADE_DELAY;
476 priv->scroll_to_x = -1;
477 priv->scroll_to_y = -1;
478 priv->first_drag = TRUE;
479 priv->initial_effect = TRUE;
481 hildon_pannable_calculate_vel_factor (area);
483 gtk_widget_add_events (GTK_WIDGET (area), GDK_POINTER_MOTION_HINT_MASK);
486 GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
488 GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
490 g_object_ref_sink (G_OBJECT (priv->hadjust));
491 g_object_ref_sink (G_OBJECT (priv->vadjust));
493 g_signal_connect_swapped (G_OBJECT (priv->hadjust), "value-changed",
494 G_CALLBACK (hildon_pannable_area_redraw), area);
495 g_signal_connect_swapped (G_OBJECT (priv->vadjust), "value-changed",
496 G_CALLBACK (hildon_pannable_area_redraw), area);
497 g_signal_connect (G_OBJECT (area), "grab-notify",
498 G_CALLBACK (hildon_pannable_area_grab_notify), NULL);
502 hildon_pannable_area_get_property (GObject * object,
507 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (object)->priv;
509 switch (property_id) {
511 g_value_set_boolean (value, priv->enabled);
514 g_value_set_enum (value, priv->mode);
516 case PROP_MOVEMENT_MODE:
517 g_value_set_flags (value, priv->mov_mode);
519 case PROP_VELOCITY_MIN:
520 g_value_set_double (value, priv->vmin);
522 case PROP_VELOCITY_MAX:
523 g_value_set_double (value, priv->vmax);
525 case PROP_VELOCITY_FAST_FACTOR:
526 g_value_set_double (value, priv->vfast_factor);
528 case PROP_DECELERATION:
529 g_value_set_double (value, priv->decel);
532 g_value_set_uint (value, priv->sps);
534 case PROP_VSCROLLBAR_POLICY:
535 g_value_set_enum (value, priv->vscrollbar_policy);
537 case PROP_HSCROLLBAR_POLICY:
538 g_value_set_enum (value, priv->hscrollbar_policy);
540 case PROP_VOVERSHOOT_MAX:
541 g_value_set_int (value, priv->vovershoot_max);
543 case PROP_HOVERSHOOT_MAX:
544 g_value_set_int (value, priv->hovershoot_max);
546 case PROP_SCROLL_TIME:
547 g_value_set_double (value, priv->scroll_time);
549 case PROP_INITIAL_HINT:
550 g_value_set_boolean (value, priv->initial_hint);
552 case PROP_SIZE_REQUEST_POLICY:
553 g_value_set_enum (value, priv->size_request_policy);
555 case PROP_HADJUSTMENT:
556 g_value_set_object (value,
557 hildon_pannable_area_get_hadjustment
558 (HILDON_PANNABLE_AREA (object)));
560 case PROP_VADJUSTMENT:
561 g_value_set_object (value,
562 hildon_pannable_area_get_vadjustment
563 (HILDON_PANNABLE_AREA (object)));
566 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
571 hildon_pannable_area_set_property (GObject * object,
573 const GValue * value,
576 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (object)->priv;
579 switch (property_id) {
581 enabled = g_value_get_boolean (value);
583 if ((priv->enabled != enabled) && (GTK_WIDGET_REALIZED (object))) {
585 gdk_window_raise (priv->event_window);
587 gdk_window_lower (priv->event_window);
590 priv->enabled = enabled;
593 priv->mode = g_value_get_enum (value);
595 case PROP_MOVEMENT_MODE:
596 priv->mov_mode = g_value_get_flags (value);
598 case PROP_VELOCITY_MIN:
599 priv->vmin = g_value_get_double (value);
601 case PROP_VELOCITY_MAX:
602 priv->vmax = g_value_get_double (value);
604 case PROP_VELOCITY_FAST_FACTOR:
605 priv->vfast_factor = g_value_get_double (value);
607 case PROP_DECELERATION:
608 hildon_pannable_calculate_vel_factor (HILDON_PANNABLE_AREA (object));
610 priv->decel = g_value_get_double (value);
613 priv->sps = g_value_get_uint (value);
615 case PROP_VSCROLLBAR_POLICY:
616 priv->vscrollbar_policy = g_value_get_enum (value);
618 gtk_widget_queue_resize (GTK_WIDGET (object));
620 case PROP_HSCROLLBAR_POLICY:
621 priv->hscrollbar_policy = g_value_get_enum (value);
623 gtk_widget_queue_resize (GTK_WIDGET (object));
625 case PROP_VOVERSHOOT_MAX:
626 priv->vovershoot_max = g_value_get_int (value);
628 case PROP_HOVERSHOOT_MAX:
629 priv->hovershoot_max = g_value_get_int (value);
631 case PROP_SCROLL_TIME:
632 priv->scroll_time = g_value_get_double (value);
634 hildon_pannable_calculate_vel_factor (HILDON_PANNABLE_AREA (object));
636 case PROP_INITIAL_HINT:
637 priv->initial_hint = g_value_get_boolean (value);
639 case PROP_SIZE_REQUEST_POLICY:
640 hildon_pannable_area_set_size_request_policy (HILDON_PANNABLE_AREA (object),
641 g_value_get_enum (value));
645 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
650 hildon_pannable_area_dispose (GObject * object)
652 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (object)->priv;
653 GtkWidget *child = gtk_bin_get_child (GTK_BIN (object));
656 g_source_remove (priv->idle_id);
660 if (priv->scroll_indicator_timeout){
661 g_source_remove (priv->scroll_indicator_timeout);
662 priv->scroll_indicator_timeout = 0;
666 g_object_unref (priv->hadjust);
667 priv->hadjust = NULL;
670 g_object_unref (priv->vadjust);
671 priv->vadjust = NULL;
675 g_signal_handlers_disconnect_by_func (GTK_WIDGET (child),
676 G_CALLBACK (hildon_pannable_area_child_mapped),
680 if (G_OBJECT_CLASS (hildon_pannable_area_parent_class)->dispose)
681 G_OBJECT_CLASS (hildon_pannable_area_parent_class)->dispose (object);
685 hildon_pannable_area_realize (GtkWidget * widget)
687 GdkWindowAttr attributes;
688 gint attributes_mask;
690 HildonPannableAreaPrivate *priv;
692 priv = HILDON_PANNABLE_AREA (widget)->priv;
694 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
696 border_width = GTK_CONTAINER (widget)->border_width;
698 attributes.x = widget->allocation.x + border_width;
699 attributes.y = widget->allocation.y + border_width;
700 attributes.width = MAX (widget->allocation.width - 2 * border_width -
701 (priv->vscroll_visible ? priv->vscroll_rect.width : 0), 0);
702 attributes.height = MAX (widget->allocation.height - 2 * border_width -
703 (priv->hscroll_visible ? priv->hscroll_rect.height : 0), 0);
704 attributes.window_type = GDK_WINDOW_CHILD;
705 attributes.event_mask = gtk_widget_get_events (widget)
706 | GDK_BUTTON_MOTION_MASK
707 | GDK_BUTTON_PRESS_MASK
708 | GDK_BUTTON_RELEASE_MASK
710 | GDK_EXPOSURE_MASK | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK;
712 widget->window = gtk_widget_get_parent_window (widget);
713 g_object_ref (widget->window);
715 attributes.wclass = GDK_INPUT_ONLY;
716 attributes_mask = GDK_WA_X | GDK_WA_Y;
718 priv->event_window = gdk_window_new (widget->window,
719 &attributes, attributes_mask);
720 gdk_window_set_user_data (priv->event_window, widget);
722 widget->style = gtk_style_attach (widget->style, widget->window);
726 hildon_pannable_area_unrealize (GtkWidget * widget)
728 HildonPannableAreaPrivate *priv;
730 priv = HILDON_PANNABLE_AREA (widget)->priv;
732 if (priv->event_window != NULL) {
733 gdk_window_set_user_data (priv->event_window, NULL);
734 gdk_window_destroy (priv->event_window);
735 priv->event_window = NULL;
738 if (GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->unrealize)
739 (*GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->unrealize)(widget);
743 hildon_pannable_area_size_request (GtkWidget * widget,
744 GtkRequisition * requisition)
746 GtkRequisition child_requisition;
747 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
748 GtkWidget *child = gtk_bin_get_child (GTK_BIN (widget));
750 if (child && GTK_WIDGET_VISIBLE (child))
752 gtk_widget_size_request (child, &child_requisition);
755 if (priv->hscrollbar_policy == GTK_POLICY_NEVER) {
756 requisition->width = child_requisition.width;
758 switch (priv->size_request_policy) {
759 case HILDON_SIZE_REQUEST_CHILDREN:
760 requisition->width = child_requisition.width;
762 case HILDON_SIZE_REQUEST_MINIMUM:
764 requisition->width = priv->area_width;
768 if (priv->vscrollbar_policy == GTK_POLICY_NEVER) {
769 requisition->height = child_requisition.height;
771 switch (priv->size_request_policy) {
772 case HILDON_SIZE_REQUEST_CHILDREN:
773 requisition->height = child_requisition.height;
775 case HILDON_SIZE_REQUEST_MINIMUM:
777 requisition->height = priv->area_width;
781 requisition->width += 2 * GTK_CONTAINER (widget)->border_width;
782 requisition->height += 2 * GTK_CONTAINER (widget)->border_width;
786 hildon_pannable_area_size_allocate (GtkWidget * widget,
787 GtkAllocation * allocation)
789 GtkAllocation child_allocation;
790 HildonPannableAreaPrivate *priv;
791 GtkWidget *child = gtk_bin_get_child (GTK_BIN (widget));
793 widget->allocation = *allocation;
795 priv = HILDON_PANNABLE_AREA (widget)->priv;
797 child_allocation.x = allocation->x + GTK_CONTAINER (widget)->border_width;
798 child_allocation.y = allocation->y + GTK_CONTAINER (widget)->border_width;
799 child_allocation.width = MAX (allocation->width -
800 2 * GTK_CONTAINER (widget)->border_width -
801 (priv->vscroll_visible ? priv->vscroll_rect.width : 0), 0);
802 child_allocation.height = MAX (allocation->height -
803 2 * GTK_CONTAINER (widget)->border_width -
804 (priv->hscroll_visible ? priv->hscroll_rect.height : 0), 0);
806 if (GTK_WIDGET_REALIZED (widget)) {
807 if (priv->event_window != NULL)
808 gdk_window_move_resize (priv->event_window,
811 child_allocation.width,
812 child_allocation.height);
815 if (priv->overshot_dist_y > 0) {
816 child_allocation.y = MIN (child_allocation.y + priv->overshot_dist_y,
817 allocation->y + child_allocation.height);
818 child_allocation.height = MAX (child_allocation.height - priv->overshot_dist_y, 0);
819 } else if (priv->overshot_dist_y < 0) {
820 child_allocation.height = MAX (child_allocation.height + priv->overshot_dist_y, 0);
823 if (priv->overshot_dist_x > 0) {
824 child_allocation.x = MIN (child_allocation.x + priv->overshot_dist_x,
825 allocation->x + child_allocation.width);
826 child_allocation.width = MAX (child_allocation.width - priv->overshot_dist_x, 0);
827 } else if (priv->overshot_dist_x < 0) {
828 child_allocation.width = MAX (child_allocation.width + priv->overshot_dist_x, 0);
832 gtk_widget_size_allocate (child, &child_allocation);
834 /* we have to do this after child size_allocate because page_size is
835 * changed when we allocate the size of the children */
836 if (priv->overshot_dist_y < 0) {
837 gtk_adjustment_set_value (priv->vadjust, priv->vadjust->upper -
838 priv->vadjust->page_size);
841 if (priv->overshot_dist_x < 0) {
842 gtk_adjustment_set_value (priv->hadjust, priv->hadjust->upper -
843 priv->hadjust->page_size);
846 hildon_pannable_area_refresh (HILDON_PANNABLE_AREA (widget));
850 hildon_pannable_area_style_set (GtkWidget * widget,
851 GtkStyle * previous_style)
853 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
855 GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->
856 style_set (widget, previous_style);
858 gtk_widget_style_get (widget, "indicator-width", &priv->area_width, NULL);
862 hildon_pannable_area_map (GtkWidget * widget)
864 HildonPannableAreaPrivate *priv;
866 priv = HILDON_PANNABLE_AREA (widget)->priv;
868 if (priv->event_window != NULL && !priv->enabled)
869 gdk_window_show (priv->event_window);
871 (*GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->map) (widget);
873 if (priv->event_window != NULL && priv->enabled)
874 gdk_window_show (priv->event_window);
878 hildon_pannable_area_unmap (GtkWidget * widget)
880 HildonPannableAreaPrivate *priv;
882 priv = HILDON_PANNABLE_AREA (widget)->priv;
884 if (priv->event_window != NULL)
885 gdk_window_hide (priv->event_window);
887 (*GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->unmap) (widget);
891 hildon_pannable_area_grab_notify (GtkWidget *widget,
892 gboolean was_grabbed,
895 /* an internal widget has grabbed the focus and now has returned it,
896 we have to do some release actions */
898 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
900 priv->scroll_indicator_event_interrupt = 0;
901 priv->scroll_delay_counter = SCROLLBAR_FADE_DELAY;
903 if (!priv->scroll_indicator_timeout) {
904 priv->scroll_indicator_timeout = gdk_threads_add_timeout
905 ((gint) (1000.0 / (gdouble) SCROLL_FADE_TIMEOUT),
906 (GSourceFunc) hildon_pannable_area_scroll_indicator_fade, widget);
915 rgb_from_gdkcolor (GdkColor *color, gdouble *r, gdouble *g, gdouble *b)
917 *r = (color->red >> 8) / 255.0;
918 *g = (color->green >> 8) / 255.0;
919 *b = (color->blue >> 8) / 255.0;
923 hildon_pannable_draw_vscroll (GtkWidget * widget,
924 GdkColor *back_color,
925 GdkColor *scroll_color)
927 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
930 cairo_pattern_t *pattern;
932 gint radius = (priv->vscroll_rect.width/2) - 1;
934 cr = gdk_cairo_create(widget->window);
936 /* Draw the background */
937 rgb_from_gdkcolor (back_color, &r, &g, &b);
938 cairo_set_source_rgb (cr, r, g, b);
939 cairo_rectangle(cr, priv->vscroll_rect.x, priv->vscroll_rect.y,
940 priv->vscroll_rect.width,
941 priv->vscroll_rect.height);
942 cairo_fill_preserve (cr);
945 /* Calculate the scroll bar height and position */
946 y = widget->allocation.y +
947 ((priv->vadjust->value / priv->vadjust->upper) *
948 (widget->allocation.height -
949 (priv->hscroll_visible ? priv->area_width : 0)));
950 height = (widget->allocation.y +
951 (((priv->vadjust->value +
952 priv->vadjust->page_size) /
953 priv->vadjust->upper) *
954 (widget->allocation.height -
955 (priv->hscroll_visible ? priv->area_width : 0)))) - y;
957 /* Set a minimum height */
958 height = MAX (SCROLL_BAR_MIN_SIZE, height);
960 /* Check the max y position */
961 y = MIN (y, widget->allocation.y + widget->allocation.height -
962 (priv->hscroll_visible ? priv->hscroll_rect.height : 0) -
965 /* Draw the scrollbar */
966 rgb_from_gdkcolor (scroll_color, &r, &g, &b);
968 pattern = cairo_pattern_create_linear(radius+1, y, radius+1,y + height);
969 cairo_pattern_add_color_stop_rgb(pattern, 0, r, g, b);
970 cairo_pattern_add_color_stop_rgb(pattern, 1, r/2, g/2, b/2);
971 cairo_set_source(cr, pattern);
973 cairo_pattern_destroy(pattern);
975 cairo_arc(cr, priv->vscroll_rect.x + radius + 1, y + radius + 1, radius, G_PI, 0);
976 cairo_line_to(cr, priv->vscroll_rect.x + (radius * 2) + 1, y + height - radius);
977 cairo_arc(cr, priv->vscroll_rect.x + radius + 1, y + height - radius, radius, 0, G_PI);
978 cairo_line_to(cr, priv->vscroll_rect.x + 1, y + height - radius);
981 cairo_paint_with_alpha(cr, priv->scroll_indicator_alpha);
987 hildon_pannable_draw_hscroll (GtkWidget * widget,
988 GdkColor *back_color,
989 GdkColor *scroll_color)
991 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
994 cairo_pattern_t *pattern;
996 gint radius = (priv->hscroll_rect.height/2) - 1;
998 cr = gdk_cairo_create(widget->window);
1000 /* Draw the background */
1001 rgb_from_gdkcolor (back_color, &r, &g, &b);
1002 cairo_set_source_rgb (cr, r, g, b);
1003 cairo_rectangle(cr, priv->hscroll_rect.x, priv->hscroll_rect.y,
1004 priv->hscroll_rect.width,
1005 priv->hscroll_rect.height);
1006 cairo_fill_preserve (cr);
1009 /* calculate the scrollbar width and position */
1010 x = widget->allocation.x +
1011 ((priv->hadjust->value / priv->hadjust->upper) *
1012 (widget->allocation.width - (priv->vscroll_visible ? priv->area_width : 0)));
1014 (widget->allocation.x +
1015 (((priv->hadjust->value +
1016 priv->hadjust->page_size) / priv->hadjust->upper) *
1017 (widget->allocation.width -
1018 (priv->vscroll_visible ? priv->area_width : 0)))) - x;
1020 /* Set a minimum width */
1021 width = MAX (SCROLL_BAR_MIN_SIZE, width);
1023 /* Check the max x position */
1024 x = MIN (x, widget->allocation.x + widget->allocation.width -
1025 (priv->vscroll_visible ? priv->vscroll_rect.width : 0) -
1028 /* Draw the scrollbar */
1029 rgb_from_gdkcolor (scroll_color, &r, &g, &b);
1031 pattern = cairo_pattern_create_linear(x, radius+1, x+width, radius+1);
1032 cairo_pattern_add_color_stop_rgb(pattern, 0, r, g, b);
1033 cairo_pattern_add_color_stop_rgb(pattern, 1, r/2, g/2, b/2);
1034 cairo_set_source(cr, pattern);
1036 cairo_pattern_destroy(pattern);
1038 cairo_arc_negative(cr, x + radius + 1, priv->hscroll_rect.y + radius + 1, radius, 3*G_PI_2, G_PI_2);
1039 cairo_line_to(cr, x + width - radius, priv->hscroll_rect.y + (radius * 2) + 1);
1040 cairo_arc_negative(cr, x + width - radius, priv->hscroll_rect.y + radius + 1, radius, G_PI_2, 3*G_PI_2);
1041 cairo_line_to(cr, x + width - radius, priv->hscroll_rect.y + 1);
1044 cairo_paint_with_alpha(cr, priv->scroll_indicator_alpha);
1050 hildon_pannable_area_initial_effect (GtkWidget * widget)
1052 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1053 gboolean hscroll_visible, vscroll_visible;
1055 if (priv->initial_hint) {
1056 if (((priv->vovershoot_max != 0)||(priv->hovershoot_max != 0)) &&
1057 ((priv->mode == HILDON_PANNABLE_AREA_MODE_AUTO) ||
1058 (priv->mode == HILDON_PANNABLE_AREA_MODE_ACCEL))) {
1059 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
1060 priv->vadjust->page_size);
1061 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
1062 priv->hadjust->page_size);
1063 /* If scrolling is possible in both axes, only hint about scrolling in
1064 the vertical one. */
1065 if ((vscroll_visible)&&(priv->vovershoot_max != 0)) {
1066 priv->overshot_dist_y = priv->vovershoot_max;
1067 priv->vel_y = priv->vmax * 0.1;
1068 } else if ((hscroll_visible)&&(priv->hovershoot_max != 0)) {
1069 priv->overshot_dist_x = priv->hovershoot_max;
1070 priv->vel_x = priv->vmax * 0.1;
1073 if (vscroll_visible || hscroll_visible) {
1075 priv->idle_id = gdk_threads_add_timeout ((gint) (1000.0 / (gdouble) priv->sps),
1077 hildon_pannable_area_timeout, widget);
1081 if (priv->vscroll_visible || priv->hscroll_visible) {
1082 priv->scroll_indicator_alpha = 1.0;
1084 if (!priv->scroll_indicator_timeout)
1085 priv->scroll_indicator_timeout =
1086 gdk_threads_add_timeout ((gint) (1000.0 / (gdouble) SCROLL_FADE_TIMEOUT),
1087 (GSourceFunc) hildon_pannable_area_scroll_indicator_fade,
1094 hildon_pannable_area_redraw (HildonPannableArea * area)
1096 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (area)->priv;
1098 hildon_pannable_area_refresh (area);
1100 /* Redraw scroll indicators */
1101 if (GTK_WIDGET_DRAWABLE (area)) {
1102 if (priv->hscroll_visible) {
1103 gdk_window_invalidate_rect (GTK_WIDGET (area)->window,
1104 &priv->hscroll_rect, FALSE);
1107 if (priv->vscroll_visible) {
1108 gdk_window_invalidate_rect (GTK_WIDGET (area)->window,
1109 &priv->vscroll_rect, FALSE);
1115 hildon_pannable_area_scroll_indicator_fade(HildonPannableArea * area)
1117 HildonPannableAreaPrivate *priv = area->priv;
1119 /* if moving do not fade out */
1120 if (((ABS (priv->vel_y)>1.0)||
1121 (ABS (priv->vel_x)>1.0))&&(!priv->clicked)) {
1126 if (priv->scroll_indicator_event_interrupt) {
1127 /* Stop a fade out, and fade back in */
1128 if (priv->scroll_indicator_alpha > 0.9) {
1129 priv->scroll_indicator_alpha = 1.0;
1130 priv->scroll_indicator_timeout = 0;
1134 priv->scroll_indicator_alpha += 0.2;
1135 hildon_pannable_area_redraw (area);
1141 if ((priv->scroll_indicator_alpha > 0.9) &&
1142 (priv->scroll_delay_counter > 0)) {
1143 priv->scroll_delay_counter--;
1148 if (!priv->scroll_indicator_event_interrupt) {
1149 /* Continue fade out */
1150 if (priv->scroll_indicator_alpha < 0.1) {
1151 priv->scroll_indicator_timeout = 0;
1152 priv->scroll_indicator_alpha = 0.0;
1156 priv->scroll_indicator_alpha -= 0.2;
1157 hildon_pannable_area_redraw (area);
1167 hildon_pannable_area_expose_event (GtkWidget * widget,
1168 GdkEventExpose * event)
1171 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1172 GdkColor back_color = widget->style->bg[GTK_STATE_NORMAL];
1173 GdkColor scroll_color = widget->style->base[GTK_STATE_SELECTED];
1175 if (gtk_bin_get_child (GTK_BIN (widget))) {
1177 if (priv->scroll_indicator_alpha > 0.1) {
1178 if (priv->vscroll_visible) {
1179 hildon_pannable_draw_vscroll (widget, &back_color, &scroll_color);
1181 if (priv->hscroll_visible) {
1182 hildon_pannable_draw_hscroll (widget, &back_color, &scroll_color);
1186 /* draw overshooting rectangles */
1187 if (priv->overshot_dist_y > 0) {
1188 gint overshot_height;
1190 overshot_height = MIN (priv->overshot_dist_y, widget->allocation.height -
1191 (priv->hscroll_visible ? priv->hscroll_rect.height : 0));
1193 gdk_draw_rectangle (widget->window,
1194 widget->style->bg_gc[GTK_STATE_NORMAL],
1196 widget->allocation.x,
1197 widget->allocation.y,
1198 widget->allocation.width -
1199 (priv->vscroll_visible ? priv->vscroll_rect.width : 0),
1201 } else if (priv->overshot_dist_y < 0) {
1202 gint overshot_height;
1206 MAX (priv->overshot_dist_y,
1207 -(widget->allocation.height -
1208 (priv->hscroll_visible ? priv->hscroll_rect.height : 0)));
1210 overshot_y = MAX (widget->allocation.y +
1211 widget->allocation.height +
1213 (priv->hscroll_visible ? priv->hscroll_rect.height : 0), 0);
1215 gdk_draw_rectangle (widget->window,
1216 widget->style->bg_gc[GTK_STATE_NORMAL],
1218 widget->allocation.x,
1220 widget->allocation.width -
1221 priv->vscroll_rect.width,
1225 if (priv->overshot_dist_x > 0) {
1226 gint overshot_width;
1228 overshot_width = MIN (priv->overshot_dist_x, widget->allocation.width -
1229 (priv->vscroll_visible ? priv->vscroll_rect.width : 0));
1231 gdk_draw_rectangle (widget->window,
1232 widget->style->bg_gc[GTK_STATE_NORMAL],
1234 widget->allocation.x,
1235 widget->allocation.y,
1237 widget->allocation.height -
1238 (priv->hscroll_visible ? priv->hscroll_rect.height : 0));
1239 } else if (priv->overshot_dist_x < 0) {
1240 gint overshot_width;
1244 MAX (priv->overshot_dist_x,
1245 -(widget->allocation.width -
1246 (priv->vscroll_visible ? priv->vscroll_rect.width : 0)));
1248 overshot_x = MAX (widget->allocation.x +
1249 widget->allocation.width +
1251 (priv->vscroll_visible ? priv->vscroll_rect.width : 0), 0);
1253 gdk_draw_rectangle (widget->window,
1254 widget->style->bg_gc[GTK_STATE_NORMAL],
1257 widget->allocation.y,
1259 widget->allocation.height -
1260 priv->hscroll_rect.height);
1265 if (G_UNLIKELY (priv->initial_effect)) {
1267 hildon_pannable_area_initial_effect (widget);
1269 priv->initial_effect = FALSE;
1272 return GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->expose_event (widget, event);
1276 hildon_pannable_area_get_topmost (GdkWindow * window,
1278 gint * tx, gint * ty)
1280 /* Find the GdkWindow at the given point, by recursing from a given
1281 * parent GdkWindow. Optionally return the co-ordinates transformed
1282 * relative to the child window.
1286 gdk_drawable_get_size (GDK_DRAWABLE (window), &width, &height);
1287 if ((x < 0) || (x >= width) || (y < 0) || (y >= height))
1291 gint child_x = 0, child_y = 0;
1292 GList *c, *children = gdk_window_peek_children (window);
1293 GdkWindow *old_window = window;
1295 for (c = children; c; c = c->next) {
1296 GdkWindow *child = (GdkWindow *) c->data;
1299 gdk_drawable_get_size (GDK_DRAWABLE (child), &width, &height);
1300 gdk_window_get_position (child, &wx, &wy);
1302 if (((x >= wx) && (x < (wx + width)) && (y >= wy)
1303 && (y < (wy + height))) && (gdk_window_is_visible (child))) {
1310 if (window == old_window)
1326 synth_crossing (GdkWindow * child,
1328 gint x_root, gint y_root,
1329 guint32 time, gboolean in)
1331 GdkEventCrossing *crossing_event;
1332 GdkEventType type = in ? GDK_ENTER_NOTIFY : GDK_LEAVE_NOTIFY;
1334 /* Send synthetic enter event */
1335 crossing_event = (GdkEventCrossing *) gdk_event_new (type);
1336 ((GdkEventAny *) crossing_event)->type = type;
1337 ((GdkEventAny *) crossing_event)->window = g_object_ref (child);
1338 ((GdkEventAny *) crossing_event)->send_event = FALSE;
1339 crossing_event->subwindow = g_object_ref (child);
1340 crossing_event->time = time;
1341 crossing_event->x = x;
1342 crossing_event->y = y;
1343 crossing_event->x_root = x_root;
1344 crossing_event->y_root = y_root;
1345 crossing_event->mode = GDK_CROSSING_NORMAL;
1346 crossing_event->detail = GDK_NOTIFY_UNKNOWN;
1347 crossing_event->focus = FALSE;
1348 crossing_event->state = 0;
1349 gdk_event_put ((GdkEvent *) crossing_event);
1350 gdk_event_free ((GdkEvent *) crossing_event);
1354 hildon_pannable_area_button_press_cb (GtkWidget * widget,
1355 GdkEventButton * event)
1358 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1360 if ((!priv->enabled) || (event->button != 1) ||
1361 ((event->time == priv->last_time) &&
1362 (priv->last_type == 1)) || (gtk_bin_get_child (GTK_BIN (widget)) == NULL))
1365 priv->scroll_indicator_event_interrupt = 1;
1367 if (!priv->scroll_indicator_timeout){
1368 priv->scroll_indicator_timeout = gdk_threads_add_timeout
1369 ((gint) (1000.0 / (gdouble) (SCROLL_FADE_TIMEOUT)),
1370 (GSourceFunc) hildon_pannable_area_scroll_indicator_fade, widget);
1373 priv->last_time = event->time;
1374 priv->last_type = 1;
1376 priv->scroll_to_x = -1;
1377 priv->scroll_to_y = -1;
1379 if (priv->clicked && priv->child) {
1380 /* Widget stole focus on last click, send crossing-out event */
1381 synth_crossing (priv->child, 0, 0, event->x_root, event->y_root,
1382 event->time, FALSE);
1390 /* Don't allow a click if we're still moving fast */
1391 if ((ABS (priv->vel_x) <= (priv->vmax * priv->vfast_factor)) &&
1392 (ABS (priv->vel_y) <= (priv->vmax * priv->vfast_factor)))
1394 hildon_pannable_area_get_topmost (gtk_bin_get_child (GTK_BIN (widget))->window,
1395 event->x, event->y, &x, &y);
1399 priv->clicked = TRUE;
1401 /* Stop scrolling on mouse-down (so you can flick, then hold to stop) */
1407 g_object_add_weak_pointer ((GObject *) priv->child,
1408 (gpointer) & priv->child);
1410 event = (GdkEventButton *) gdk_event_copy ((GdkEvent *) event);
1416 synth_crossing (priv->child, x, y, event->x_root,
1417 event->y_root, event->time, TRUE);
1419 /* Send synthetic click (button press/release) event */
1420 ((GdkEventAny *) event)->window = g_object_ref (priv->child);
1422 gdk_event_put ((GdkEvent *) event);
1423 gdk_event_free ((GdkEvent *) event);
1431 hildon_pannable_area_refresh (HildonPannableArea * area)
1433 HildonPannableAreaPrivate *priv = area->priv;
1434 gboolean prev_hscroll_visible, prev_vscroll_visible;
1436 if (!gtk_bin_get_child (GTK_BIN (area))) {
1437 priv->vscroll_visible = FALSE;
1438 priv->hscroll_visible = FALSE;
1442 prev_hscroll_visible = priv->hscroll_visible;
1443 prev_vscroll_visible = priv->vscroll_visible;
1445 switch (priv->hscrollbar_policy) {
1446 case GTK_POLICY_ALWAYS:
1447 priv->hscroll_visible = TRUE;
1449 case GTK_POLICY_NEVER:
1450 priv->hscroll_visible = FALSE;
1453 priv->hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
1454 priv->hadjust->page_size);
1457 switch (priv->vscrollbar_policy) {
1458 case GTK_POLICY_ALWAYS:
1459 priv->vscroll_visible = TRUE;
1461 case GTK_POLICY_NEVER:
1462 priv->vscroll_visible = FALSE;
1465 priv->vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
1466 priv->vadjust->page_size);
1469 /* Store the vscroll/hscroll areas for redrawing */
1470 if (priv->vscroll_visible) {
1471 GtkAllocation *allocation = >K_WIDGET (area)->allocation;
1472 priv->vscroll_rect.x = allocation->x + allocation->width -
1474 priv->vscroll_rect.y = allocation->y;
1475 priv->vscroll_rect.width = priv->area_width;
1476 priv->vscroll_rect.height = allocation->height -
1477 (priv->hscroll_visible ? priv->area_width : 0);
1479 if (priv->hscroll_visible) {
1480 GtkAllocation *allocation = >K_WIDGET (area)->allocation;
1481 priv->hscroll_rect.y = allocation->y + allocation->height -
1483 priv->hscroll_rect.x = allocation->x;
1484 priv->hscroll_rect.height = priv->area_width;
1485 priv->hscroll_rect.width = allocation->width -
1486 (priv->vscroll_visible ? priv->area_width : 0);
1489 if (GTK_WIDGET_DRAWABLE (area)) {
1490 if (priv->hscroll_visible != prev_hscroll_visible) {
1491 gtk_widget_queue_resize (GTK_WIDGET (area));
1494 if (priv->vscroll_visible != prev_vscroll_visible) {
1495 gtk_widget_queue_resize (GTK_WIDGET (area));
1501 /* Scroll by a particular amount (in pixels). Optionally, return if
1502 * the scroll on a particular axis was successful.
1505 hildon_pannable_axis_scroll (HildonPannableArea *area,
1506 GtkAdjustment *adjust,
1510 gint *overshot_dist,
1516 HildonPannableAreaPrivate *priv = area->priv;
1518 dist = gtk_adjustment_get_value (adjust) - inc;
1521 * We use overshot_dist to define the distance of the current overshoot,
1522 * and overshooting to define the direction/whether or not we are overshot
1524 if (!(*overshooting)) {
1526 /* Initiation of the overshoot happens when the finger is released
1527 * and the current position of the pannable contents are out of range
1529 if (dist < adjust->lower) {
1532 dist = adjust->lower;
1534 if (overshoot_max!=0) {
1537 *overshot_dist = CLAMP (*overshot_dist + *vel, 0, overshoot_max);
1538 gtk_widget_queue_resize (GTK_WIDGET (area));
1542 } else if (dist > adjust->upper - adjust->page_size) {
1545 dist = adjust->upper - adjust->page_size;
1547 if (overshoot_max!=0) {
1550 *overshot_dist = CLAMP (*overshot_dist + *vel, -overshoot_max, 0);
1551 gtk_widget_queue_resize (GTK_WIDGET (area));
1556 if ((*scroll_to) != -1) {
1557 if (((inc < 0)&&(*scroll_to <= dist))||
1558 ((inc > 0)&&(*scroll_to >= dist))) {
1566 gtk_adjustment_set_value (adjust, dist);
1568 if (!priv->clicked) {
1570 /* When the overshoot has started we continue for BOUNCE_STEPS more steps into the overshoot
1571 * before we reverse direction. The deceleration factor is calculated based on
1572 * the percentage distance from the first item with each iteration, therefore always
1573 * returning us to the top/bottom most element
1575 if (*overshot_dist > 0) {
1577 if ((*overshooting < BOUNCE_STEPS) && (*vel > 0)) {
1579 *vel = (((gdouble)*overshot_dist)/overshoot_max) * (*vel);
1580 } else if ((*overshooting >= BOUNCE_STEPS) && (*vel > 0)) {
1583 } else if ((*overshooting > 1) && (*vel < 0)) {
1585 /* we add the MAX in order to avoid very small speeds */
1586 *vel = MIN ((((gdouble)*overshot_dist)/overshoot_max) * (*vel), -10.0);
1589 *overshot_dist = CLAMP (*overshot_dist + *vel, 0, overshoot_max);
1591 gtk_widget_queue_resize (GTK_WIDGET (area));
1593 } else if (*overshot_dist < 0) {
1595 if ((*overshooting < BOUNCE_STEPS) && (*vel < 0)) {
1597 *vel = (((gdouble)*overshot_dist)/overshoot_max) * (*vel) * -1;
1598 } else if ((*overshooting >= BOUNCE_STEPS) && (*vel < 0)) {
1601 } else if ((*overshooting > 1) && (*vel > 0)) {
1603 /* we add the MIN in order to avoid very small speeds */
1604 *vel = MAX ((((gdouble)*overshot_dist)/overshoot_max) * (*vel) * -1, 10.0);
1607 *overshot_dist = CLAMP (*overshot_dist + (*vel), -overshoot_max, 0);
1609 gtk_widget_queue_resize (GTK_WIDGET (area));
1614 gtk_widget_queue_resize (GTK_WIDGET (area));
1617 if (*overshot_dist > 0) {
1618 *overshot_dist = CLAMP ((*overshot_dist) + inc, 0, overshoot_max);
1619 } else if (*overshot_dist < 0) {
1620 *overshot_dist = CLAMP ((*overshot_dist) + inc, -1 * overshoot_max, 0);
1623 gtk_adjustment_set_value (adjust, dist);
1625 gtk_widget_queue_resize (GTK_WIDGET (area));
1631 hildon_pannable_area_scroll (HildonPannableArea *area,
1632 gdouble x, gdouble y)
1635 HildonPannableAreaPrivate *priv = area->priv;
1636 gboolean hscroll_visible, vscroll_visible;
1638 if (gtk_bin_get_child (GTK_BIN (area)) == NULL)
1641 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
1642 priv->vadjust->page_size);
1643 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
1644 priv->hadjust->page_size);
1649 if (vscroll_visible) {
1650 hildon_pannable_axis_scroll (area, priv->vadjust, &priv->vel_y, y,
1651 &priv->overshooting_y, &priv->overshot_dist_y,
1652 &priv->scroll_to_y, priv->vovershoot_max, &sy);
1657 if (hscroll_visible) {
1658 hildon_pannable_axis_scroll (area, priv->hadjust, &priv->vel_x, x,
1659 &priv->overshooting_x, &priv->overshot_dist_x,
1660 &priv->scroll_to_x, priv->hovershoot_max, &sx);
1665 /* If the scroll on a particular axis wasn't succesful, reset the
1666 * initial scroll position to the new mouse co-ordinate. This means
1667 * when you get to the top of the page, dragging down works immediately.
1680 hildon_pannable_area_timeout (HildonPannableArea * area)
1682 HildonPannableAreaPrivate *priv = area->priv;
1684 if ((!priv->enabled) || (priv->mode == HILDON_PANNABLE_AREA_MODE_PUSH)) {
1690 if (!priv->clicked) {
1691 /* Decelerate gradually when pointer is raised */
1692 if ((!priv->overshot_dist_y) &&
1693 (!priv->overshot_dist_x)) {
1695 /* in case we move to a specific point do not decelerate when arriving */
1696 if ((priv->scroll_to_x != -1)||(priv->scroll_to_y != -1)) {
1698 if (ABS (priv->vel_x) >= 1.5) {
1699 priv->vel_x *= priv->decel;
1702 if (ABS (priv->vel_y) >= 1.5) {
1703 priv->vel_y *= priv->decel;
1707 priv->vel_x *= priv->decel;
1708 priv->vel_y *= priv->decel;
1710 if ((ABS (priv->vel_x) < 1.0) && (ABS (priv->vel_y) < 1.0)) {
1719 } else if (priv->mode == HILDON_PANNABLE_AREA_MODE_AUTO) {
1725 hildon_pannable_area_scroll (area, priv->vel_x, priv->vel_y);
1731 hildon_pannable_area_calculate_velocity (gdouble *vel,
1739 if (ABS (dist) >= 0.00001) {
1740 rawvel = ((dist / ABS (delta)) *
1741 (gdouble) sps) * FORCE;
1742 *vel = *vel * (1 - SMOOTH_FACTOR) +
1743 rawvel * SMOOTH_FACTOR;
1744 *vel = *vel > 0 ? MIN (*vel, vmax)
1745 : MAX (dist, -1 * vmax);
1750 hildon_pannable_area_motion_notify_cb (GtkWidget * widget,
1751 GdkEventMotion * event)
1753 HildonPannableArea *area = HILDON_PANNABLE_AREA (widget);
1754 HildonPannableAreaPrivate *priv = area->priv;
1759 if (gtk_bin_get_child (GTK_BIN (widget)) == NULL)
1762 if ((!priv->enabled) || (!priv->clicked) ||
1763 ((event->time == priv->last_time) && (priv->last_type == 2))) {
1764 gdk_window_get_pointer (widget->window, NULL, NULL, 0);
1768 if (priv->last_type == 1) {
1769 priv->first_drag = TRUE;
1772 /* Only start the scroll if the mouse cursor passes beyond the
1773 * DnD threshold for dragging.
1775 g_object_get (G_OBJECT (gtk_settings_get_default ()),
1776 "gtk-dnd-drag-threshold", &dnd_threshold, NULL);
1777 x = event->x - priv->x;
1778 y = event->y - priv->y;
1780 if (priv->first_drag && (!priv->moved) &&
1781 ((ABS (x) > (dnd_threshold))
1782 || (ABS (y) > (dnd_threshold)))) {
1787 if (priv->first_drag) {
1789 if (ABS (priv->iy - event->y) >=
1790 ABS (priv->ix - event->x)) {
1791 gboolean vscroll_visible;
1793 g_signal_emit (area,
1794 pannable_area_signals[VERTICAL_MOVEMENT],
1795 0, (priv->iy > event->y) ?
1796 HILDON_MOVEMENT_UP :
1797 HILDON_MOVEMENT_DOWN,
1798 (gdouble)priv->ix, (gdouble)priv->iy);
1800 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
1801 priv->vadjust->page_size);
1803 if (!((vscroll_visible)&&
1804 (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT)))
1805 priv->moved = FALSE;
1808 gboolean hscroll_visible;
1810 g_signal_emit (area,
1811 pannable_area_signals[HORIZONTAL_MOVEMENT],
1812 0, (priv->ix > event->x) ?
1813 HILDON_MOVEMENT_LEFT :
1814 HILDON_MOVEMENT_RIGHT,
1815 (gdouble)priv->ix, (gdouble)priv->iy);
1817 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
1818 priv->hadjust->page_size);
1820 if (!((hscroll_visible)&&
1821 (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ)))
1822 priv->moved = FALSE;
1826 priv->first_drag = FALSE;
1828 if ((priv->mode != HILDON_PANNABLE_AREA_MODE_PUSH) &&
1829 (priv->mode != HILDON_PANNABLE_AREA_MODE_AUTO)) {
1832 priv->idle_id = gdk_threads_add_timeout ((gint)
1833 (1000.0 / (gdouble) priv->sps),
1835 hildon_pannable_area_timeout, area);
1840 switch (priv->mode) {
1841 case HILDON_PANNABLE_AREA_MODE_PUSH:
1842 /* Scroll by the amount of pixels the cursor has moved
1843 * since the last motion event.
1845 hildon_pannable_area_scroll (area, x, y);
1849 case HILDON_PANNABLE_AREA_MODE_ACCEL:
1850 /* Set acceleration relative to the initial click */
1851 priv->ex = event->x;
1852 priv->ey = event->y;
1853 priv->vel_x = ((x > 0) ? 1 : -1) *
1855 (gdouble) widget->allocation.width) *
1856 (priv->vmax - priv->vmin)) + priv->vmin);
1857 priv->vel_y = ((y > 0) ? 1 : -1) *
1859 (gdouble) widget->allocation.height) *
1860 (priv->vmax - priv->vmin)) + priv->vmin);
1862 case HILDON_PANNABLE_AREA_MODE_AUTO:
1864 delta = event->time - priv->last_time;
1866 if (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT) {
1867 gdouble dist = event->y - priv->y;
1869 hildon_pannable_area_calculate_velocity (&priv->vel_y,
1880 if (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ) {
1881 gdouble dist = event->x - priv->x;
1883 hildon_pannable_area_calculate_velocity (&priv->vel_x,
1893 hildon_pannable_area_scroll (area, x, y);
1895 if (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ)
1897 if (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT)
1908 /* Send motion notify to child */
1909 priv->last_time = event->time;
1910 priv->last_type = 2;
1911 event = (GdkEventMotion *) gdk_event_copy ((GdkEvent *) event);
1912 event->x = priv->cx + (event->x - priv->ix);
1913 event->y = priv->cy + (event->y - priv->iy);
1914 event->window = g_object_ref (priv->child);
1915 gdk_event_put ((GdkEvent *) event);
1916 gdk_event_free ((GdkEvent *) event);
1919 gdk_window_get_pointer (widget->window, NULL, NULL, 0);
1925 hildon_pannable_area_button_release_cb (GtkWidget * widget,
1926 GdkEventButton * event)
1928 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1932 if (gtk_bin_get_child (GTK_BIN (widget)) == NULL)
1935 priv->scroll_indicator_event_interrupt = 0;
1936 priv->scroll_delay_counter = SCROLLBAR_FADE_DELAY;
1938 if ((ABS (priv->vel_y) > 1.0)||
1939 (ABS (priv->vel_x) > 1.0)) {
1940 priv->scroll_indicator_alpha = 1.0;
1943 if (!priv->scroll_indicator_timeout) {
1944 priv->scroll_indicator_timeout = gdk_threads_add_timeout
1945 ((gint) (1000.0 / (gdouble) SCROLL_FADE_TIMEOUT),
1946 (GSourceFunc) hildon_pannable_area_scroll_indicator_fade, widget);
1949 if ((!priv->clicked) || (!priv->enabled) || (event->button != 1) ||
1950 ((event->time == priv->last_time) && (priv->last_type == 3)))
1953 priv->clicked = FALSE;
1955 if (priv->mode == HILDON_PANNABLE_AREA_MODE_AUTO ||
1956 priv->mode == HILDON_PANNABLE_AREA_MODE_ACCEL) {
1958 /* If overshoot has been initiated with a finger down, on release set max speed */
1959 if (priv->overshot_dist_y != 0) {
1960 priv->overshooting_y = BOUNCE_STEPS; /* Hack to stop a bounce in the finger down case */
1961 priv->vel_y = priv->vmax;
1964 if (priv->overshot_dist_x != 0) {
1965 priv->overshooting_x = BOUNCE_STEPS; /* Hack to stop a bounce in the finger down case */
1966 priv->vel_x = priv->vmax;
1970 priv->idle_id = gdk_threads_add_timeout ((gint) (1000.0 / (gdouble) priv->sps),
1972 hildon_pannable_area_timeout, widget);
1975 priv->last_time = event->time;
1976 priv->last_type = 3;
1979 priv->moved = FALSE;
1984 hildon_pannable_area_get_topmost (gtk_bin_get_child (GTK_BIN (widget))->window,
1985 event->x, event->y, &x, &y);
1987 event = (GdkEventButton *) gdk_event_copy ((GdkEvent *) event);
1991 /* Leave the widget if we've moved - This doesn't break selection,
1992 * but stops buttons from being clicked.
1994 if ((child != priv->child) || (priv->moved)) {
1995 /* Send synthetic leave event */
1996 synth_crossing (priv->child, x, y, event->x_root,
1997 event->y_root, event->time, FALSE);
1998 /* Send synthetic button release event */
1999 ((GdkEventAny *) event)->window = g_object_ref (priv->child);
2000 gdk_event_put ((GdkEvent *) event);
2002 /* Send synthetic button release event */
2003 ((GdkEventAny *) event)->window = g_object_ref (child);
2004 gdk_event_put ((GdkEvent *) event);
2005 /* Send synthetic leave event */
2006 synth_crossing (priv->child, x, y, event->x_root,
2007 event->y_root, event->time, FALSE);
2009 g_object_remove_weak_pointer ((GObject *) priv->child,
2010 (gpointer) & priv->child);
2012 priv->moved = FALSE;
2013 gdk_event_free ((GdkEvent *) event);
2018 /* utility event handler */
2020 hildon_pannable_area_scroll_cb (GtkWidget *widget,
2021 GdkEventScroll *event)
2023 GtkAdjustment *adj = NULL;
2024 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
2026 if ((!priv->enabled) ||
2027 (gtk_bin_get_child (GTK_BIN (widget)) == NULL))
2030 priv->scroll_indicator_event_interrupt = 0;
2031 priv->scroll_indicator_alpha = 1.0;
2032 priv->scroll_delay_counter = SCROLLBAR_FADE_DELAY + 20;
2034 if (!priv->scroll_indicator_timeout) {
2035 priv->scroll_indicator_timeout = gdk_threads_add_timeout
2036 ((gint) (1000.0 / (gdouble) (SCROLL_FADE_TIMEOUT)),
2037 (GSourceFunc) hildon_pannable_area_scroll_indicator_fade, widget);
2040 /* Stop inertial scrolling */
2041 if (priv->idle_id) {
2044 priv->overshooting_x = 0;
2045 priv->overshooting_y = 0;
2047 if ((priv->overshot_dist_x>0)||(priv->overshot_dist_y>0)) {
2048 priv->overshot_dist_x = 0;
2049 priv->overshot_dist_y = 0;
2051 gtk_widget_queue_resize (GTK_WIDGET (widget));
2054 g_source_remove (priv->idle_id);
2058 if (event->direction == GDK_SCROLL_UP || event->direction == GDK_SCROLL_DOWN)
2059 adj = priv->vadjust;
2061 adj = priv->hadjust;
2065 gdouble delta, new_value;
2067 /* from gtkrange.c calculate delta*/
2068 delta = pow (adj->page_size, 2.0 / 3.0);
2070 if (event->direction == GDK_SCROLL_UP ||
2071 event->direction == GDK_SCROLL_LEFT)
2074 new_value = CLAMP (adj->value + delta, adj->lower, adj->upper - adj->page_size);
2076 gtk_adjustment_set_value (adj, new_value);
2083 hildon_pannable_area_child_mapped (GtkWidget *widget,
2087 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (user_data)->priv;
2089 if (priv->event_window != NULL && priv->enabled)
2090 gdk_window_raise (priv->event_window);
2094 hildon_pannable_area_add (GtkContainer *container, GtkWidget *child)
2096 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (container)->priv;
2098 g_return_if_fail (gtk_bin_get_child (GTK_BIN (container)) == NULL);
2100 gtk_widget_set_parent (child, GTK_WIDGET (container));
2101 GTK_BIN (container)->child = child;
2103 g_signal_connect_after (child, "map-event",
2104 G_CALLBACK (hildon_pannable_area_child_mapped),
2107 if (!gtk_widget_set_scroll_adjustments (child, priv->hadjust, priv->vadjust)) {
2108 g_warning ("%s: cannot add non scrollable widget, "
2109 "wrap it in a viewport", __FUNCTION__);
2114 hildon_pannable_area_remove (GtkContainer *container, GtkWidget *child)
2116 g_return_if_fail (HILDON_IS_PANNABLE_AREA (container));
2117 g_return_if_fail (child != NULL);
2118 g_return_if_fail (gtk_bin_get_child (GTK_BIN (container)) == child);
2120 gtk_widget_set_scroll_adjustments (child, NULL, NULL);
2122 g_signal_handlers_disconnect_by_func (GTK_WIDGET (child),
2123 G_CALLBACK (hildon_pannable_area_child_mapped),
2126 /* chain parent class handler to remove child */
2127 GTK_CONTAINER_CLASS (hildon_pannable_area_parent_class)->remove (container, child);
2131 hildon_pannable_calculate_vel_factor (HildonPannableArea * self)
2133 HildonPannableAreaPrivate *priv = self->priv;
2138 n = ceil (priv->sps * priv->scroll_time);
2140 for (i = 0; i < n && fct_i >= RATIO_TOLERANCE; i++) {
2141 fct_i *= priv->decel;
2145 priv->vel_factor = fct;
2149 * hildon_pannable_area_new:
2151 * Create a new pannable area widget
2153 * Returns: the newly created #HildonPannableArea
2159 hildon_pannable_area_new (void)
2161 return g_object_new (HILDON_TYPE_PANNABLE_AREA, NULL);
2165 * hildon_pannable_area_new_full:
2166 * @mode: #HildonPannableAreaMode
2167 * @enabled: Value for the enabled property
2168 * @vel_min: Value for the velocity-min property
2169 * @vel_max: Value for the velocity-max property
2170 * @decel: Value for the deceleration property
2171 * @sps: Value for the sps property
2173 * Create a new #HildonPannableArea widget and set various properties
2175 * returns: the newly create #HildonPannableArea
2181 hildon_pannable_area_new_full (gint mode, gboolean enabled,
2182 gdouble vel_min, gdouble vel_max,
2183 gdouble decel, guint sps)
2185 return g_object_new (HILDON_TYPE_PANNABLE_AREA,
2188 "velocity_min", vel_min,
2189 "velocity_max", vel_max,
2190 "deceleration", decel, "sps", sps, NULL);
2194 * hildon_pannable_area_add_with_viewport:
2195 * @area: A #HildonPannableArea
2196 * @child: Child widget to add to the viewport
2198 * Convenience function used to add a child to a #GtkViewport, and add the
2199 * viewport to the scrolled window.
2205 hildon_pannable_area_add_with_viewport (HildonPannableArea * area,
2209 GtkWidget *viewport;
2211 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
2212 g_return_if_fail (GTK_IS_WIDGET (child));
2213 g_return_if_fail (child->parent == NULL);
2215 bin = GTK_BIN (area);
2217 if (bin->child != NULL)
2219 g_return_if_fail (GTK_IS_VIEWPORT (bin->child));
2220 g_return_if_fail (GTK_BIN (bin->child)->child == NULL);
2222 viewport = bin->child;
2226 HildonPannableAreaPrivate *priv = area->priv;
2228 viewport = gtk_viewport_new (priv->hadjust,
2230 gtk_viewport_set_shadow_type (GTK_VIEWPORT (viewport), GTK_SHADOW_NONE);
2231 gtk_container_add (GTK_CONTAINER (area), viewport);
2234 gtk_widget_show (viewport);
2235 gtk_container_add (GTK_CONTAINER (viewport), child);
2239 * hildon_pannable_area_scroll_to:
2240 * @area: A #HildonPannableArea.
2241 * @x: The x coordinate of the destination point or -1 to ignore this axis.
2242 * @y: The y coordinate of the destination point or -1 to ignore this axis.
2244 * Smoothly scrolls @area to ensure that (@x, @y) is a visible point
2245 * on the widget. To move in only one coordinate, you must set the other one
2246 * to -1. Notice that, in %HILDON_PANNABLE_AREA_MODE_PUSH mode, this function
2247 * works just like hildon_pannable_area_jump_to().
2249 * This function is useful if you need to present the user with a particular
2250 * element inside a scrollable widget, like #GtkTreeView. For instance,
2251 * the following example shows how to scroll inside a #GtkTreeView to
2252 * make visible an item, indicated by the #GtkTreeIter @iter.
2256 * GtkTreePath *path;
2257 * GdkRectangle *rect;
2259 * path = gtk_tree_model_get_path (model, &iter);
2260 * gtk_tree_view_get_background_area (GTK_TREE_VIEW (treeview),
2261 * path, NULL, &rect);
2262 * gtk_tree_view_convert_bin_window_to_tree_coords (GTK_TREE_VIEW (treeview),
2263 * 0, rect.y, NULL, &y);
2264 * hildon_pannable_area_scroll_to (panarea, -1, y);
2265 * gtk_tree_path_free (path);
2269 * If you want to present a child widget in simpler scenarios,
2270 * use hildon_pannable_area_scroll_to_child() instead.
2272 * There is a precondition to this function: the widget must be
2273 * already realized. Check the hildon_pannable_area_jump_to_child() for
2274 * more tips regarding how to call this function during
2280 hildon_pannable_area_scroll_to (HildonPannableArea *area,
2281 const gint x, const gint y)
2283 HildonPannableAreaPrivate *priv;
2285 gint dist_x, dist_y;
2286 gboolean hscroll_visible, vscroll_visible;
2288 g_return_if_fail (GTK_WIDGET_REALIZED (area));
2289 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
2293 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
2294 priv->vadjust->page_size);
2295 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
2296 priv->hadjust->page_size);
2298 if (((!vscroll_visible)&&(!hscroll_visible)) ||
2299 (x == -1 && y == -1)) {
2303 if (priv->mode == HILDON_PANNABLE_AREA_MODE_PUSH)
2304 hildon_pannable_area_jump_to (area, x, y);
2306 width = priv->hadjust->upper - priv->hadjust->lower;
2307 height = priv->vadjust->upper - priv->vadjust->lower;
2309 g_return_if_fail (x < width || y < height);
2312 priv->scroll_to_x = x - priv->hadjust->page_size/2;
2313 dist_x = priv->scroll_to_x - priv->hadjust->value;
2315 priv->scroll_to_x = -1;
2317 priv->vel_x = - dist_x/priv->vel_factor;
2320 priv->scroll_to_x = -1;
2324 priv->scroll_to_y = y - priv->vadjust->page_size/2;
2325 dist_y = priv->scroll_to_y - priv->vadjust->value;
2327 priv->scroll_to_y = -1;
2329 priv->vel_y = - dist_y/priv->vel_factor;
2332 priv->scroll_to_y = y;
2335 if ((priv->scroll_to_y == -1) && (priv->scroll_to_y == -1)) {
2339 priv->scroll_indicator_alpha = 1.0;
2341 if (!priv->scroll_indicator_timeout)
2342 priv->scroll_indicator_timeout = gdk_threads_add_timeout
2343 ((gint) (1000.0 / (gdouble) SCROLL_FADE_TIMEOUT),
2344 (GSourceFunc) hildon_pannable_area_scroll_indicator_fade, area);
2347 priv->idle_id = gdk_threads_add_timeout ((gint) (1000.0 / (gdouble) priv->sps),
2349 hildon_pannable_area_timeout, area);
2353 * hildon_pannable_area_jump_to:
2354 * @area: A #HildonPannableArea.
2355 * @x: The x coordinate of the destination point or -1 to ignore this axis.
2356 * @y: The y coordinate of the destination point or -1 to ignore this axis.
2358 * Jumps the position of @area to ensure that (@x, @y) is a visible
2359 * point in the widget. In order to move in only one coordinate, you
2360 * must set the other one to -1. See hildon_pannable_area_scroll_to()
2361 * function for an example of how to calculate the position of
2362 * children in scrollable widgets like #GtkTreeview.
2364 * There is a precondition to this function: the widget must be
2365 * already realized. Check the hildon_pannable_area_jump_to_child() for
2366 * more tips regarding how to call this function during
2372 hildon_pannable_area_jump_to (HildonPannableArea *area,
2373 const gint x, const gint y)
2375 HildonPannableAreaPrivate *priv;
2378 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
2379 g_return_if_fail (GTK_WIDGET_REALIZED (area));
2380 g_return_if_fail (x >= -1 && y >= -1);
2382 if (x == -1 && y == -1) {
2388 width = priv->hadjust->upper - priv->hadjust->lower;
2389 height = priv->vadjust->upper - priv->vadjust->lower;
2391 g_return_if_fail (x < width || y < height);
2394 gdouble jump_to = x - priv->hadjust->page_size/2;
2396 if (jump_to > priv->hadjust->upper - priv->hadjust->page_size) {
2397 jump_to = priv->hadjust->upper - priv->hadjust->page_size;
2400 gtk_adjustment_set_value (priv->hadjust, jump_to);
2404 gdouble jump_to = y - priv->vadjust->page_size/2;
2406 if (jump_to > priv->vadjust->upper - priv->vadjust->page_size) {
2407 jump_to = priv->vadjust->upper - priv->vadjust->page_size;
2410 gtk_adjustment_set_value (priv->vadjust, jump_to);
2413 priv->scroll_indicator_alpha = 1.0;
2415 if (priv->scroll_indicator_timeout) {
2416 g_source_remove (priv->scroll_indicator_timeout);
2417 priv->scroll_indicator_timeout = 0;
2420 if (priv->idle_id) {
2423 priv->overshooting_x = 0;
2424 priv->overshooting_y = 0;
2426 if ((priv->overshot_dist_x>0)||(priv->overshot_dist_y>0)) {
2427 priv->overshot_dist_x = 0;
2428 priv->overshot_dist_y = 0;
2430 gtk_widget_queue_resize (GTK_WIDGET (area));
2433 g_source_remove (priv->idle_id);
2439 * hildon_pannable_area_scroll_to_child:
2440 * @area: A #HildonPannableArea.
2441 * @child: A #GtkWidget, descendant of @area.
2443 * Smoothly scrolls until @child is visible inside @area. @child must
2444 * be a descendant of @area. If you need to scroll inside a scrollable
2445 * widget, e.g., #GtkTreeview, see hildon_pannable_area_scroll_to().
2447 * There is a precondition to this function: the widget must be
2448 * already realized. Check the hildon_pannable_area_jump_to_child() for
2449 * more tips regarding how to call this function during
2455 hildon_pannable_area_scroll_to_child (HildonPannableArea *area, GtkWidget *child)
2457 GtkWidget *bin_child;
2460 g_return_if_fail (GTK_WIDGET_REALIZED (area));
2461 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
2462 g_return_if_fail (GTK_IS_WIDGET (child));
2463 g_return_if_fail (gtk_widget_is_ancestor (child, GTK_WIDGET (area)));
2465 if (GTK_BIN (area)->child == NULL)
2468 /* We need to get to check the child of the inside the area */
2469 bin_child = GTK_BIN (area)->child;
2471 /* we check if we added a viewport */
2472 if (GTK_IS_VIEWPORT (bin_child)) {
2473 bin_child = GTK_BIN (bin_child)->child;
2476 if (gtk_widget_translate_coordinates (child, bin_child, 0, 0, &x, &y))
2477 hildon_pannable_area_scroll_to (area, x, y);
2481 * hildon_pannable_area_jump_to_child:
2482 * @area: A #HildonPannableArea.
2483 * @child: A #GtkWidget, descendant of @area.
2485 * Jumps to make sure @child is visible inside @area. @child must
2486 * be a descendant of @area. If you want to move inside a scrollable
2487 * widget, like, #GtkTreeview, see hildon_pannable_area_scroll_to().
2489 * There is a precondition to this function: the widget must be
2490 * already realized. You can control if the widget is ready with the
2491 * GTK_WIDGET_REALIZED macro. If you want to call this function during
2492 * the initialization process of the widget do it inside a callback to
2493 * the ::realize signal, using g_signal_connect_after() function.
2498 hildon_pannable_area_jump_to_child (HildonPannableArea *area, GtkWidget *child)
2500 GtkWidget *bin_child;
2503 g_return_if_fail (GTK_WIDGET_REALIZED (area));
2504 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
2505 g_return_if_fail (GTK_IS_WIDGET (child));
2506 g_return_if_fail (gtk_widget_is_ancestor (child, GTK_WIDGET (area)));
2508 if (gtk_bin_get_child (GTK_BIN (area)) == NULL)
2511 /* We need to get to check the child of the inside the area */
2512 bin_child = gtk_bin_get_child (GTK_BIN (area));
2514 /* we check if we added a viewport */
2515 if (GTK_IS_VIEWPORT (bin_child)) {
2516 bin_child = gtk_bin_get_child (GTK_BIN (bin_child));
2519 if (gtk_widget_translate_coordinates (child, bin_child, 0, 0, &x, &y))
2520 hildon_pannable_area_jump_to (area, x, y);
2524 * hildon_pannable_get_child_widget_at:
2525 * @area: A #HildonPannableArea.
2526 * @x: horizontal coordinate of the point
2527 * @y: vertical coordinate of the point
2529 * Get the widget at the point (x, y) inside the pannable area. In
2530 * case no widget found it returns NULL.
2532 * returns: the #GtkWidget if we find a widget, NULL in any other case
2537 hildon_pannable_get_child_widget_at (HildonPannableArea *area,
2538 gdouble x, gdouble y)
2540 GdkWindow *window = NULL;
2541 GtkWidget *child_widget = NULL;
2543 window = hildon_pannable_area_get_topmost
2544 (gtk_bin_get_child (GTK_BIN (area))->window,
2547 gdk_window_get_user_data (window, (gpointer) &child_widget);
2549 return child_widget;
2554 * hildon_pannable_area_get_hadjustment:
2555 * @area: A #HildonPannableArea.
2557 * Returns the horizontal adjustment
2559 * returns: The horizontal #GtkAdjustment
2562 hildon_pannable_area_get_hadjustment (HildonPannableArea *area)
2565 g_return_val_if_fail (HILDON_IS_PANNABLE_AREA (area), NULL);
2567 return area->priv->hadjust;
2571 * hildon_pannable_area_get_vadjustment:
2572 * @area: A #HildonPannableArea.
2574 * Returns the vertical adjustment
2576 * returns: The vertical #GtkAdjustment
2579 hildon_pannable_area_get_vadjustment (HildonPannableArea *area)
2581 g_return_val_if_fail (HILDON_IS_PANNABLE_AREA (area), NULL);
2583 return area->priv->vadjust;
2588 * hildon_pannable_area_get_size_request_policy:
2589 * @area: A #HildonPannableArea.
2591 * This function returns the current size request policy of the
2592 * widget. That policy controls the way the size_request is done in
2593 * the pannable area. Check
2594 * hildon_pannable_area_set_size_request_policy() for a more detailed
2597 * returns: the policy is currently being used in the widget
2598 * #HildonSizeRequestPolicy.
2600 HildonSizeRequestPolicy
2601 hildon_pannable_area_get_size_request_policy (HildonPannableArea *area)
2603 HildonPannableAreaPrivate *priv;
2605 g_return_val_if_fail (HILDON_IS_PANNABLE_AREA (area), FALSE);
2609 return priv->size_request_policy;
2613 * hildon_pannable_area_set_size_request_policy:
2614 * @area: A #HildonPannableArea.
2615 * @size_request_policy: One of the allowed #HildonSizeRequestPolicy
2617 * This function sets the pannable area size request policy. That
2618 * policy controls the way the size_request is done in the pannable
2619 * area. Pannable can use the size request of its children
2620 * (#HILDON_SIZE_REQUEST_CHILDREN) or the minimum size required for
2621 * the area itself (#HILDON_SIZE_REQUEST_MINIMUM), the latter is the
2622 * default. Recall this size depends on the scrolling policy you are
2623 * requesting to the pannable area, if you set #GTK_POLICY_NEVER this
2624 * parameter will not have any effect with
2625 * #HILDON_SIZE_REQUEST_MINIMUM set.
2629 hildon_pannable_area_set_size_request_policy (HildonPannableArea *area,
2630 HildonSizeRequestPolicy size_request_policy)
2632 HildonPannableAreaPrivate *priv;
2634 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
2638 if (priv->size_request_policy == size_request_policy)
2641 priv->size_request_policy = size_request_policy;
2643 gtk_widget_queue_resize (GTK_WIDGET (area));
2645 g_object_notify (G_OBJECT (area), "size-request-policy");