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) {
1074 priv->idle_id = gdk_threads_add_timeout ((gint) (1000.0 / (gdouble) priv->sps),
1076 hildon_pannable_area_timeout, widget);
1080 if (priv->vscroll_visible || priv->hscroll_visible) {
1081 priv->scroll_indicator_alpha = 1.0;
1083 priv->scroll_indicator_timeout =
1084 gdk_threads_add_timeout ((gint) (1000.0 / (gdouble) SCROLL_FADE_TIMEOUT),
1085 (GSourceFunc) hildon_pannable_area_scroll_indicator_fade,
1092 hildon_pannable_area_redraw (HildonPannableArea * area)
1094 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (area)->priv;
1096 /* Redraw scroll indicators */
1097 if (GTK_WIDGET_DRAWABLE (area)) {
1098 if (priv->hscroll_visible) {
1099 gdk_window_invalidate_rect (GTK_WIDGET (area)->window,
1100 &priv->hscroll_rect, FALSE);
1103 if (priv->vscroll_visible) {
1104 gdk_window_invalidate_rect (GTK_WIDGET (area)->window,
1105 &priv->vscroll_rect, FALSE);
1111 hildon_pannable_area_scroll_indicator_fade(HildonPannableArea * area)
1114 HildonPannableAreaPrivate *priv = area->priv;
1116 /* if moving do not fade out */
1117 if (((ABS (priv->vel_y)>1.0)||
1118 (ABS (priv->vel_x)>1.0))&&(!priv->clicked)) {
1123 if (priv->scroll_indicator_event_interrupt) {
1124 /* Stop a fade out, and fade back in */
1125 if (priv->scroll_indicator_alpha >= 0.9) {
1126 priv->scroll_indicator_timeout = 0;
1127 priv->scroll_indicator_alpha = 1.0;
1130 priv->scroll_indicator_alpha += 0.2;
1133 hildon_pannable_area_redraw (area);
1136 if ((priv->scroll_indicator_alpha > 0.9) &&
1137 (priv->scroll_delay_counter > 0)) {
1138 priv->scroll_delay_counter--;
1143 if (!priv->scroll_indicator_event_interrupt) {
1144 /* Continue fade out */
1145 if (priv->scroll_indicator_alpha <= 0.1) {
1146 priv->scroll_indicator_timeout = 0;
1147 priv->scroll_delay_counter = SCROLLBAR_FADE_DELAY;
1148 priv->scroll_indicator_alpha = 0.0;
1151 priv->scroll_indicator_alpha -= 0.2;
1154 hildon_pannable_area_redraw (area);
1161 hildon_pannable_area_expose_event (GtkWidget * widget,
1162 GdkEventExpose * event)
1165 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1166 GdkColor back_color = widget->style->bg[GTK_STATE_NORMAL];
1167 GdkColor scroll_color = widget->style->base[GTK_STATE_SELECTED];
1169 if (gtk_bin_get_child (GTK_BIN (widget))) {
1171 if (priv->scroll_indicator_alpha > 0.1) {
1172 if (priv->vscroll_visible) {
1173 hildon_pannable_draw_vscroll (widget, &back_color, &scroll_color);
1175 if (priv->hscroll_visible) {
1176 hildon_pannable_draw_hscroll (widget, &back_color, &scroll_color);
1180 /* draw overshooting rectangles */
1181 if (priv->overshot_dist_y > 0) {
1182 gint overshot_height;
1184 overshot_height = MIN (priv->overshot_dist_y, widget->allocation.height -
1185 (priv->hscroll_visible ? priv->hscroll_rect.height : 0));
1187 gdk_draw_rectangle (widget->window,
1188 widget->style->bg_gc[GTK_STATE_NORMAL],
1190 widget->allocation.x,
1191 widget->allocation.y,
1192 widget->allocation.width -
1193 (priv->vscroll_visible ? priv->vscroll_rect.width : 0),
1195 } else if (priv->overshot_dist_y < 0) {
1196 gint overshot_height;
1200 MAX (priv->overshot_dist_y,
1201 -(widget->allocation.height -
1202 (priv->hscroll_visible ? priv->hscroll_rect.height : 0)));
1204 overshot_y = MAX (widget->allocation.y +
1205 widget->allocation.height +
1207 (priv->hscroll_visible ? priv->hscroll_rect.height : 0), 0);
1209 gdk_draw_rectangle (widget->window,
1210 widget->style->bg_gc[GTK_STATE_NORMAL],
1212 widget->allocation.x,
1214 widget->allocation.width -
1215 priv->vscroll_rect.width,
1219 if (priv->overshot_dist_x > 0) {
1220 gint overshot_width;
1222 overshot_width = MIN (priv->overshot_dist_x, widget->allocation.width -
1223 (priv->vscroll_visible ? priv->vscroll_rect.width : 0));
1225 gdk_draw_rectangle (widget->window,
1226 widget->style->bg_gc[GTK_STATE_NORMAL],
1228 widget->allocation.x,
1229 widget->allocation.y,
1231 widget->allocation.height -
1232 (priv->hscroll_visible ? priv->hscroll_rect.height : 0));
1233 } else if (priv->overshot_dist_x < 0) {
1234 gint overshot_width;
1238 MAX (priv->overshot_dist_x,
1239 -(widget->allocation.width -
1240 (priv->vscroll_visible ? priv->vscroll_rect.width : 0)));
1242 overshot_x = MAX (widget->allocation.x +
1243 widget->allocation.width +
1245 (priv->vscroll_visible ? priv->vscroll_rect.width : 0), 0);
1247 gdk_draw_rectangle (widget->window,
1248 widget->style->bg_gc[GTK_STATE_NORMAL],
1251 widget->allocation.y,
1253 widget->allocation.height -
1254 priv->hscroll_rect.height);
1259 if (G_UNLIKELY (priv->initial_effect)) {
1261 hildon_pannable_area_initial_effect (widget);
1263 priv->initial_effect = FALSE;
1266 return GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->expose_event (widget, event);
1270 hildon_pannable_area_get_topmost (GdkWindow * window,
1272 gint * tx, gint * ty)
1274 /* Find the GdkWindow at the given point, by recursing from a given
1275 * parent GdkWindow. Optionally return the co-ordinates transformed
1276 * relative to the child window.
1280 gdk_drawable_get_size (GDK_DRAWABLE (window), &width, &height);
1281 if ((x < 0) || (x >= width) || (y < 0) || (y >= height))
1285 gint child_x = 0, child_y = 0;
1286 GList *c, *children = gdk_window_peek_children (window);
1287 GdkWindow *old_window = window;
1289 for (c = children; c; c = c->next) {
1290 GdkWindow *child = (GdkWindow *) c->data;
1293 gdk_drawable_get_size (GDK_DRAWABLE (child), &width, &height);
1294 gdk_window_get_position (child, &wx, &wy);
1296 if (((x >= wx) && (x < (wx + width)) && (y >= wy)
1297 && (y < (wy + height))) && (gdk_window_is_visible (child))) {
1304 if (window == old_window)
1320 synth_crossing (GdkWindow * child,
1322 gint x_root, gint y_root,
1323 guint32 time, gboolean in)
1325 GdkEventCrossing *crossing_event;
1326 GdkEventType type = in ? GDK_ENTER_NOTIFY : GDK_LEAVE_NOTIFY;
1328 /* Send synthetic enter event */
1329 crossing_event = (GdkEventCrossing *) gdk_event_new (type);
1330 ((GdkEventAny *) crossing_event)->type = type;
1331 ((GdkEventAny *) crossing_event)->window = g_object_ref (child);
1332 ((GdkEventAny *) crossing_event)->send_event = FALSE;
1333 crossing_event->subwindow = g_object_ref (child);
1334 crossing_event->time = time;
1335 crossing_event->x = x;
1336 crossing_event->y = y;
1337 crossing_event->x_root = x_root;
1338 crossing_event->y_root = y_root;
1339 crossing_event->mode = GDK_CROSSING_NORMAL;
1340 crossing_event->detail = GDK_NOTIFY_UNKNOWN;
1341 crossing_event->focus = FALSE;
1342 crossing_event->state = 0;
1343 gdk_event_put ((GdkEvent *) crossing_event);
1344 gdk_event_free ((GdkEvent *) crossing_event);
1348 hildon_pannable_area_button_press_cb (GtkWidget * widget,
1349 GdkEventButton * event)
1352 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1354 if ((!priv->enabled) || (event->button != 1) ||
1355 ((event->time == priv->last_time) &&
1356 (priv->last_type == 1)) || (gtk_bin_get_child (GTK_BIN (widget)) == NULL))
1359 priv->scroll_indicator_event_interrupt = 1;
1360 priv->scroll_delay_counter = SCROLLBAR_FADE_DELAY;
1362 if (!priv->scroll_indicator_timeout){
1363 priv->scroll_indicator_timeout = gdk_threads_add_timeout
1364 ((gint) (1000.0 / (gdouble) (SCROLL_FADE_TIMEOUT)),
1365 (GSourceFunc) hildon_pannable_area_scroll_indicator_fade, widget);
1368 priv->last_time = event->time;
1369 priv->last_type = 1;
1371 priv->scroll_to_x = -1;
1372 priv->scroll_to_y = -1;
1374 if (priv->clicked && priv->child) {
1375 /* Widget stole focus on last click, send crossing-out event */
1376 synth_crossing (priv->child, 0, 0, event->x_root, event->y_root,
1377 event->time, FALSE);
1385 /* Don't allow a click if we're still moving fast */
1386 if ((ABS (priv->vel_x) <= (priv->vmax * priv->vfast_factor)) &&
1387 (ABS (priv->vel_y) <= (priv->vmax * priv->vfast_factor)))
1389 hildon_pannable_area_get_topmost (gtk_bin_get_child (GTK_BIN (widget))->window,
1390 event->x, event->y, &x, &y);
1394 priv->clicked = TRUE;
1396 /* Stop scrolling on mouse-down (so you can flick, then hold to stop) */
1402 g_object_add_weak_pointer ((GObject *) priv->child,
1403 (gpointer) & priv->child);
1405 event = (GdkEventButton *) gdk_event_copy ((GdkEvent *) event);
1411 synth_crossing (priv->child, x, y, event->x_root,
1412 event->y_root, event->time, TRUE);
1414 /* Send synthetic click (button press/release) event */
1415 ((GdkEventAny *) event)->window = g_object_ref (priv->child);
1417 gdk_event_put ((GdkEvent *) event);
1418 gdk_event_free ((GdkEvent *) event);
1426 hildon_pannable_area_refresh (HildonPannableArea * area)
1428 HildonPannableAreaPrivate *priv = area->priv;
1429 gboolean prev_hscroll_visible, prev_vscroll_visible;
1431 if (!gtk_bin_get_child (GTK_BIN (area))) {
1432 priv->vscroll_visible = FALSE;
1433 priv->hscroll_visible = FALSE;
1437 prev_hscroll_visible = priv->hscroll_visible;
1438 prev_vscroll_visible = priv->vscroll_visible;
1440 switch (priv->hscrollbar_policy) {
1441 case GTK_POLICY_ALWAYS:
1442 priv->hscroll_visible = TRUE;
1444 case GTK_POLICY_NEVER:
1445 priv->hscroll_visible = FALSE;
1448 priv->hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
1449 priv->hadjust->page_size);
1452 switch (priv->vscrollbar_policy) {
1453 case GTK_POLICY_ALWAYS:
1454 priv->vscroll_visible = TRUE;
1456 case GTK_POLICY_NEVER:
1457 priv->vscroll_visible = FALSE;
1460 priv->vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
1461 priv->vadjust->page_size);
1464 /* Store the vscroll/hscroll areas for redrawing */
1465 if (priv->vscroll_visible) {
1466 GtkAllocation *allocation = >K_WIDGET (area)->allocation;
1467 priv->vscroll_rect.x = allocation->x + allocation->width -
1469 priv->vscroll_rect.y = allocation->y;
1470 priv->vscroll_rect.width = priv->area_width;
1471 priv->vscroll_rect.height = allocation->height -
1472 (priv->hscroll_visible ? priv->area_width : 0);
1474 if (priv->hscroll_visible) {
1475 GtkAllocation *allocation = >K_WIDGET (area)->allocation;
1476 priv->hscroll_rect.y = allocation->y + allocation->height -
1478 priv->hscroll_rect.x = allocation->x;
1479 priv->hscroll_rect.height = priv->area_width;
1480 priv->hscroll_rect.width = allocation->width -
1481 (priv->vscroll_visible ? priv->area_width : 0);
1484 if (GTK_WIDGET_DRAWABLE (area)) {
1485 if (priv->hscroll_visible != prev_hscroll_visible) {
1486 gtk_widget_queue_resize (GTK_WIDGET (area));
1489 if (priv->vscroll_visible != prev_vscroll_visible) {
1490 gtk_widget_queue_resize (GTK_WIDGET (area));
1496 /* Scroll by a particular amount (in pixels). Optionally, return if
1497 * the scroll on a particular axis was successful.
1500 hildon_pannable_axis_scroll (HildonPannableArea *area,
1501 GtkAdjustment *adjust,
1505 gint *overshot_dist,
1511 HildonPannableAreaPrivate *priv = area->priv;
1513 dist = gtk_adjustment_get_value (adjust) - inc;
1516 * We use overshot_dist to define the distance of the current overshoot,
1517 * and overshooting to define the direction/whether or not we are overshot
1519 if (!(*overshooting)) {
1521 /* Initiation of the overshoot happens when the finger is released
1522 * and the current position of the pannable contents are out of range
1524 if (dist < adjust->lower) {
1527 dist = adjust->lower;
1529 if (overshoot_max!=0) {
1532 *overshot_dist = CLAMP (*overshot_dist + *vel, 0, overshoot_max);
1533 gtk_widget_queue_resize (GTK_WIDGET (area));
1537 } else if (dist > adjust->upper - adjust->page_size) {
1540 dist = adjust->upper - adjust->page_size;
1542 if (overshoot_max!=0) {
1545 *overshot_dist = CLAMP (*overshot_dist + *vel, -overshoot_max, 0);
1546 gtk_widget_queue_resize (GTK_WIDGET (area));
1551 if ((*scroll_to) != -1) {
1552 if (((inc < 0)&&(*scroll_to <= dist))||
1553 ((inc > 0)&&(*scroll_to >= dist))) {
1561 gtk_adjustment_set_value (adjust, dist);
1563 if (!priv->clicked) {
1565 /* When the overshoot has started we continue for BOUNCE_STEPS more steps into the overshoot
1566 * before we reverse direction. The deceleration factor is calculated based on
1567 * the percentage distance from the first item with each iteration, therefore always
1568 * returning us to the top/bottom most element
1570 if (*overshot_dist > 0) {
1572 if ((*overshooting < BOUNCE_STEPS) && (*vel > 0)) {
1574 *vel = (((gdouble)*overshot_dist)/overshoot_max) * (*vel);
1575 } else if ((*overshooting >= BOUNCE_STEPS) && (*vel > 0)) {
1578 } else if ((*overshooting > 1) && (*vel < 0)) {
1580 /* we add the MAX in order to avoid very small speeds */
1581 *vel = MIN ((((gdouble)*overshot_dist)/overshoot_max) * (*vel), -10.0);
1584 *overshot_dist = CLAMP (*overshot_dist + *vel, 0, overshoot_max);
1586 gtk_widget_queue_resize (GTK_WIDGET (area));
1588 } else if (*overshot_dist < 0) {
1590 if ((*overshooting < BOUNCE_STEPS) && (*vel < 0)) {
1592 *vel = (((gdouble)*overshot_dist)/overshoot_max) * (*vel) * -1;
1593 } else if ((*overshooting >= BOUNCE_STEPS) && (*vel < 0)) {
1596 } else if ((*overshooting > 1) && (*vel > 0)) {
1598 /* we add the MIN in order to avoid very small speeds */
1599 *vel = MAX ((((gdouble)*overshot_dist)/overshoot_max) * (*vel) * -1, 10.0);
1602 *overshot_dist = CLAMP (*overshot_dist + (*vel), -overshoot_max, 0);
1604 gtk_widget_queue_resize (GTK_WIDGET (area));
1609 gtk_widget_queue_resize (GTK_WIDGET (area));
1612 if (*overshot_dist > 0) {
1613 *overshot_dist = CLAMP ((*overshot_dist) + inc, 0, overshoot_max);
1614 } else if (*overshot_dist < 0) {
1615 *overshot_dist = CLAMP ((*overshot_dist) + inc, -1 * overshoot_max, 0);
1618 gtk_adjustment_set_value (adjust, dist);
1620 gtk_widget_queue_resize (GTK_WIDGET (area));
1626 hildon_pannable_area_scroll (HildonPannableArea *area,
1627 gdouble x, gdouble y)
1630 HildonPannableAreaPrivate *priv = area->priv;
1631 gboolean hscroll_visible, vscroll_visible;
1633 if (gtk_bin_get_child (GTK_BIN (area)) == NULL)
1636 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
1637 priv->vadjust->page_size);
1638 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
1639 priv->hadjust->page_size);
1644 if (vscroll_visible) {
1645 hildon_pannable_axis_scroll (area, priv->vadjust, &priv->vel_y, y,
1646 &priv->overshooting_y, &priv->overshot_dist_y,
1647 &priv->scroll_to_y, priv->vovershoot_max, &sy);
1652 if (hscroll_visible) {
1653 hildon_pannable_axis_scroll (area, priv->hadjust, &priv->vel_x, x,
1654 &priv->overshooting_x, &priv->overshot_dist_x,
1655 &priv->scroll_to_x, priv->hovershoot_max, &sx);
1660 /* If the scroll on a particular axis wasn't succesful, reset the
1661 * initial scroll position to the new mouse co-ordinate. This means
1662 * when you get to the top of the page, dragging down works immediately.
1675 hildon_pannable_area_timeout (HildonPannableArea * area)
1677 HildonPannableAreaPrivate *priv = area->priv;
1679 if ((!priv->enabled) || (priv->mode == HILDON_PANNABLE_AREA_MODE_PUSH)) {
1685 if (!priv->clicked) {
1686 /* Decelerate gradually when pointer is raised */
1687 if ((!priv->overshot_dist_y) &&
1688 (!priv->overshot_dist_x)) {
1690 /* in case we move to a specific point do not decelerate when arriving */
1691 if ((priv->scroll_to_x != -1)||(priv->scroll_to_y != -1)) {
1693 if (ABS (priv->vel_x) >= 1.5) {
1694 priv->vel_x *= priv->decel;
1697 if (ABS (priv->vel_y) >= 1.5) {
1698 priv->vel_y *= priv->decel;
1702 priv->vel_x *= priv->decel;
1703 priv->vel_y *= priv->decel;
1705 if ((ABS (priv->vel_x) < 1.0) && (ABS (priv->vel_y) < 1.0)) {
1714 } else if (priv->mode == HILDON_PANNABLE_AREA_MODE_AUTO) {
1720 hildon_pannable_area_scroll (area, priv->vel_x, priv->vel_y);
1726 hildon_pannable_area_calculate_velocity (gdouble *vel,
1734 if (ABS (dist) >= 0.00001) {
1735 rawvel = ((dist / ABS (delta)) *
1736 (gdouble) sps) * FORCE;
1737 *vel = *vel * (1 - SMOOTH_FACTOR) +
1738 rawvel * SMOOTH_FACTOR;
1739 *vel = *vel > 0 ? MIN (*vel, vmax)
1740 : MAX (dist, -1 * vmax);
1745 hildon_pannable_area_motion_notify_cb (GtkWidget * widget,
1746 GdkEventMotion * event)
1748 HildonPannableArea *area = HILDON_PANNABLE_AREA (widget);
1749 HildonPannableAreaPrivate *priv = area->priv;
1754 if (gtk_bin_get_child (GTK_BIN (widget)) == NULL)
1757 if ((!priv->enabled) || (!priv->clicked) ||
1758 ((event->time == priv->last_time) && (priv->last_type == 2))) {
1759 gdk_window_get_pointer (widget->window, NULL, NULL, 0);
1763 if (priv->last_type == 1) {
1764 priv->first_drag = TRUE;
1767 /* Only start the scroll if the mouse cursor passes beyond the
1768 * DnD threshold for dragging.
1770 g_object_get (G_OBJECT (gtk_settings_get_default ()),
1771 "gtk-dnd-drag-threshold", &dnd_threshold, NULL);
1772 x = event->x - priv->x;
1773 y = event->y - priv->y;
1775 if (priv->first_drag && (!priv->moved) &&
1776 ((ABS (x) > (dnd_threshold))
1777 || (ABS (y) > (dnd_threshold)))) {
1782 if (priv->first_drag) {
1784 if (ABS (priv->iy - event->y) >=
1785 ABS (priv->ix - event->x)) {
1786 gboolean vscroll_visible;
1788 g_signal_emit (area,
1789 pannable_area_signals[VERTICAL_MOVEMENT],
1790 0, (priv->iy > event->y) ?
1791 HILDON_MOVEMENT_UP :
1792 HILDON_MOVEMENT_DOWN,
1793 (gdouble)priv->ix, (gdouble)priv->iy);
1795 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
1796 priv->vadjust->page_size);
1798 if (!((vscroll_visible)&&
1799 (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT)))
1800 priv->moved = FALSE;
1803 gboolean hscroll_visible;
1805 g_signal_emit (area,
1806 pannable_area_signals[HORIZONTAL_MOVEMENT],
1807 0, (priv->ix > event->x) ?
1808 HILDON_MOVEMENT_LEFT :
1809 HILDON_MOVEMENT_RIGHT,
1810 (gdouble)priv->ix, (gdouble)priv->iy);
1812 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
1813 priv->hadjust->page_size);
1815 if (!((hscroll_visible)&&
1816 (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ)))
1817 priv->moved = FALSE;
1821 priv->first_drag = FALSE;
1823 if ((priv->mode != HILDON_PANNABLE_AREA_MODE_PUSH) &&
1824 (priv->mode != HILDON_PANNABLE_AREA_MODE_AUTO)) {
1827 priv->idle_id = gdk_threads_add_timeout ((gint)
1828 (1000.0 / (gdouble) priv->sps),
1830 hildon_pannable_area_timeout, area);
1835 switch (priv->mode) {
1836 case HILDON_PANNABLE_AREA_MODE_PUSH:
1837 /* Scroll by the amount of pixels the cursor has moved
1838 * since the last motion event.
1840 hildon_pannable_area_scroll (area, x, y);
1844 case HILDON_PANNABLE_AREA_MODE_ACCEL:
1845 /* Set acceleration relative to the initial click */
1846 priv->ex = event->x;
1847 priv->ey = event->y;
1848 priv->vel_x = ((x > 0) ? 1 : -1) *
1850 (gdouble) widget->allocation.width) *
1851 (priv->vmax - priv->vmin)) + priv->vmin);
1852 priv->vel_y = ((y > 0) ? 1 : -1) *
1854 (gdouble) widget->allocation.height) *
1855 (priv->vmax - priv->vmin)) + priv->vmin);
1857 case HILDON_PANNABLE_AREA_MODE_AUTO:
1859 delta = event->time - priv->last_time;
1861 if (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT) {
1862 gdouble dist = event->y - priv->y;
1864 hildon_pannable_area_calculate_velocity (&priv->vel_y,
1875 if (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ) {
1876 gdouble dist = event->x - priv->x;
1878 hildon_pannable_area_calculate_velocity (&priv->vel_x,
1888 hildon_pannable_area_scroll (area, x, y);
1890 if (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ)
1892 if (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT)
1903 /* Send motion notify to child */
1904 priv->last_time = event->time;
1905 priv->last_type = 2;
1906 event = (GdkEventMotion *) gdk_event_copy ((GdkEvent *) event);
1907 event->x = priv->cx + (event->x - priv->ix);
1908 event->y = priv->cy + (event->y - priv->iy);
1909 event->window = g_object_ref (priv->child);
1910 gdk_event_put ((GdkEvent *) event);
1911 gdk_event_free ((GdkEvent *) event);
1914 gdk_window_get_pointer (widget->window, NULL, NULL, 0);
1920 hildon_pannable_area_button_release_cb (GtkWidget * widget,
1921 GdkEventButton * event)
1923 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1927 if (gtk_bin_get_child (GTK_BIN (widget)) == NULL)
1930 priv->scroll_indicator_event_interrupt = 0;
1931 priv->scroll_delay_counter = SCROLLBAR_FADE_DELAY;
1933 if ((ABS (priv->vel_y) > 1.0)||
1934 (ABS (priv->vel_x) > 1.0)) {
1935 priv->scroll_indicator_alpha = 1.0;
1938 if (!priv->scroll_indicator_timeout) {
1939 priv->scroll_indicator_timeout = gdk_threads_add_timeout
1940 ((gint) (1000.0 / (gdouble) SCROLL_FADE_TIMEOUT),
1941 (GSourceFunc) hildon_pannable_area_scroll_indicator_fade, widget);
1944 if ((!priv->clicked) || (!priv->enabled) || (event->button != 1) ||
1945 ((event->time == priv->last_time) && (priv->last_type == 3)))
1948 priv->clicked = FALSE;
1950 if (priv->mode == HILDON_PANNABLE_AREA_MODE_AUTO ||
1951 priv->mode == HILDON_PANNABLE_AREA_MODE_ACCEL) {
1953 /* If overshoot has been initiated with a finger down, on release set max speed */
1954 if (priv->overshot_dist_y != 0) {
1955 priv->overshooting_y = BOUNCE_STEPS; /* Hack to stop a bounce in the finger down case */
1956 priv->vel_y = priv->vmax;
1959 if (priv->overshot_dist_x != 0) {
1960 priv->overshooting_x = BOUNCE_STEPS; /* Hack to stop a bounce in the finger down case */
1961 priv->vel_x = priv->vmax;
1965 priv->idle_id = gdk_threads_add_timeout ((gint) (1000.0 / (gdouble) priv->sps),
1967 hildon_pannable_area_timeout, widget);
1970 priv->last_time = event->time;
1971 priv->last_type = 3;
1974 priv->moved = FALSE;
1979 hildon_pannable_area_get_topmost (gtk_bin_get_child (GTK_BIN (widget))->window,
1980 event->x, event->y, &x, &y);
1982 event = (GdkEventButton *) gdk_event_copy ((GdkEvent *) event);
1986 /* Leave the widget if we've moved - This doesn't break selection,
1987 * but stops buttons from being clicked.
1989 if ((child != priv->child) || (priv->moved)) {
1990 /* Send synthetic leave event */
1991 synth_crossing (priv->child, x, y, event->x_root,
1992 event->y_root, event->time, FALSE);
1993 /* Send synthetic button release event */
1994 ((GdkEventAny *) event)->window = g_object_ref (priv->child);
1995 gdk_event_put ((GdkEvent *) event);
1997 /* Send synthetic button release event */
1998 ((GdkEventAny *) event)->window = g_object_ref (child);
1999 gdk_event_put ((GdkEvent *) event);
2000 /* Send synthetic leave event */
2001 synth_crossing (priv->child, x, y, event->x_root,
2002 event->y_root, event->time, FALSE);
2004 g_object_remove_weak_pointer ((GObject *) priv->child,
2005 (gpointer) & priv->child);
2007 priv->moved = FALSE;
2008 gdk_event_free ((GdkEvent *) event);
2013 /* utility event handler */
2015 hildon_pannable_area_scroll_cb (GtkWidget *widget,
2016 GdkEventScroll *event)
2018 GtkAdjustment *adj = NULL;
2019 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
2021 if ((!priv->enabled) ||
2022 (gtk_bin_get_child (GTK_BIN (widget)) == NULL))
2025 priv->scroll_indicator_event_interrupt = 0;
2026 priv->scroll_indicator_alpha = 1.0;
2027 priv->scroll_delay_counter = SCROLLBAR_FADE_DELAY + 20;
2029 if (!priv->scroll_indicator_timeout) {
2030 priv->scroll_indicator_timeout = gdk_threads_add_timeout
2031 ((gint) (1000.0 / (gdouble) (SCROLL_FADE_TIMEOUT)),
2032 (GSourceFunc) hildon_pannable_area_scroll_indicator_fade, widget);
2035 /* Stop inertial scrolling */
2036 if (priv->idle_id) {
2039 priv->overshooting_x = 0;
2040 priv->overshooting_y = 0;
2042 if ((priv->overshot_dist_x>0)||(priv->overshot_dist_y>0)) {
2043 priv->overshot_dist_x = 0;
2044 priv->overshot_dist_y = 0;
2046 gtk_widget_queue_resize (GTK_WIDGET (widget));
2049 g_source_remove (priv->idle_id);
2053 if (event->direction == GDK_SCROLL_UP || event->direction == GDK_SCROLL_DOWN)
2054 adj = priv->vadjust;
2056 adj = priv->hadjust;
2060 gdouble delta, new_value;
2062 /* from gtkrange.c calculate delta*/
2063 delta = pow (adj->page_size, 2.0 / 3.0);
2065 if (event->direction == GDK_SCROLL_UP ||
2066 event->direction == GDK_SCROLL_LEFT)
2069 new_value = CLAMP (adj->value + delta, adj->lower, adj->upper - adj->page_size);
2071 gtk_adjustment_set_value (adj, new_value);
2078 hildon_pannable_area_child_mapped (GtkWidget *widget,
2082 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (user_data)->priv;
2084 if (priv->event_window != NULL && priv->enabled)
2085 gdk_window_raise (priv->event_window);
2089 hildon_pannable_area_add (GtkContainer *container, GtkWidget *child)
2091 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (container)->priv;
2093 g_return_if_fail (gtk_bin_get_child (GTK_BIN (container)) == NULL);
2095 gtk_widget_set_parent (child, GTK_WIDGET (container));
2096 GTK_BIN (container)->child = child;
2098 g_signal_connect_after (child, "map-event",
2099 G_CALLBACK (hildon_pannable_area_child_mapped),
2102 if (!gtk_widget_set_scroll_adjustments (child, priv->hadjust, priv->vadjust)) {
2103 g_warning ("%s: cannot add non scrollable widget, "
2104 "wrap it in a viewport", __FUNCTION__);
2109 hildon_pannable_area_remove (GtkContainer *container, GtkWidget *child)
2111 g_return_if_fail (HILDON_IS_PANNABLE_AREA (container));
2112 g_return_if_fail (child != NULL);
2113 g_return_if_fail (gtk_bin_get_child (GTK_BIN (container)) == child);
2115 gtk_widget_set_scroll_adjustments (child, NULL, NULL);
2117 g_signal_handlers_disconnect_by_func (GTK_WIDGET (child),
2118 G_CALLBACK (hildon_pannable_area_child_mapped),
2121 /* chain parent class handler to remove child */
2122 GTK_CONTAINER_CLASS (hildon_pannable_area_parent_class)->remove (container, child);
2126 hildon_pannable_calculate_vel_factor (HildonPannableArea * self)
2128 HildonPannableAreaPrivate *priv = self->priv;
2133 n = ceil (priv->sps * priv->scroll_time);
2135 for (i = 0; i < n && fct_i >= RATIO_TOLERANCE; i++) {
2136 fct_i *= priv->decel;
2140 priv->vel_factor = fct;
2144 * hildon_pannable_area_new:
2146 * Create a new pannable area widget
2148 * Returns: the newly created #HildonPannableArea
2154 hildon_pannable_area_new (void)
2156 return g_object_new (HILDON_TYPE_PANNABLE_AREA, NULL);
2160 * hildon_pannable_area_new_full:
2161 * @mode: #HildonPannableAreaMode
2162 * @enabled: Value for the enabled property
2163 * @vel_min: Value for the velocity-min property
2164 * @vel_max: Value for the velocity-max property
2165 * @decel: Value for the deceleration property
2166 * @sps: Value for the sps property
2168 * Create a new #HildonPannableArea widget and set various properties
2170 * returns: the newly create #HildonPannableArea
2176 hildon_pannable_area_new_full (gint mode, gboolean enabled,
2177 gdouble vel_min, gdouble vel_max,
2178 gdouble decel, guint sps)
2180 return g_object_new (HILDON_TYPE_PANNABLE_AREA,
2183 "velocity_min", vel_min,
2184 "velocity_max", vel_max,
2185 "deceleration", decel, "sps", sps, NULL);
2189 * hildon_pannable_area_add_with_viewport:
2190 * @area: A #HildonPannableArea
2191 * @child: Child widget to add to the viewport
2193 * Convenience function used to add a child to a #GtkViewport, and add the
2194 * viewport to the scrolled window.
2200 hildon_pannable_area_add_with_viewport (HildonPannableArea * area,
2204 GtkWidget *viewport;
2206 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
2207 g_return_if_fail (GTK_IS_WIDGET (child));
2208 g_return_if_fail (child->parent == NULL);
2210 bin = GTK_BIN (area);
2212 if (bin->child != NULL)
2214 g_return_if_fail (GTK_IS_VIEWPORT (bin->child));
2215 g_return_if_fail (GTK_BIN (bin->child)->child == NULL);
2217 viewport = bin->child;
2221 HildonPannableAreaPrivate *priv = area->priv;
2223 viewport = gtk_viewport_new (priv->hadjust,
2225 gtk_viewport_set_shadow_type (GTK_VIEWPORT (viewport), GTK_SHADOW_NONE);
2226 gtk_container_add (GTK_CONTAINER (area), viewport);
2229 gtk_widget_show (viewport);
2230 gtk_container_add (GTK_CONTAINER (viewport), child);
2234 * hildon_pannable_area_scroll_to:
2235 * @area: A #HildonPannableArea.
2236 * @x: The x coordinate of the destination point or -1 to ignore this axis.
2237 * @y: The y coordinate of the destination point or -1 to ignore this axis.
2239 * Smoothly scrolls @area to ensure that (@x, @y) is a visible point
2240 * on the widget. To move in only one coordinate, you must set the other one
2241 * to -1. Notice that, in %HILDON_PANNABLE_AREA_MODE_PUSH mode, this function
2242 * works just like hildon_pannable_area_jump_to().
2244 * This function is useful if you need to present the user with a particular
2245 * element inside a scrollable widget, like #GtkTreeView. For instance,
2246 * the following example shows how to scroll inside a #GtkTreeView to
2247 * make visible an item, indicated by the #GtkTreeIter @iter.
2251 * GtkTreePath *path;
2252 * GdkRectangle *rect;
2254 * path = gtk_tree_model_get_path (model, &iter);
2255 * gtk_tree_view_get_background_area (GTK_TREE_VIEW (treeview),
2256 * path, NULL, &rect);
2257 * gtk_tree_view_convert_bin_window_to_tree_coords (GTK_TREE_VIEW (treeview),
2258 * 0, rect.y, NULL, &y);
2259 * hildon_pannable_area_scroll_to (panarea, -1, y);
2260 * gtk_tree_path_free (path);
2264 * If you want to present a child widget in simpler scenarios,
2265 * use hildon_pannable_area_scroll_to_child() instead.
2267 * There is a precondition to this function: the widget must be
2268 * already realized. Check the hildon_pannable_area_jump_to_child() for
2269 * more tips regarding how to call this function during
2275 hildon_pannable_area_scroll_to (HildonPannableArea *area,
2276 const gint x, const gint y)
2278 HildonPannableAreaPrivate *priv;
2280 gint dist_x, dist_y;
2281 gboolean hscroll_visible, vscroll_visible;
2283 g_return_if_fail (GTK_WIDGET_REALIZED (area));
2284 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
2288 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
2289 priv->vadjust->page_size);
2290 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
2291 priv->hadjust->page_size);
2293 if (((!vscroll_visible)&&(!hscroll_visible)) ||
2294 (x == -1 && y == -1)) {
2298 if (priv->mode == HILDON_PANNABLE_AREA_MODE_PUSH)
2299 hildon_pannable_area_jump_to (area, x, y);
2301 width = priv->hadjust->upper - priv->hadjust->lower;
2302 height = priv->vadjust->upper - priv->vadjust->lower;
2304 g_return_if_fail (x < width || y < height);
2307 priv->scroll_to_x = x - priv->hadjust->page_size/2;
2308 dist_x = priv->scroll_to_x - priv->hadjust->value;
2310 priv->scroll_to_x = -1;
2312 priv->vel_x = - dist_x/priv->vel_factor;
2315 priv->scroll_to_x = -1;
2319 priv->scroll_to_y = y - priv->vadjust->page_size/2;
2320 dist_y = priv->scroll_to_y - priv->vadjust->value;
2322 priv->scroll_to_y = -1;
2324 priv->vel_y = - dist_y/priv->vel_factor;
2327 priv->scroll_to_y = y;
2330 if ((priv->scroll_to_y == -1) && (priv->scroll_to_y == -1)) {
2334 priv->scroll_indicator_alpha = 1.0;
2336 if (!priv->scroll_indicator_timeout)
2337 priv->scroll_indicator_timeout = gdk_threads_add_timeout
2338 ((gint) (1000.0 / (gdouble) SCROLL_FADE_TIMEOUT),
2339 (GSourceFunc) hildon_pannable_area_scroll_indicator_fade, area);
2342 priv->idle_id = gdk_threads_add_timeout ((gint) (1000.0 / (gdouble) priv->sps),
2344 hildon_pannable_area_timeout, area);
2348 * hildon_pannable_area_jump_to:
2349 * @area: A #HildonPannableArea.
2350 * @x: The x coordinate of the destination point or -1 to ignore this axis.
2351 * @y: The y coordinate of the destination point or -1 to ignore this axis.
2353 * Jumps the position of @area to ensure that (@x, @y) is a visible
2354 * point in the widget. In order to move in only one coordinate, you
2355 * must set the other one to -1. See hildon_pannable_area_scroll_to()
2356 * function for an example of how to calculate the position of
2357 * children in scrollable widgets like #GtkTreeview.
2359 * There is a precondition to this function: the widget must be
2360 * already realized. Check the hildon_pannable_area_jump_to_child() for
2361 * more tips regarding how to call this function during
2367 hildon_pannable_area_jump_to (HildonPannableArea *area,
2368 const gint x, const gint y)
2370 HildonPannableAreaPrivate *priv;
2373 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
2374 g_return_if_fail (GTK_WIDGET_REALIZED (area));
2375 g_return_if_fail (x >= -1 && y >= -1);
2377 if (x == -1 && y == -1) {
2383 width = priv->hadjust->upper - priv->hadjust->lower;
2384 height = priv->vadjust->upper - priv->vadjust->lower;
2386 g_return_if_fail (x < width || y < height);
2389 gdouble jump_to = x - priv->hadjust->page_size/2;
2391 if (jump_to > priv->hadjust->upper - priv->hadjust->page_size) {
2392 jump_to = priv->hadjust->upper - priv->hadjust->page_size;
2395 gtk_adjustment_set_value (priv->hadjust, jump_to);
2399 gdouble jump_to = y - priv->vadjust->page_size/2;
2401 if (jump_to > priv->vadjust->upper - priv->vadjust->page_size) {
2402 jump_to = priv->vadjust->upper - priv->vadjust->page_size;
2405 gtk_adjustment_set_value (priv->vadjust, jump_to);
2408 priv->scroll_indicator_alpha = 1.0;
2410 if (priv->scroll_indicator_timeout) {
2411 g_source_remove (priv->scroll_indicator_timeout);
2412 priv->scroll_indicator_timeout = 0;
2415 if (priv->idle_id) {
2418 priv->overshooting_x = 0;
2419 priv->overshooting_y = 0;
2421 if ((priv->overshot_dist_x>0)||(priv->overshot_dist_y>0)) {
2422 priv->overshot_dist_x = 0;
2423 priv->overshot_dist_y = 0;
2425 gtk_widget_queue_resize (GTK_WIDGET (area));
2428 g_source_remove (priv->idle_id);
2434 * hildon_pannable_area_scroll_to_child:
2435 * @area: A #HildonPannableArea.
2436 * @child: A #GtkWidget, descendant of @area.
2438 * Smoothly scrolls until @child is visible inside @area. @child must
2439 * be a descendant of @area. If you need to scroll inside a scrollable
2440 * widget, e.g., #GtkTreeview, see hildon_pannable_area_scroll_to().
2442 * There is a precondition to this function: the widget must be
2443 * already realized. Check the hildon_pannable_area_jump_to_child() for
2444 * more tips regarding how to call this function during
2450 hildon_pannable_area_scroll_to_child (HildonPannableArea *area, GtkWidget *child)
2452 GtkWidget *bin_child;
2455 g_return_if_fail (GTK_WIDGET_REALIZED (area));
2456 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
2457 g_return_if_fail (GTK_IS_WIDGET (child));
2458 g_return_if_fail (gtk_widget_is_ancestor (child, GTK_WIDGET (area)));
2460 if (GTK_BIN (area)->child == NULL)
2463 /* We need to get to check the child of the inside the area */
2464 bin_child = GTK_BIN (area)->child;
2466 /* we check if we added a viewport */
2467 if (GTK_IS_VIEWPORT (bin_child)) {
2468 bin_child = GTK_BIN (bin_child)->child;
2471 if (gtk_widget_translate_coordinates (child, bin_child, 0, 0, &x, &y))
2472 hildon_pannable_area_scroll_to (area, x, y);
2476 * hildon_pannable_area_jump_to_child:
2477 * @area: A #HildonPannableArea.
2478 * @child: A #GtkWidget, descendant of @area.
2480 * Jumps to make sure @child is visible inside @area. @child must
2481 * be a descendant of @area. If you want to move inside a scrollable
2482 * widget, like, #GtkTreeview, see hildon_pannable_area_scroll_to().
2484 * There is a precondition to this function: the widget must be
2485 * already realized. You can control if the widget is ready with the
2486 * GTK_WIDGET_REALIZED macro. If you want to call this function during
2487 * the initialization process of the widget do it inside a callback to
2488 * the ::realize signal, using g_signal_connect_after() function.
2493 hildon_pannable_area_jump_to_child (HildonPannableArea *area, GtkWidget *child)
2495 GtkWidget *bin_child;
2498 g_return_if_fail (GTK_WIDGET_REALIZED (area));
2499 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
2500 g_return_if_fail (GTK_IS_WIDGET (child));
2501 g_return_if_fail (gtk_widget_is_ancestor (child, GTK_WIDGET (area)));
2503 if (gtk_bin_get_child (GTK_BIN (area)) == NULL)
2506 /* We need to get to check the child of the inside the area */
2507 bin_child = gtk_bin_get_child (GTK_BIN (area));
2509 /* we check if we added a viewport */
2510 if (GTK_IS_VIEWPORT (bin_child)) {
2511 bin_child = gtk_bin_get_child (GTK_BIN (bin_child));
2514 if (gtk_widget_translate_coordinates (child, bin_child, 0, 0, &x, &y))
2515 hildon_pannable_area_jump_to (area, x, y);
2519 * hildon_pannable_get_child_widget_at:
2520 * @area: A #HildonPannableArea.
2521 * @x: horizontal coordinate of the point
2522 * @y: vertical coordinate of the point
2524 * Get the widget at the point (x, y) inside the pannable area. In
2525 * case no widget found it returns NULL.
2527 * returns: the #GtkWidget if we find a widget, NULL in any other case
2532 hildon_pannable_get_child_widget_at (HildonPannableArea *area,
2533 gdouble x, gdouble y)
2535 GdkWindow *window = NULL;
2536 GtkWidget *child_widget = NULL;
2538 window = hildon_pannable_area_get_topmost
2539 (gtk_bin_get_child (GTK_BIN (area))->window,
2542 gdk_window_get_user_data (window, (gpointer) &child_widget);
2544 return child_widget;
2549 * hildon_pannable_area_get_hadjustment:
2550 * @area: A #HildonPannableArea.
2552 * Returns the horizontal adjustment
2554 * returns: The horizontal #GtkAdjustment
2557 hildon_pannable_area_get_hadjustment (HildonPannableArea *area)
2560 g_return_val_if_fail (HILDON_IS_PANNABLE_AREA (area), NULL);
2562 return area->priv->hadjust;
2566 * hildon_pannable_area_get_vadjustment:
2567 * @area: A #HildonPannableArea.
2569 * Returns the vertical adjustment
2571 * returns: The vertical #GtkAdjustment
2574 hildon_pannable_area_get_vadjustment (HildonPannableArea *area)
2576 g_return_val_if_fail (HILDON_IS_PANNABLE_AREA (area), NULL);
2578 return area->priv->vadjust;
2583 * hildon_pannable_area_get_size_request_policy:
2584 * @area: A #HildonPannableArea.
2586 * This function returns the current size request policy of the
2587 * widget. That policy controls the way the size_request is done in
2588 * the pannable area. Check
2589 * hildon_pannable_area_set_size_request_policy() for a more detailed
2592 * returns: the policy is currently being used in the widget
2593 * #HildonSizeRequestPolicy.
2595 HildonSizeRequestPolicy
2596 hildon_pannable_area_get_size_request_policy (HildonPannableArea *area)
2598 HildonPannableAreaPrivate *priv;
2600 g_return_val_if_fail (HILDON_IS_PANNABLE_AREA (area), FALSE);
2604 return priv->size_request_policy;
2608 * hildon_pannable_area_set_size_request_policy:
2609 * @area: A #HildonPannableArea.
2610 * @size_request_policy: One of the allowed #HildonSizeRequestPolicy
2612 * This function sets the pannable area size request policy. That
2613 * policy controls the way the size_request is done in the pannable
2614 * area. Pannable can use the size request of its children
2615 * (#HILDON_SIZE_REQUEST_CHILDREN) or the minimum size required for
2616 * the area itself (#HILDON_SIZE_REQUEST_MINIMUM), the latter is the
2617 * default. Recall this size depends on the scrolling policy you are
2618 * requesting to the pannable area, if you set #GTK_POLICY_NEVER this
2619 * parameter will not have any effect with
2620 * #HILDON_SIZE_REQUEST_MINIMUM set.
2624 hildon_pannable_area_set_size_request_policy (HildonPannableArea *area,
2625 HildonSizeRequestPolicy size_request_policy)
2627 HildonPannableAreaPrivate *priv;
2629 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
2633 if (priv->size_request_policy == size_request_policy)
2636 priv->size_request_policy = size_request_policy;
2638 gtk_widget_queue_resize (GTK_WIDGET (area));
2640 g_object_notify (G_OBJECT (area), "size-request-policy");