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 is a container widget that can be "panned" (scrolled)
30 * up and down using the touchscreen with fingers. The widget has no scrollbars,
31 * but it rather shows small scroll indicators to give an idea of the part of the
32 * content that is visible at a time. The scroll indicators appear when a dragging
33 * motion is started on the pannable area.
35 * The scrolling is "kinetic", meaning the motion can be "flicked" and it will
36 * continue from the initial motion by gradually slowing down to an eventual stop.
37 * The motion can also be stopped immediately by pressing the touchscreen over the
41 #undef HILDON_DISABLE_DEPRECATED
44 #if USE_CAIRO_SCROLLBARS == 1
49 #include "hildon-pannable-area.h"
50 #include "hildon-marshalers.h"
51 #include "hildon-enum-types.h"
53 #define USE_CAIRO_SCROLLBARS 0
55 #define SCROLL_BAR_MIN_SIZE 5
56 #define RATIO_TOLERANCE 0.000001
57 #define SCROLL_FADE_TIMEOUT 100
58 #define MOTION_EVENTS_PER_SECOND 25
59 #define CURSOR_STOPPED_TIMEOUT 80
60 #define PANNABLE_MAX_WIDTH 788
61 #define PANNABLE_MAX_HEIGHT 378
63 G_DEFINE_TYPE (HildonPannableArea, hildon_pannable_area, GTK_TYPE_BIN)
65 #define PANNABLE_AREA_PRIVATE(o) \
66 (G_TYPE_INSTANCE_GET_PRIVATE ((o), HILDON_TYPE_PANNABLE_AREA, \
67 HildonPannableAreaPrivate))
69 struct _HildonPannableAreaPrivate {
70 HildonPannableAreaMode mode;
71 HildonMovementMode mov_mode;
72 GdkWindow *event_window;
73 gdouble x; /* Used to store mouse co-ordinates of the first or */
74 gdouble y; /* previous events in a press-motion pair */
75 gdouble ex; /* Used to store mouse co-ordinates of the last */
76 gdouble ey; /* motion event in acceleration mode */
78 gboolean button_pressed;
79 guint32 last_time; /* Last event time, to stop infinite loops */
85 gdouble vmax_overshooting;
92 guint panning_threshold;
93 guint scrollbar_fade_delay;
96 guint direction_error_margin;
102 gint ix; /* Initial click mouse co-ordinates */
104 gint cx; /* Initial click child window mouse co-ordinates */
111 gint overshot_dist_x;
112 gint overshot_dist_y;
115 gdouble scroll_indicator_alpha;
116 gint motion_event_scroll_timeout;
117 gint scroll_indicator_timeout;
118 gint scroll_indicator_event_interrupt;
119 gint scroll_delay_counter;
122 gboolean initial_hint;
123 gboolean initial_effect;
124 gboolean low_friction_mode;
127 gboolean size_request_policy;
128 gboolean hscroll_visible;
129 gboolean vscroll_visible;
130 GdkRectangle hscroll_rect;
131 GdkRectangle vscroll_rect;
132 guint indicator_width;
134 GtkAdjustment *hadjust;
135 GtkAdjustment *vadjust;
137 GtkPolicyType vscrollbar_policy;
138 GtkPolicyType hscrollbar_policy;
140 GdkGC *scrollbars_gc;
150 static guint pannable_area_signals [LAST_SIGNAL] = { 0 };
158 PROP_VEL_MAX_OVERSHOOTING,
159 PROP_VELOCITY_FAST_FACTOR,
163 PROP_PANNING_THRESHOLD,
164 PROP_SCROLLBAR_FADE_DELAY,
167 PROP_DIRECTION_ERROR_MARGIN,
168 PROP_VSCROLLBAR_POLICY,
169 PROP_HSCROLLBAR_POLICY,
174 PROP_LOW_FRICTION_MODE,
175 PROP_SIZE_REQUEST_POLICY,
181 static void hildon_pannable_area_class_init (HildonPannableAreaClass * klass);
182 static void hildon_pannable_area_init (HildonPannableArea * area);
183 static void hildon_pannable_area_get_property (GObject * object,
187 static void hildon_pannable_area_set_property (GObject * object,
189 const GValue * value,
191 static void hildon_pannable_area_dispose (GObject * object);
192 static void hildon_pannable_area_realize (GtkWidget * widget);
193 static void hildon_pannable_area_unrealize (GtkWidget * widget);
194 static void hildon_pannable_area_size_request (GtkWidget * widget,
195 GtkRequisition * requisition);
196 static void hildon_pannable_area_size_allocate (GtkWidget * widget,
197 GtkAllocation * allocation);
198 static void hildon_pannable_area_child_allocate_calculate (GtkWidget * widget,
199 GtkAllocation * allocation,
200 GtkAllocation * child_allocation);
201 static void hildon_pannable_area_style_set (GtkWidget * widget,
202 GtkStyle * previous_style);
203 static void hildon_pannable_area_map (GtkWidget * widget);
204 static void hildon_pannable_area_unmap (GtkWidget * widget);
205 static void hildon_pannable_area_grab_notify (GtkWidget *widget,
206 gboolean was_grabbed,
208 #if USE_CAIRO_SCROLLBARS == 1
209 static void rgb_from_gdkcolor (GdkColor *color, gdouble *r, gdouble *g, gdouble *b);
210 #else /* USE_CAIRO_SCROLLBARS */
211 static void tranparency_color (GdkColor *color,
214 gdouble transparency);
215 #endif /* USE_CAIRO_SCROLLBARS */
216 static void hildon_pannable_draw_vscroll (GtkWidget * widget,
217 GdkColor *back_color,
218 GdkColor *scroll_color);
219 static void hildon_pannable_draw_hscroll (GtkWidget * widget,
220 GdkColor *back_color,
221 GdkColor *scroll_color);
222 static void hildon_pannable_area_initial_effect (GtkWidget * widget);
223 static void hildon_pannable_area_redraw (HildonPannableArea * area);
224 static void hildon_pannable_area_launch_fade_timeout (HildonPannableArea * area,
226 static void hildon_pannable_area_adjust_value_changed (HildonPannableArea * area,
228 static void hildon_pannable_area_adjust_changed (HildonPannableArea * area,
230 static gboolean hildon_pannable_area_scroll_indicator_fade(HildonPannableArea * area);
231 static gboolean hildon_pannable_area_expose_event (GtkWidget * widget,
232 GdkEventExpose * event);
233 static GdkWindow * hildon_pannable_area_get_topmost (GdkWindow * window,
235 gint * tx, gint * ty,
237 static void synth_crossing (GdkWindow * child,
239 gint x_root, gint y_root,
240 guint32 time, gboolean in);
241 static gboolean hildon_pannable_area_button_press_cb (GtkWidget * widget,
242 GdkEventButton * event);
243 static void hildon_pannable_area_refresh (HildonPannableArea * area);
244 static gboolean hildon_pannable_area_check_scrollbars (HildonPannableArea * area);
245 static void hildon_pannable_axis_scroll (HildonPannableArea *area,
246 GtkAdjustment *adjust,
254 static void hildon_pannable_area_scroll (HildonPannableArea *area,
255 gdouble x, gdouble y);
256 static gboolean hildon_pannable_area_timeout (HildonPannableArea * area);
257 static void hildon_pannable_area_calculate_velocity (gdouble *vel,
261 gdouble drag_inertia,
264 static gboolean hildon_pannable_area_motion_event_scroll_timeout (HildonPannableArea *area);
265 static void hildon_pannable_area_motion_event_scroll (HildonPannableArea *area,
266 gdouble x, gdouble y);
267 static gboolean hildon_pannable_area_motion_notify_cb (GtkWidget * widget,
268 GdkEventMotion * event);
269 static gboolean hildon_pannable_leave_notify_event (GtkWidget *widget,
270 GdkEventCrossing *event);
271 static gboolean hildon_pannable_area_button_release_cb (GtkWidget * widget,
272 GdkEventButton * event);
273 static gboolean hildon_pannable_area_scroll_cb (GtkWidget *widget,
274 GdkEventScroll *event);
275 static void hildon_pannable_area_child_mapped (GtkWidget *widget,
278 static void hildon_pannable_area_add (GtkContainer *container, GtkWidget *child);
279 static void hildon_pannable_area_remove (GtkContainer *container, GtkWidget *child);
280 static void hildon_pannable_calculate_vel_factor (HildonPannableArea * self);
284 hildon_pannable_area_class_init (HildonPannableAreaClass * klass)
286 GObjectClass *object_class = G_OBJECT_CLASS (klass);
287 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
288 GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
291 g_type_class_add_private (klass, sizeof (HildonPannableAreaPrivate));
293 object_class->get_property = hildon_pannable_area_get_property;
294 object_class->set_property = hildon_pannable_area_set_property;
295 object_class->dispose = hildon_pannable_area_dispose;
297 widget_class->realize = hildon_pannable_area_realize;
298 widget_class->unrealize = hildon_pannable_area_unrealize;
299 widget_class->map = hildon_pannable_area_map;
300 widget_class->unmap = hildon_pannable_area_unmap;
301 widget_class->size_request = hildon_pannable_area_size_request;
302 widget_class->size_allocate = hildon_pannable_area_size_allocate;
303 widget_class->expose_event = hildon_pannable_area_expose_event;
304 widget_class->style_set = hildon_pannable_area_style_set;
305 widget_class->button_press_event = hildon_pannable_area_button_press_cb;
306 widget_class->button_release_event = hildon_pannable_area_button_release_cb;
307 widget_class->motion_notify_event = hildon_pannable_area_motion_notify_cb;
308 widget_class->leave_notify_event = hildon_pannable_leave_notify_event;
309 widget_class->scroll_event = hildon_pannable_area_scroll_cb;
311 container_class->add = hildon_pannable_area_add;
312 container_class->remove = hildon_pannable_area_remove;
314 klass->horizontal_movement = NULL;
315 klass->vertical_movement = NULL;
317 g_object_class_install_property (object_class,
319 g_param_spec_boolean ("enabled",
321 "Enable or disable finger-scroll.",
326 g_object_class_install_property (object_class,
327 PROP_VSCROLLBAR_POLICY,
328 g_param_spec_enum ("vscrollbar_policy",
330 "Visual policy of the vertical scrollbar",
331 GTK_TYPE_POLICY_TYPE,
332 GTK_POLICY_AUTOMATIC,
336 g_object_class_install_property (object_class,
337 PROP_HSCROLLBAR_POLICY,
338 g_param_spec_enum ("hscrollbar_policy",
340 "Visual policy of the horizontal scrollbar",
341 GTK_TYPE_POLICY_TYPE,
342 GTK_POLICY_AUTOMATIC,
346 g_object_class_install_property (object_class,
348 g_param_spec_enum ("mode",
350 "Change the finger-scrolling mode.",
351 HILDON_TYPE_PANNABLE_AREA_MODE,
352 HILDON_PANNABLE_AREA_MODE_AUTO,
356 g_object_class_install_property (object_class,
358 g_param_spec_flags ("mov_mode",
359 "Scroll movement mode",
360 "Controls if the widget can scroll vertically, horizontally or both",
361 HILDON_TYPE_MOVEMENT_MODE,
362 HILDON_MOVEMENT_MODE_VERT,
366 g_object_class_install_property (object_class,
368 g_param_spec_double ("velocity_min",
369 "Minimum scroll velocity",
370 "Minimum distance the child widget should scroll "
371 "per 'frame', in pixels per frame.",
376 g_object_class_install_property (object_class,
378 g_param_spec_double ("velocity_max",
379 "Maximum scroll velocity",
380 "Maximum distance the child widget should scroll "
381 "per 'frame', in pixels per frame.",
386 g_object_class_install_property (object_class,
387 PROP_VEL_MAX_OVERSHOOTING,
388 g_param_spec_double ("velocity_overshooting_max",
389 "Maximum scroll velocity when overshooting",
390 "Maximum distance the child widget should scroll "
391 "per 'frame', in pixels per frame when it overshoots after hitting the edge.",
396 g_object_class_install_property (object_class,
397 PROP_VELOCITY_FAST_FACTOR,
398 g_param_spec_double ("velocity_fast_factor",
399 "Fast velocity factor",
400 "Minimum velocity that is considered 'fast': "
401 "children widgets won't receive button presses. "
402 "Expressed as a fraction of the maximum velocity.",
407 g_object_class_install_property (object_class,
409 g_param_spec_double ("deceleration",
410 "Deceleration multiplier",
411 "The multiplier used when decelerating when in "
412 "acceleration scrolling mode.",
417 g_object_class_install_property (object_class,
419 g_param_spec_double ("drag_inertia",
420 "Inertia of the cursor dragging",
421 "Percentage of the calculated speed in each moment we are are going to use"
422 "to calculate the launch speed, the other part would be the speed"
423 "calculated previously",
428 g_object_class_install_property (object_class,
430 g_param_spec_uint ("sps",
431 "Scrolls per second",
432 "Amount of scroll events to generate per second.",
437 g_object_class_install_property (object_class,
438 PROP_PANNING_THRESHOLD,
439 g_param_spec_uint ("panning_threshold",
440 "Threshold to consider a motion event an scroll",
441 "Amount of pixels to consider a motion event an scroll, if it is less"
442 "it is a click detected incorrectly by the touch screen.",
447 g_object_class_install_property (object_class,
448 PROP_SCROLLBAR_FADE_DELAY,
449 g_param_spec_uint ("scrollbar_fade_delay",
450 "Time before starting to fade the scrollbar",
451 "Time the scrollbar is going to be visible if the widget is not in"
452 "action in miliseconds",
457 g_object_class_install_property (object_class,
459 g_param_spec_uint ("bounce_steps",
461 "Number of steps that is going to be used to bounce when hitting the"
462 "edge, the rubberband effect depends on it",
467 g_object_class_install_property (object_class,
469 g_param_spec_uint ("force",
470 "Multiplier of the calculated speed",
471 "Force applied to the movement, multiplies the calculated speed of the"
472 "user movement the cursor in the screen",
477 g_object_class_install_property (object_class,
478 PROP_DIRECTION_ERROR_MARGIN,
479 g_param_spec_uint ("direction_error_margin",
480 "Margin in the direction detection",
481 "After detecting the direction of the movement (horizontal or"
482 "vertical), we can add this margin of error to allow the movement in"
483 "the other direction even apparently it is not",
488 g_object_class_install_property (object_class,
490 g_param_spec_int ("vovershoot_max",
491 "Vertical overshoot distance",
492 "Space we allow the widget to pass over its vertical limits when"
493 "hitting the edges, set 0 in order to deactivate overshooting.",
498 g_object_class_install_property (object_class,
500 g_param_spec_int ("hovershoot_max",
501 "Horizontal overshoot distance",
502 "Space we allow the widget to pass over its horizontal limits when"
503 "hitting the edges, set 0 in order to deactivate overshooting.",
508 g_object_class_install_property (object_class,
510 g_param_spec_double ("scroll_time",
511 "Time to scroll to a position",
512 "The time to scroll to a position when calling the hildon_pannable_scroll_to function",
517 g_object_class_install_property (object_class,
519 g_param_spec_boolean ("initial-hint",
521 "Whether to hint the user about the pannability of the container.",
526 g_object_class_install_property (object_class,
527 PROP_LOW_FRICTION_MODE,
528 g_param_spec_boolean ("low-friction-mode",
529 "Do not decelerate the initial velocity",
530 "Avoid decelerating the panning movement, like no friction, the widget"
531 "will stop in the edges or if the user clicks.",
536 g_object_class_install_property (object_class,
537 PROP_SIZE_REQUEST_POLICY,
538 g_param_spec_enum ("size-request-policy",
539 "Size Requisition policy",
540 "Controls the size request policy of the widget",
541 HILDON_TYPE_SIZE_REQUEST_POLICY,
542 HILDON_SIZE_REQUEST_MINIMUM,
546 g_object_class_install_property (object_class,
548 g_param_spec_object ("hadjustment",
549 "Horizontal Adjustment",
550 "The GtkAdjustment for the horizontal position",
553 g_object_class_install_property (object_class,
555 g_param_spec_object ("vadjustment",
556 "Vertical Adjustment",
557 "The GtkAdjustment for the vertical position",
561 gtk_widget_class_install_style_property (widget_class,
564 "Width of the scroll indicators",
565 "Pixel width used to draw the scroll indicators.",
570 * HildonPannableArea::horizontal-movement:
571 * @hildonpannable: the object which received the signal
572 * @direction: the direction of the movement #HILDON_MOVEMENT_LEFT or #HILDON_MOVEMENT_RIGHT
573 * @initial_x: the x coordinate of the point where the user clicked to start the movement
574 * @initial_y: the y coordinate of the point where the user clicked to start the movement
576 * The horizontal-movement signal is emitted when the pannable area
577 * detects a horizontal movement. The detection does not mean the
578 * widget is going to move (i.e. maybe the children are smaller
579 * horizontally than the screen).
583 pannable_area_signals[HORIZONTAL_MOVEMENT] =
584 g_signal_new ("horizontal_movement",
585 G_TYPE_FROM_CLASS (object_class),
586 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
587 G_STRUCT_OFFSET (HildonPannableAreaClass, horizontal_movement),
589 _hildon_marshal_VOID__INT_DOUBLE_DOUBLE,
596 * HildonPannableArea::vertical-movement:
597 * @hildonpannable: the object which received the signal
598 * @direction: the direction of the movement #HILDON_MOVEMENT_UP or #HILDON_MOVEMENT_DOWN
599 * @initial_x: the x coordinate of the point where the user clicked to start the movement
600 * @initial_y: the y coordinate of the point where the user clicked to start the movement
602 * The vertical-movement signal is emitted when the pannable area
603 * detects a vertical movement. The detection does not mean the
604 * widget is going to move (i.e. maybe the children are smaller
605 * vertically than the screen).
609 pannable_area_signals[VERTICAL_MOVEMENT] =
610 g_signal_new ("vertical_movement",
611 G_TYPE_FROM_CLASS (object_class),
612 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
613 G_STRUCT_OFFSET (HildonPannableAreaClass, vertical_movement),
615 _hildon_marshal_VOID__INT_DOUBLE_DOUBLE,
624 hildon_pannable_area_init (HildonPannableArea * area)
626 HildonPannableAreaPrivate *priv = PANNABLE_AREA_PRIVATE (area);
628 GTK_WIDGET_UNSET_FLAGS (area, GTK_NO_WINDOW);
633 priv->button_pressed = FALSE;
636 priv->vscroll_visible = TRUE;
637 priv->hscroll_visible = TRUE;
638 priv->indicator_width = 6;
639 priv->overshot_dist_x = 0;
640 priv->overshot_dist_y = 0;
641 priv->overshooting_y = 0;
642 priv->overshooting_x = 0;
646 priv->scroll_indicator_alpha = 0.0;
647 priv->scroll_indicator_timeout = 0;
648 priv->motion_event_scroll_timeout = 0;
649 priv->scroll_indicator_event_interrupt = 0;
650 priv->scroll_delay_counter = 0;
651 priv->scrollbar_fade_delay = 0;
652 priv->scroll_to_x = -1;
653 priv->scroll_to_y = -1;
654 priv->first_drag = TRUE;
655 priv->initial_effect = TRUE;
656 priv->child_width = 0;
657 priv->child_height = 0;
658 priv->last_in = TRUE;
660 gtk_widget_add_events (GTK_WIDGET (area), GDK_POINTER_MOTION_HINT_MASK);
663 GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
665 GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
667 g_object_ref_sink (G_OBJECT (priv->hadjust));
668 g_object_ref_sink (G_OBJECT (priv->vadjust));
670 g_signal_connect_swapped (priv->hadjust, "value-changed",
671 G_CALLBACK (hildon_pannable_area_adjust_value_changed), area);
672 g_signal_connect_swapped (priv->vadjust, "value-changed",
673 G_CALLBACK (hildon_pannable_area_adjust_value_changed), area);
674 g_signal_connect_swapped (priv->hadjust, "changed",
675 G_CALLBACK (hildon_pannable_area_adjust_changed), area);
676 g_signal_connect_swapped (priv->vadjust, "changed",
677 G_CALLBACK (hildon_pannable_area_adjust_changed), area);
678 g_signal_connect (area, "grab-notify",
679 G_CALLBACK (hildon_pannable_area_grab_notify), NULL);
683 hildon_pannable_area_get_property (GObject * object,
688 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (object)->priv;
690 switch (property_id) {
692 g_value_set_boolean (value, priv->enabled);
695 g_value_set_enum (value, priv->mode);
697 case PROP_MOVEMENT_MODE:
698 g_value_set_flags (value, priv->mov_mode);
700 case PROP_VELOCITY_MIN:
701 g_value_set_double (value, priv->vmin);
703 case PROP_VELOCITY_MAX:
704 g_value_set_double (value, priv->vmax);
706 case PROP_VEL_MAX_OVERSHOOTING:
707 g_value_set_double (value, priv->vmax_overshooting);
709 case PROP_VELOCITY_FAST_FACTOR:
710 g_value_set_double (value, priv->vfast_factor);
712 case PROP_DECELERATION:
713 g_value_set_double (value, priv->decel);
715 case PROP_DRAG_INERTIA:
716 g_value_set_double (value, priv->drag_inertia);
719 g_value_set_uint (value, priv->sps);
721 case PROP_PANNING_THRESHOLD:
722 g_value_set_uint (value, priv->panning_threshold);
724 case PROP_SCROLLBAR_FADE_DELAY:
725 /* convert to miliseconds */
726 g_value_set_uint (value, priv->scrollbar_fade_delay * SCROLL_FADE_TIMEOUT);
728 case PROP_BOUNCE_STEPS:
729 g_value_set_uint (value, priv->bounce_steps);
732 g_value_set_uint (value, priv->force);
734 case PROP_DIRECTION_ERROR_MARGIN:
735 g_value_set_uint (value, priv->direction_error_margin);
737 case PROP_VSCROLLBAR_POLICY:
738 g_value_set_enum (value, priv->vscrollbar_policy);
740 case PROP_HSCROLLBAR_POLICY:
741 g_value_set_enum (value, priv->hscrollbar_policy);
743 case PROP_VOVERSHOOT_MAX:
744 g_value_set_int (value, priv->vovershoot_max);
746 case PROP_HOVERSHOOT_MAX:
747 g_value_set_int (value, priv->hovershoot_max);
749 case PROP_SCROLL_TIME:
750 g_value_set_double (value, priv->scroll_time);
752 case PROP_INITIAL_HINT:
753 g_value_set_boolean (value, priv->initial_hint);
755 case PROP_LOW_FRICTION_MODE:
756 g_value_set_boolean (value, priv->low_friction_mode);
758 case PROP_SIZE_REQUEST_POLICY:
759 g_value_set_enum (value, priv->size_request_policy);
761 case PROP_HADJUSTMENT:
762 g_value_set_object (value,
763 hildon_pannable_area_get_hadjustment
764 (HILDON_PANNABLE_AREA (object)));
766 case PROP_VADJUSTMENT:
767 g_value_set_object (value,
768 hildon_pannable_area_get_vadjustment
769 (HILDON_PANNABLE_AREA (object)));
772 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
777 hildon_pannable_area_set_property (GObject * object,
779 const GValue * value,
782 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (object)->priv;
785 switch (property_id) {
787 enabled = g_value_get_boolean (value);
789 if ((priv->enabled != enabled) && (GTK_WIDGET_REALIZED (object))) {
791 gdk_window_raise (priv->event_window);
793 gdk_window_lower (priv->event_window);
796 priv->enabled = enabled;
799 priv->mode = g_value_get_enum (value);
801 case PROP_MOVEMENT_MODE:
802 priv->mov_mode = g_value_get_flags (value);
804 case PROP_VELOCITY_MIN:
805 priv->vmin = g_value_get_double (value);
807 case PROP_VELOCITY_MAX:
808 priv->vmax = g_value_get_double (value);
810 case PROP_VEL_MAX_OVERSHOOTING:
811 priv->vmax_overshooting = g_value_get_double (value);
813 case PROP_VELOCITY_FAST_FACTOR:
814 priv->vfast_factor = g_value_get_double (value);
816 case PROP_DECELERATION:
817 priv->decel = g_value_get_double (value);
818 hildon_pannable_calculate_vel_factor (HILDON_PANNABLE_AREA (object));
820 case PROP_DRAG_INERTIA:
821 priv->drag_inertia = g_value_get_double (value);
824 priv->sps = g_value_get_uint (value);
826 case PROP_PANNING_THRESHOLD:
827 priv->panning_threshold = g_value_get_uint (value);
829 case PROP_SCROLLBAR_FADE_DELAY:
830 /* convert to miliseconds */
831 priv->scrollbar_fade_delay = g_value_get_uint (value)/(SCROLL_FADE_TIMEOUT);
833 case PROP_BOUNCE_STEPS:
834 priv->bounce_steps = g_value_get_uint (value);
837 priv->force = g_value_get_uint (value);
839 case PROP_DIRECTION_ERROR_MARGIN:
840 priv->direction_error_margin = g_value_get_uint (value);
842 case PROP_VSCROLLBAR_POLICY:
843 priv->vscrollbar_policy = g_value_get_enum (value);
845 gtk_widget_queue_resize (GTK_WIDGET (object));
847 case PROP_HSCROLLBAR_POLICY:
848 priv->hscrollbar_policy = g_value_get_enum (value);
850 gtk_widget_queue_resize (GTK_WIDGET (object));
852 case PROP_VOVERSHOOT_MAX:
853 priv->vovershoot_max = g_value_get_int (value);
855 case PROP_HOVERSHOOT_MAX:
856 priv->hovershoot_max = g_value_get_int (value);
858 case PROP_SCROLL_TIME:
859 priv->scroll_time = g_value_get_double (value);
861 hildon_pannable_calculate_vel_factor (HILDON_PANNABLE_AREA (object));
863 case PROP_INITIAL_HINT:
864 priv->initial_hint = g_value_get_boolean (value);
866 case PROP_LOW_FRICTION_MODE:
867 priv->low_friction_mode = g_value_get_boolean (value);
869 case PROP_SIZE_REQUEST_POLICY:
870 hildon_pannable_area_set_size_request_policy (HILDON_PANNABLE_AREA (object),
871 g_value_get_enum (value));
875 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
880 hildon_pannable_area_dispose (GObject * object)
882 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (object)->priv;
883 GtkWidget *child = gtk_bin_get_child (GTK_BIN (object));
886 g_source_remove (priv->idle_id);
890 if (priv->scroll_indicator_timeout){
891 g_source_remove (priv->scroll_indicator_timeout);
892 priv->scroll_indicator_timeout = 0;
895 if (priv->motion_event_scroll_timeout){
896 g_source_remove (priv->motion_event_scroll_timeout);
897 priv->motion_event_scroll_timeout = 0;
901 g_signal_handlers_disconnect_by_func (child,
902 hildon_pannable_area_child_mapped,
906 g_signal_handlers_disconnect_by_func (object,
907 hildon_pannable_area_grab_notify,
911 g_signal_handlers_disconnect_by_func (priv->hadjust,
912 hildon_pannable_area_adjust_value_changed,
914 g_signal_handlers_disconnect_by_func (priv->hadjust,
915 hildon_pannable_area_adjust_changed,
917 g_object_unref (priv->hadjust);
918 priv->hadjust = NULL;
922 g_signal_handlers_disconnect_by_func (priv->vadjust,
923 hildon_pannable_area_adjust_value_changed,
925 g_signal_handlers_disconnect_by_func (priv->vadjust,
926 hildon_pannable_area_adjust_changed,
928 g_object_unref (priv->vadjust);
929 priv->vadjust = NULL;
932 if (G_OBJECT_CLASS (hildon_pannable_area_parent_class)->dispose)
933 G_OBJECT_CLASS (hildon_pannable_area_parent_class)->dispose (object);
937 hildon_pannable_area_realize (GtkWidget * widget)
939 GdkWindowAttr attributes;
940 gint attributes_mask;
942 HildonPannableAreaPrivate *priv;
944 priv = HILDON_PANNABLE_AREA (widget)->priv;
946 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
948 border_width = GTK_CONTAINER (widget)->border_width;
950 attributes.x = widget->allocation.x + border_width;
951 attributes.y = widget->allocation.y + border_width;
952 attributes.width = MAX (widget->allocation.width - 2 * border_width, 0);
953 attributes.height = MAX (widget->allocation.height - 2 * border_width, 0);
954 attributes.window_type = GDK_WINDOW_CHILD;
956 /* avoid using the hildon_window */
957 attributes.visual = gtk_widget_get_visual (widget);
958 attributes.colormap = gtk_widget_get_colormap (widget);
959 attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK;
960 attributes.wclass = GDK_INPUT_OUTPUT;
962 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
964 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
965 &attributes, attributes_mask);
966 gdk_window_set_user_data (widget->window, widget);
968 /* create the events window */
971 attributes.event_mask = gtk_widget_get_events (widget)
972 | GDK_BUTTON_MOTION_MASK
973 | GDK_BUTTON_PRESS_MASK
974 | GDK_BUTTON_RELEASE_MASK
976 | GDK_EXPOSURE_MASK | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK;
977 attributes.wclass = GDK_INPUT_ONLY;
979 attributes_mask = GDK_WA_X | GDK_WA_Y;
981 priv->event_window = gdk_window_new (widget->window,
982 &attributes, attributes_mask);
983 gdk_window_set_user_data (priv->event_window, widget);
985 widget->style = gtk_style_attach (widget->style, widget->window);
986 gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
988 priv->scrollbars_gc = gdk_gc_new (GDK_DRAWABLE (widget->window));
989 gdk_gc_copy (priv->scrollbars_gc, widget->style->fg_gc[GTK_STATE_INSENSITIVE]);
993 hildon_pannable_area_unrealize (GtkWidget * widget)
995 HildonPannableAreaPrivate *priv;
997 priv = HILDON_PANNABLE_AREA (widget)->priv;
999 if (priv->event_window != NULL) {
1000 gdk_window_set_user_data (priv->event_window, NULL);
1001 gdk_window_destroy (priv->event_window);
1002 priv->event_window = NULL;
1005 gdk_gc_unref (priv->scrollbars_gc);
1007 if (GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->unrealize)
1008 (*GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->unrealize)(widget);
1012 hildon_pannable_area_size_request (GtkWidget * widget,
1013 GtkRequisition * requisition)
1015 GtkRequisition child_requisition = {0};
1016 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1017 GtkWidget *child = gtk_bin_get_child (GTK_BIN (widget));
1019 if (child && GTK_WIDGET_VISIBLE (child))
1021 gtk_widget_size_request (child, &child_requisition);
1024 if (priv->hscrollbar_policy == GTK_POLICY_NEVER) {
1025 requisition->width = child_requisition.width;
1027 switch (priv->size_request_policy) {
1028 case HILDON_SIZE_REQUEST_CHILDREN:
1029 requisition->width = MIN (PANNABLE_MAX_WIDTH,
1030 child_requisition.width);
1032 case HILDON_SIZE_REQUEST_MINIMUM:
1034 requisition->width = priv->indicator_width;
1038 if (priv->vscrollbar_policy == GTK_POLICY_NEVER) {
1039 requisition->height = child_requisition.height;
1041 switch (priv->size_request_policy) {
1042 case HILDON_SIZE_REQUEST_CHILDREN:
1043 requisition->height = MIN (PANNABLE_MAX_HEIGHT,
1044 child_requisition.height);
1046 case HILDON_SIZE_REQUEST_MINIMUM:
1048 requisition->height = priv->indicator_width;
1052 requisition->width += 2 * GTK_CONTAINER (widget)->border_width;
1053 requisition->height += 2 * GTK_CONTAINER (widget)->border_width;
1057 hildon_pannable_area_child_allocate_calculate (GtkWidget * widget,
1058 GtkAllocation * allocation,
1059 GtkAllocation * child_allocation)
1062 HildonPannableAreaPrivate *priv;
1064 border_width = GTK_CONTAINER (widget)->border_width;
1066 priv = HILDON_PANNABLE_AREA (widget)->priv;
1068 child_allocation->x = 0;
1069 child_allocation->y = 0;
1070 child_allocation->width = MAX (allocation->width - 2 * border_width -
1071 (priv->vscroll_visible ? priv->vscroll_rect.width : 0), 0);
1072 child_allocation->height = MAX (allocation->height - 2 * border_width -
1073 (priv->hscroll_visible ? priv->hscroll_rect.height : 0), 0);
1075 if (priv->overshot_dist_y > 0) {
1076 child_allocation->y = MIN (child_allocation->y + priv->overshot_dist_y,
1077 child_allocation->height);
1078 child_allocation->height = MAX (child_allocation->height - priv->overshot_dist_y, 0);
1079 } else if (priv->overshot_dist_y < 0) {
1080 child_allocation->height = MAX (child_allocation->height + priv->overshot_dist_y, 0);
1083 if (priv->overshot_dist_x > 0) {
1084 child_allocation->x = MIN (child_allocation->x + priv->overshot_dist_x,
1085 child_allocation->width);
1086 child_allocation->width = MAX (child_allocation->width - priv->overshot_dist_x, 0);
1087 } else if (priv->overshot_dist_x < 0) {
1088 child_allocation->width = MAX (child_allocation->width + priv->overshot_dist_x, 0);
1093 hildon_pannable_area_size_allocate (GtkWidget * widget,
1094 GtkAllocation * allocation)
1096 GtkAllocation child_allocation;
1097 HildonPannableAreaPrivate *priv;
1098 GtkWidget *child = gtk_bin_get_child (GTK_BIN (widget));
1101 border_width = GTK_CONTAINER (widget)->border_width;
1103 widget->allocation = *allocation;
1105 priv = HILDON_PANNABLE_AREA (widget)->priv;
1107 if (GTK_WIDGET_REALIZED (widget)) {
1108 gdk_window_move_resize (widget->window,
1109 allocation->x + border_width,
1110 allocation->y + border_width,
1111 allocation->width - border_width * 2,
1112 allocation->height - border_width * 2);
1113 gdk_window_move_resize (priv->event_window,
1116 allocation->width - border_width * 2,
1117 allocation->height - border_width * 2);
1120 if (child && GTK_WIDGET_VISIBLE (child)) {
1122 hildon_pannable_area_child_allocate_calculate (widget,
1126 gtk_widget_size_allocate (child, &child_allocation);
1128 if (hildon_pannable_area_check_scrollbars (HILDON_PANNABLE_AREA (widget))) {
1129 hildon_pannable_area_child_allocate_calculate (widget,
1133 gtk_widget_size_allocate (child, &child_allocation);
1136 /* we have to do this after child size_allocate because page_size is
1137 * changed when we allocate the size of the children */
1138 if (priv->overshot_dist_y < 0) {
1139 gtk_adjustment_set_value (priv->vadjust, priv->vadjust->upper -
1140 priv->vadjust->page_size);
1143 if (priv->overshot_dist_x < 0) {
1144 gtk_adjustment_set_value (priv->hadjust, priv->hadjust->upper -
1145 priv->hadjust->page_size);
1149 hildon_pannable_area_check_scrollbars (HILDON_PANNABLE_AREA (widget));
1154 hildon_pannable_area_style_set (GtkWidget * widget,
1155 GtkStyle * previous_style)
1157 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1159 GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->
1160 style_set (widget, previous_style);
1162 gtk_widget_style_get (widget, "indicator-width", &priv->indicator_width, NULL);
1166 hildon_pannable_area_map (GtkWidget * widget)
1168 HildonPannableAreaPrivate *priv;
1170 priv = HILDON_PANNABLE_AREA (widget)->priv;
1172 gdk_window_show (widget->window);
1174 if (priv->event_window != NULL && !priv->enabled)
1175 gdk_window_show (priv->event_window);
1177 (*GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->map) (widget);
1179 if (priv->event_window != NULL && priv->enabled)
1180 gdk_window_show (priv->event_window);
1184 hildon_pannable_area_unmap (GtkWidget * widget)
1186 HildonPannableAreaPrivate *priv;
1188 priv = HILDON_PANNABLE_AREA (widget)->priv;
1190 if (priv->event_window != NULL)
1191 gdk_window_hide (priv->event_window);
1193 gdk_window_hide (widget->window);
1195 (*GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->unmap) (widget);
1199 hildon_pannable_area_grab_notify (GtkWidget *widget,
1200 gboolean was_grabbed,
1203 /* an internal widget has grabbed the focus and now has returned it,
1204 we have to do some release actions */
1206 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1208 priv->scroll_indicator_event_interrupt = 0;
1210 if ((!priv->scroll_indicator_timeout)&&(priv->scroll_indicator_alpha)>0.1) {
1211 priv->scroll_delay_counter = priv->scrollbar_fade_delay;
1213 hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget),
1214 priv->scroll_indicator_alpha);
1217 priv->last_type = 3;
1218 priv->moved = FALSE;
1222 #if USE_CAIRO_SCROLLBARS == 1
1225 rgb_from_gdkcolor (GdkColor *color, gdouble *r, gdouble *g, gdouble *b)
1227 *r = (color->red >> 8) / 255.0;
1228 *g = (color->green >> 8) / 255.0;
1229 *b = (color->blue >> 8) / 255.0;
1233 hildon_pannable_draw_vscroll (GtkWidget * widget,
1234 GdkColor *back_color,
1235 GdkColor *scroll_color)
1237 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1240 cairo_pattern_t *pattern;
1242 gint radius = (priv->vscroll_rect.width/2) - 1;
1244 cr = gdk_cairo_create(widget->window);
1246 /* Draw the background */
1247 rgb_from_gdkcolor (back_color, &r, &g, &b);
1248 cairo_set_source_rgb (cr, r, g, b);
1249 cairo_rectangle(cr, priv->vscroll_rect.x, priv->vscroll_rect.y,
1250 priv->vscroll_rect.width,
1251 priv->vscroll_rect.height);
1252 cairo_fill_preserve (cr);
1255 /* Calculate the scroll bar height and position */
1256 y = ((priv->vadjust->value - priv->vadjust->lower) / (priv->vadjust->upper - priv->vadjust->lower)) *
1257 (widget->allocation.height -
1258 (priv->hscroll_visible ? priv->indicator_width : 0));
1259 height = ((((priv->vadjust->value - priv->vadjust->lower) +
1260 priv->vadjust->page_size) /
1261 (priv->vadjust->upper - priv->vadjust->lower)) *
1262 (widget->allocation.height -
1263 (priv->hscroll_visible ? priv->indicator_width : 0))) - y;
1265 /* Set a minimum height */
1266 height = MAX (SCROLL_BAR_MIN_SIZE, height);
1268 /* Check the max y position */
1269 y = MIN (y, widget->allocation.height -
1270 (priv->hscroll_visible ? priv->hscroll_rect.height : 0) -
1273 /* Draw the scrollbar */
1274 rgb_from_gdkcolor (scroll_color, &r, &g, &b);
1276 pattern = cairo_pattern_create_linear(radius+1, y, radius+1,y + height);
1277 cairo_pattern_add_color_stop_rgb(pattern, 0, r, g, b);
1278 cairo_pattern_add_color_stop_rgb(pattern, 1, r/2, g/2, b/2);
1279 cairo_set_source(cr, pattern);
1281 cairo_pattern_destroy(pattern);
1283 cairo_arc(cr, priv->vscroll_rect.x + radius + 1, y + radius + 1, radius, G_PI, 0);
1284 cairo_line_to(cr, priv->vscroll_rect.x + (radius * 2) + 1, y + height - radius);
1285 cairo_arc(cr, priv->vscroll_rect.x + radius + 1, y + height - radius, radius, 0, G_PI);
1286 cairo_line_to(cr, priv->vscroll_rect.x + 1, y + height - radius);
1289 cairo_paint_with_alpha(cr, priv->scroll_indicator_alpha);
1295 hildon_pannable_draw_hscroll (GtkWidget * widget,
1296 GdkColor *back_color,
1297 GdkColor *scroll_color)
1299 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1302 cairo_pattern_t *pattern;
1304 gint radius = (priv->hscroll_rect.height/2) - 1;
1306 cr = gdk_cairo_create(widget->window);
1308 /* Draw the background */
1309 rgb_from_gdkcolor (back_color, &r, &g, &b);
1310 cairo_set_source_rgb (cr, r, g, b);
1311 cairo_rectangle(cr, priv->hscroll_rect.x, priv->hscroll_rect.y,
1312 priv->hscroll_rect.width,
1313 priv->hscroll_rect.height);
1314 cairo_fill_preserve (cr);
1317 /* calculate the scrollbar width and position */
1318 x = ((priv->hadjust->value - priv->hadjust->lower) / (priv->hadjust->upper - priv->hadjust->lower)) *
1319 (widget->allocation.width - (priv->vscroll_visible ? priv->indicator_width : 0));
1320 width =((((priv->hadjust->value - priv->hadjust->lower) +
1321 priv->hadjust->page_size) / (priv->hadjust->upper - priv->hadjust->lower)) *
1322 (widget->allocation.width -
1323 (priv->vscroll_visible ? priv->indicator_width : 0))) - x;
1325 /* Set a minimum width */
1326 width = MAX (SCROLL_BAR_MIN_SIZE, width);
1328 /* Check the max x position */
1329 x = MIN (x, widget->allocation.width -
1330 (priv->vscroll_visible ? priv->vscroll_rect.width : 0) -
1333 /* Draw the scrollbar */
1334 rgb_from_gdkcolor (scroll_color, &r, &g, &b);
1336 pattern = cairo_pattern_create_linear(x, radius+1, x+width, radius+1);
1337 cairo_pattern_add_color_stop_rgb(pattern, 0, r, g, b);
1338 cairo_pattern_add_color_stop_rgb(pattern, 1, r/2, g/2, b/2);
1339 cairo_set_source(cr, pattern);
1341 cairo_pattern_destroy(pattern);
1343 cairo_arc_negative(cr, x + radius + 1, priv->hscroll_rect.y + radius + 1, radius, 3*G_PI_2, G_PI_2);
1344 cairo_line_to(cr, x + width - radius, priv->hscroll_rect.y + (radius * 2) + 1);
1345 cairo_arc_negative(cr, x + width - radius, priv->hscroll_rect.y + radius + 1, radius, G_PI_2, 3*G_PI_2);
1346 cairo_line_to(cr, x + width - radius, priv->hscroll_rect.y + 1);
1349 cairo_paint_with_alpha(cr, priv->scroll_indicator_alpha);
1354 #else /* USE_CAIRO_SCROLLBARS */
1357 tranparency_color (GdkColor *color,
1360 gdouble transparency)
1364 diff = colora.red - colorb.red;
1365 color->red = colora.red-diff*transparency;
1367 diff = colora.green - colorb.green;
1368 color->green = colora.green-diff*transparency;
1370 diff = colora.blue - colorb.blue;
1371 color->blue = colora.blue-diff*transparency;
1375 hildon_pannable_draw_vscroll (GtkWidget *widget,
1376 GdkColor *back_color,
1377 GdkColor *scroll_color)
1379 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1381 GdkColor transp_color;
1382 GdkGC *gc = widget->style->fg_gc[GTK_STATE_INSENSITIVE];
1384 gdk_draw_rectangle (widget->window,
1385 widget->style->bg_gc[GTK_STATE_NORMAL],
1387 priv->vscroll_rect.x, priv->vscroll_rect.y,
1388 priv->vscroll_rect.width,
1389 priv->vscroll_rect.height);
1391 y = ((priv->vadjust->value - priv->vadjust->lower) / (priv->vadjust->upper - priv->vadjust->lower)) *
1392 (widget->allocation.height - (priv->hscroll_visible ? priv->indicator_width : 0));
1393 height = ((((priv->vadjust->value - priv->vadjust->lower) + priv->vadjust->page_size) /
1394 (priv->vadjust->upper - priv->vadjust->lower)) *
1395 (widget->allocation.height -
1396 (priv->hscroll_visible ? priv->indicator_width : 0))) - y;
1398 /* Set a minimum height */
1399 height = MAX (SCROLL_BAR_MIN_SIZE, height);
1401 /* Check the max y position */
1402 y = MIN (y, widget->allocation.height -
1403 (priv->hscroll_visible ? priv->hscroll_rect.height : 0) -
1406 if (priv->scroll_indicator_alpha < 1.0) {
1407 tranparency_color (&transp_color, *back_color, *scroll_color,
1408 priv->scroll_indicator_alpha);
1410 gdk_gc_set_rgb_fg_color (priv->scrollbars_gc, &transp_color);
1412 gc = priv->scrollbars_gc;
1415 gdk_draw_rectangle (widget->window, gc,
1416 TRUE, priv->vscroll_rect.x, y,
1417 priv->vscroll_rect.width, height);
1421 hildon_pannable_draw_hscroll (GtkWidget *widget,
1422 GdkColor *back_color,
1423 GdkColor *scroll_color)
1425 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1427 GdkColor transp_color;
1428 GdkGC *gc = widget->style->fg_gc[GTK_STATE_INSENSITIVE];
1430 gdk_draw_rectangle (widget->window,
1431 widget->style->bg_gc[GTK_STATE_INSENSITIVE],
1433 priv->hscroll_rect.x, priv->hscroll_rect.y,
1434 priv->hscroll_rect.width,
1435 priv->hscroll_rect.height);
1437 /* calculate the scrollbar width and position */
1438 x = ((priv->hadjust->value - priv->hadjust->lower) / (priv->hadjust->upper - priv->hadjust->lower)) *
1439 (widget->allocation.width - (priv->vscroll_visible ? priv->indicator_width : 0));
1440 width =((((priv->hadjust->value - priv->hadjust->lower) +
1441 priv->hadjust->page_size) / (priv->hadjust->upper - priv->hadjust->lower)) *
1442 (widget->allocation.width -
1443 (priv->vscroll_visible ? priv->indicator_width : 0))) - x;
1445 /* Set a minimum width */
1446 width = MAX (SCROLL_BAR_MIN_SIZE, width);
1448 /* Check the max x position */
1449 x = MIN (x, widget->allocation.width -
1450 (priv->vscroll_visible ? priv->vscroll_rect.width : 0) -
1453 if (priv->scroll_indicator_alpha < 1.0) {
1454 tranparency_color (&transp_color, *back_color, *scroll_color,
1455 priv->scroll_indicator_alpha);
1457 gdk_gc_set_rgb_fg_color (priv->scrollbars_gc, &transp_color);
1459 gc = priv->scrollbars_gc;
1462 gdk_draw_rectangle (widget->window, gc,
1463 TRUE, x, priv->hscroll_rect.y, width,
1464 priv->hscroll_rect.height);
1467 #endif /* USE_CAIRO_SCROLLBARS */
1470 hildon_pannable_area_initial_effect (GtkWidget * widget)
1472 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1474 if (priv->vscroll_visible || priv->hscroll_visible) {
1476 priv->scroll_indicator_event_interrupt = 0;
1477 priv->scroll_delay_counter = priv->scrollbar_fade_delay;
1479 hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget), 1.0);
1481 priv->initial_effect = FALSE;
1486 hildon_pannable_area_launch_fade_timeout (HildonPannableArea * area,
1489 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (area)->priv;
1491 priv->scroll_indicator_alpha = alpha;
1493 if (!priv->scroll_indicator_timeout)
1494 priv->scroll_indicator_timeout =
1495 gdk_threads_add_timeout (SCROLL_FADE_TIMEOUT,
1496 (GSourceFunc) hildon_pannable_area_scroll_indicator_fade,
1501 hildon_pannable_area_adjust_changed (HildonPannableArea * area,
1504 if (GTK_WIDGET_REALIZED (area))
1505 hildon_pannable_area_refresh (area);
1509 hildon_pannable_area_adjust_value_changed (HildonPannableArea * area,
1512 if (GTK_WIDGET_REALIZED (area)) {
1513 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (area)->priv;
1515 hildon_pannable_area_redraw (area);
1517 if ((priv->vscroll_visible) || (priv->hscroll_visible)) {
1518 priv->scroll_indicator_event_interrupt = 0;
1519 priv->scroll_delay_counter = priv->scrollbar_fade_delay;
1521 hildon_pannable_area_launch_fade_timeout (area, 1.0);
1527 hildon_pannable_area_redraw (HildonPannableArea * area)
1529 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (area)->priv;
1531 /* Redraw scroll indicators */
1532 if (GTK_WIDGET_DRAWABLE (area)) {
1533 if (priv->hscroll_visible) {
1534 gdk_window_invalidate_rect (GTK_WIDGET (area)->window,
1535 &priv->hscroll_rect, FALSE);
1538 if (priv->vscroll_visible) {
1539 gdk_window_invalidate_rect (GTK_WIDGET (area)->window,
1540 &priv->vscroll_rect, FALSE);
1546 hildon_pannable_area_scroll_indicator_fade(HildonPannableArea * area)
1548 HildonPannableAreaPrivate *priv = area->priv;
1550 /* if moving do not fade out */
1551 if (((ABS (priv->vel_y)>1.0)||
1552 (ABS (priv->vel_x)>1.0))&&(!priv->button_pressed)) {
1557 if (priv->scroll_indicator_event_interrupt) {
1558 /* Stop a fade out, and fade back in */
1559 if (priv->scroll_indicator_alpha > 0.9) {
1560 priv->scroll_indicator_alpha = 1.0;
1561 priv->scroll_indicator_timeout = 0;
1565 priv->scroll_indicator_alpha += 0.2;
1566 hildon_pannable_area_redraw (area);
1572 if ((priv->scroll_indicator_alpha > 0.9) &&
1573 (priv->scroll_delay_counter > 0)) {
1574 priv->scroll_delay_counter--;
1579 if (!priv->scroll_indicator_event_interrupt) {
1580 /* Continue fade out */
1581 if (priv->scroll_indicator_alpha < 0.1) {
1582 priv->scroll_indicator_timeout = 0;
1583 priv->scroll_indicator_alpha = 0.0;
1587 priv->scroll_indicator_alpha -= 0.2;
1588 hildon_pannable_area_redraw (area);
1598 hildon_pannable_area_expose_event (GtkWidget * widget,
1599 GdkEventExpose * event)
1602 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1603 #if USE_CAIRO_SCROLLBARS == 1
1604 GdkColor back_color = widget->style->bg[GTK_STATE_NORMAL];
1605 GdkColor scroll_color = widget->style->base[GTK_STATE_SELECTED];
1606 #else /* USE_CAIRO_SCROLLBARS */
1607 GdkColor back_color = widget->style->bg[GTK_STATE_NORMAL];
1608 GdkColor scroll_color = widget->style->fg[GTK_STATE_INSENSITIVE];
1611 if (G_UNLIKELY ((priv->initial_hint) && (priv->initial_effect))) {
1612 hildon_pannable_area_initial_effect (widget);
1615 if (gtk_bin_get_child (GTK_BIN (widget))) {
1617 if (priv->scroll_indicator_alpha > 0.1) {
1618 if (priv->vscroll_visible) {
1619 hildon_pannable_draw_vscroll (widget, &back_color, &scroll_color);
1621 if (priv->hscroll_visible) {
1622 hildon_pannable_draw_hscroll (widget, &back_color, &scroll_color);
1626 /* draw overshooting rectangles */
1627 if (priv->overshot_dist_y > 0) {
1628 gint overshot_height;
1630 overshot_height = MIN (priv->overshot_dist_y, widget->allocation.height -
1631 (priv->hscroll_visible ? priv->hscroll_rect.height : 0));
1633 gdk_draw_rectangle (widget->window,
1634 widget->style->bg_gc[GTK_STATE_NORMAL],
1638 widget->allocation.width -
1639 (priv->vscroll_visible ? priv->vscroll_rect.width : 0),
1641 } else if (priv->overshot_dist_y < 0) {
1642 gint overshot_height;
1646 MAX (priv->overshot_dist_y,
1647 -(widget->allocation.height -
1648 (priv->hscroll_visible ? priv->hscroll_rect.height : 0)));
1650 overshot_y = MAX (widget->allocation.height +
1652 (priv->hscroll_visible ? priv->hscroll_rect.height : 0), 0);
1654 gdk_draw_rectangle (widget->window,
1655 widget->style->bg_gc[GTK_STATE_NORMAL],
1659 widget->allocation.width -
1660 priv->vscroll_rect.width,
1664 if (priv->overshot_dist_x > 0) {
1665 gint overshot_width;
1667 overshot_width = MIN (priv->overshot_dist_x, widget->allocation.width -
1668 (priv->vscroll_visible ? priv->vscroll_rect.width : 0));
1670 gdk_draw_rectangle (widget->window,
1671 widget->style->bg_gc[GTK_STATE_NORMAL],
1676 widget->allocation.height -
1677 (priv->hscroll_visible ? priv->hscroll_rect.height : 0));
1678 } else if (priv->overshot_dist_x < 0) {
1679 gint overshot_width;
1683 MAX (priv->overshot_dist_x,
1684 -(widget->allocation.width -
1685 (priv->vscroll_visible ? priv->vscroll_rect.width : 0)));
1687 overshot_x = MAX (widget->allocation.width +
1689 (priv->vscroll_visible ? priv->vscroll_rect.width : 0), 0);
1691 gdk_draw_rectangle (widget->window,
1692 widget->style->bg_gc[GTK_STATE_NORMAL],
1697 widget->allocation.height -
1698 priv->hscroll_rect.height);
1703 return GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->expose_event (widget, event);
1707 hildon_pannable_area_get_topmost (GdkWindow * window,
1709 gint * tx, gint * ty,
1712 /* Find the GdkWindow at the given point, by recursing from a given
1713 * parent GdkWindow. Optionally return the co-ordinates transformed
1714 * relative to the child window.
1717 GList *c, *children;
1718 GdkWindow *selected_window = NULL;
1720 gdk_drawable_get_size (GDK_DRAWABLE (window), &width, &height);
1721 if ((x < 0) || (x >= width) || (y < 0) || (y >= height))
1724 children = gdk_window_peek_children (window);
1731 selected_window = window;
1734 for (c = children; c; c = c->next) {
1735 GdkWindow *child = (GdkWindow *) c->data;
1738 gdk_drawable_get_size (GDK_DRAWABLE (child), &width, &height);
1739 gdk_window_get_position (child, &wx, &wy);
1741 if ((x >= wx) && (x < (wx + width)) && (y >= wy) && (y < (wy + height)) &&
1742 (gdk_window_is_visible (child))) {
1744 if (gdk_window_peek_children (child)) {
1745 selected_window = hildon_pannable_area_get_topmost (child, x-wx, y-wy,
1747 if (!selected_window) {
1752 selected_window = child;
1755 if ((gdk_window_get_events (child)&mask)) {
1760 selected_window = child;
1766 return selected_window;
1770 synth_crossing (GdkWindow * child,
1772 gint x_root, gint y_root,
1773 guint32 time, gboolean in)
1775 GdkEventCrossing *crossing_event;
1776 GdkEventType type = in ? GDK_ENTER_NOTIFY : GDK_LEAVE_NOTIFY;
1778 /* Send synthetic enter event */
1779 crossing_event = (GdkEventCrossing *) gdk_event_new (type);
1780 ((GdkEventAny *) crossing_event)->type = type;
1781 ((GdkEventAny *) crossing_event)->window = g_object_ref (child);
1782 ((GdkEventAny *) crossing_event)->send_event = FALSE;
1783 crossing_event->subwindow = g_object_ref (child);
1784 crossing_event->time = time;
1785 crossing_event->x = x;
1786 crossing_event->y = y;
1787 crossing_event->x_root = x_root;
1788 crossing_event->y_root = y_root;
1789 crossing_event->mode = GDK_CROSSING_NORMAL;
1790 crossing_event->detail = GDK_NOTIFY_UNKNOWN;
1791 crossing_event->focus = FALSE;
1792 crossing_event->state = 0;
1793 gdk_event_put ((GdkEvent *) crossing_event);
1794 gdk_event_free ((GdkEvent *) crossing_event);
1798 hildon_pannable_area_button_press_cb (GtkWidget * widget,
1799 GdkEventButton * event)
1802 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1804 if ((!priv->enabled) || (event->button != 1) ||
1805 ((event->time == priv->last_time) &&
1806 (priv->last_type == 1)) || (gtk_bin_get_child (GTK_BIN (widget)) == NULL))
1809 priv->scroll_indicator_event_interrupt = 1;
1811 hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget),
1812 priv->scroll_indicator_alpha);
1814 priv->last_time = event->time;
1815 priv->last_type = 1;
1817 priv->scroll_to_x = -1;
1818 priv->scroll_to_y = -1;
1820 if (priv->button_pressed && priv->child) {
1821 /* Widget stole focus on last click, send crossing-out event */
1822 synth_crossing (priv->child, 0, 0, event->x_root, event->y_root,
1823 event->time, FALSE);
1831 /* Don't allow a click if we're still moving fast */
1832 if ((ABS (priv->vel_x) <= (priv->vmax * priv->vfast_factor)) &&
1833 (ABS (priv->vel_y) <= (priv->vmax * priv->vfast_factor)))
1835 hildon_pannable_area_get_topmost (gtk_bin_get_child (GTK_BIN (widget))->window,
1836 event->x, event->y, &x, &y, GDK_BUTTON_PRESS_MASK);
1840 priv->button_pressed = TRUE;
1842 /* Stop scrolling on mouse-down (so you can flick, then hold to stop) */
1848 gdk_drawable_get_size (priv->child, &priv->child_width,
1849 &priv->child_height);
1850 priv->last_in = TRUE;
1852 g_object_add_weak_pointer ((GObject *) priv->child,
1853 (gpointer) & priv->child);
1855 event = (GdkEventButton *) gdk_event_copy ((GdkEvent *) event);
1861 synth_crossing (priv->child, x, y, event->x_root,
1862 event->y_root, event->time, TRUE);
1864 /* Send synthetic click (button press/release) event */
1865 ((GdkEventAny *) event)->window = g_object_ref (priv->child);
1867 gdk_event_put ((GdkEvent *) event);
1868 gdk_event_free ((GdkEvent *) event);
1876 hildon_pannable_area_check_scrollbars (HildonPannableArea * area)
1878 HildonPannableAreaPrivate *priv = area->priv;
1879 gboolean prev_hscroll_visible, prev_vscroll_visible;
1881 prev_hscroll_visible = priv->hscroll_visible;
1882 prev_vscroll_visible = priv->vscroll_visible;
1884 if (!gtk_bin_get_child (GTK_BIN (area))) {
1885 priv->vscroll_visible = FALSE;
1886 priv->hscroll_visible = FALSE;
1888 switch (priv->hscrollbar_policy) {
1889 case GTK_POLICY_ALWAYS:
1890 priv->hscroll_visible = TRUE;
1892 case GTK_POLICY_NEVER:
1893 priv->hscroll_visible = FALSE;
1896 priv->hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
1897 priv->hadjust->page_size);
1900 switch (priv->vscrollbar_policy) {
1901 case GTK_POLICY_ALWAYS:
1902 priv->vscroll_visible = TRUE;
1904 case GTK_POLICY_NEVER:
1905 priv->vscroll_visible = FALSE;
1908 priv->vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
1909 priv->vadjust->page_size);
1912 /* Store the vscroll/hscroll areas for redrawing */
1913 if (priv->vscroll_visible) {
1914 GtkAllocation *allocation = >K_WIDGET (area)->allocation;
1915 priv->vscroll_rect.x = allocation->width - priv->indicator_width;
1916 priv->vscroll_rect.y = 0;
1917 priv->vscroll_rect.width = priv->indicator_width;
1918 priv->vscroll_rect.height = allocation->height -
1919 (priv->hscroll_visible ? priv->indicator_width : 0);
1921 if (priv->hscroll_visible) {
1922 GtkAllocation *allocation = >K_WIDGET (area)->allocation;
1923 priv->hscroll_rect.y = allocation->height - priv->indicator_width;
1924 priv->hscroll_rect.x = 0;
1925 priv->hscroll_rect.height = priv->indicator_width;
1926 priv->hscroll_rect.width = allocation->width -
1927 (priv->vscroll_visible ? priv->indicator_width : 0);
1931 return ((priv->hscroll_visible != prev_hscroll_visible) ||
1932 (priv->vscroll_visible != prev_vscroll_visible));
1936 hildon_pannable_area_refresh (HildonPannableArea * area)
1938 if (GTK_WIDGET_DRAWABLE (area) &&
1939 hildon_pannable_area_check_scrollbars (area)) {
1940 gtk_widget_queue_resize (GTK_WIDGET (area));
1942 hildon_pannable_area_redraw (area);
1946 /* Scroll by a particular amount (in pixels). Optionally, return if
1947 * the scroll on a particular axis was successful.
1950 hildon_pannable_axis_scroll (HildonPannableArea *area,
1951 GtkAdjustment *adjust,
1955 gint *overshot_dist,
1961 HildonPannableAreaPrivate *priv = area->priv;
1963 dist = gtk_adjustment_get_value (adjust) - inc;
1966 * We use overshot_dist to define the distance of the current overshoot,
1967 * and overshooting to define the direction/whether or not we are overshot
1969 if (!(*overshooting)) {
1971 /* Initiation of the overshoot happens when the finger is released
1972 * and the current position of the pannable contents are out of range
1974 if (dist < adjust->lower) {
1977 dist = adjust->lower;
1979 if (overshoot_max!=0) {
1982 *overshot_dist = CLAMP (*overshot_dist + *vel, 0, overshoot_max);
1983 *vel = MIN (priv->vmax_overshooting, *vel);
1984 gtk_widget_queue_resize (GTK_WIDGET (area));
1988 } else if (dist > adjust->upper - adjust->page_size) {
1991 dist = adjust->upper - adjust->page_size;
1993 if (overshoot_max!=0) {
1996 *overshot_dist = CLAMP (*overshot_dist + *vel, -overshoot_max, 0);
1997 *vel = MAX (-priv->vmax_overshooting, *vel);
1998 gtk_widget_queue_resize (GTK_WIDGET (area));
2003 if ((*scroll_to) != -1) {
2004 if (((inc < 0)&&(*scroll_to <= dist))||
2005 ((inc > 0)&&(*scroll_to >= dist))) {
2013 gtk_adjustment_set_value (adjust, dist);
2015 if (!priv->button_pressed) {
2017 /* When the overshoot has started we continue for
2018 * PROP_BOUNCE_STEPS more steps into the overshoot before we
2019 * reverse direction. The deceleration factor is calculated
2020 * based on the percentage distance from the first item with
2021 * each iteration, therefore always returning us to the
2022 * top/bottom most element
2024 if (*overshot_dist > 0) {
2026 if ((*overshooting < priv->bounce_steps) && (*vel > 0)) {
2028 *vel = (((gdouble)*overshot_dist)/overshoot_max) * (*vel);
2029 } else if ((*overshooting >= priv->bounce_steps) && (*vel > 0)) {
2031 } else if ((*overshooting > 1) && (*vel < 0)) {
2032 /* we add the MIN in order to avoid very small speeds */
2033 *vel = MIN ((((gdouble)*overshot_dist)*0.4) * -1, -2.0);
2036 *overshot_dist = CLAMP (*overshot_dist + *vel, 0, overshoot_max);
2038 gtk_widget_queue_resize (GTK_WIDGET (area));
2040 } else if (*overshot_dist < 0) {
2042 if ((*overshooting < priv->bounce_steps) && (*vel < 0)) {
2044 *vel = (((gdouble)*overshot_dist)/overshoot_max) * (*vel) * -1;
2045 } else if ((*overshooting >= priv->bounce_steps) && (*vel < 0)) {
2047 } else if ((*overshooting > 1) && (*vel > 0)) {
2048 /* we add the MAX in order to avoid very small speeds */
2049 *vel = MAX ((((gdouble)*overshot_dist)*0.4) * -1, 2.0);
2052 *overshot_dist = CLAMP (*overshot_dist + (*vel), -overshoot_max, 0);
2054 gtk_widget_queue_resize (GTK_WIDGET (area));
2059 gtk_widget_queue_resize (GTK_WIDGET (area));
2063 gint overshot_dist_old = *overshot_dist;
2065 if (*overshot_dist > 0) {
2066 *overshot_dist = CLAMP ((*overshot_dist) + inc, 0, overshoot_max);
2067 } else if (*overshot_dist < 0) {
2068 *overshot_dist = CLAMP ((*overshot_dist) + inc, -1 * overshoot_max, 0);
2071 gtk_adjustment_set_value (adjust, dist);
2074 if (*overshot_dist != overshot_dist_old)
2075 gtk_widget_queue_resize (GTK_WIDGET (area));
2081 hildon_pannable_area_scroll (HildonPannableArea *area,
2082 gdouble x, gdouble y)
2085 HildonPannableAreaPrivate *priv = area->priv;
2086 gboolean hscroll_visible, vscroll_visible;
2088 if (gtk_bin_get_child (GTK_BIN (area)) == NULL)
2091 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
2092 priv->vadjust->page_size);
2093 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
2094 priv->hadjust->page_size);
2099 if (vscroll_visible) {
2100 hildon_pannable_axis_scroll (area, priv->vadjust, &priv->vel_y, y,
2101 &priv->overshooting_y, &priv->overshot_dist_y,
2102 &priv->scroll_to_y, priv->vovershoot_max, &sy);
2107 if (hscroll_visible) {
2108 hildon_pannable_axis_scroll (area, priv->hadjust, &priv->vel_x, x,
2109 &priv->overshooting_x, &priv->overshot_dist_x,
2110 &priv->scroll_to_x, priv->hovershoot_max, &sx);
2115 /* If the scroll on a particular axis wasn't succesful, reset the
2116 * initial scroll position to the new mouse co-ordinate. This means
2117 * when you get to the top of the page, dragging down works immediately.
2119 if (priv->mode == HILDON_PANNABLE_AREA_MODE_ACCEL) {
2131 hildon_pannable_area_timeout (HildonPannableArea * area)
2133 HildonPannableAreaPrivate *priv = area->priv;
2135 if ((!priv->enabled) || (priv->mode == HILDON_PANNABLE_AREA_MODE_PUSH)) {
2141 if (!priv->button_pressed) {
2142 /* Decelerate gradually when pointer is raised */
2143 if ((!priv->overshot_dist_y) &&
2144 (!priv->overshot_dist_x)) {
2146 /* in case we move to a specific point do not decelerate when arriving */
2147 if ((priv->scroll_to_x != -1)||(priv->scroll_to_y != -1)) {
2149 if (ABS (priv->vel_x) >= 1.5) {
2150 priv->vel_x *= priv->decel;
2153 if (ABS (priv->vel_y) >= 1.5) {
2154 priv->vel_y *= priv->decel;
2158 if ((!priv->low_friction_mode) ||
2159 ((priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ) &&
2160 (ABS (priv->vel_x) < 0.8*priv->vmax)))
2161 priv->vel_x *= priv->decel;
2163 if ((!priv->low_friction_mode) ||
2164 ((priv->mov_mode&HILDON_MOVEMENT_MODE_VERT) &&
2165 (ABS (priv->vel_y) < 0.8*priv->vmax)))
2166 priv->vel_y *= priv->decel;
2168 if ((ABS (priv->vel_x) < 1.0) && (ABS (priv->vel_y) < 1.0)) {
2177 } else if (priv->mode == HILDON_PANNABLE_AREA_MODE_AUTO) {
2183 hildon_pannable_area_scroll (area, priv->vel_x, priv->vel_y);
2189 hildon_pannable_area_calculate_velocity (gdouble *vel,
2193 gdouble drag_inertia,
2199 if (ABS (dist) >= RATIO_TOLERANCE) {
2200 rawvel = (dist / ABS (delta)) * force;
2201 *vel = *vel * (1 - drag_inertia) +
2202 rawvel * drag_inertia;
2203 *vel = *vel > 0 ? MIN (*vel, vmax)
2204 : MAX (*vel, -1 * vmax);
2209 hildon_pannable_area_motion_event_scroll_timeout (HildonPannableArea *area)
2211 HildonPannableAreaPrivate *priv = area->priv;
2213 if ((priv->motion_x != 0)||(priv->motion_y != 0))
2214 hildon_pannable_area_scroll (area, priv->motion_x, priv->motion_y);
2216 priv->motion_event_scroll_timeout = 0;
2222 hildon_pannable_area_motion_event_scroll (HildonPannableArea *area,
2223 gdouble x, gdouble y)
2225 HildonPannableAreaPrivate *priv = area->priv;
2227 if (priv->motion_event_scroll_timeout) {
2229 priv->motion_x += x;
2230 priv->motion_y += y;
2234 /* we do not delay the first event but the next ones */
2235 hildon_pannable_area_scroll (area, x, y);
2240 priv->motion_event_scroll_timeout = gdk_threads_add_timeout
2241 ((gint) (1000.0 / (gdouble) MOTION_EVENTS_PER_SECOND),
2242 (GSourceFunc) hildon_pannable_area_motion_event_scroll_timeout, area);
2247 hildon_pannable_area_motion_notify_cb (GtkWidget * widget,
2248 GdkEventMotion * event)
2250 HildonPannableArea *area = HILDON_PANNABLE_AREA (widget);
2251 HildonPannableAreaPrivate *priv = area->priv;
2255 if (gtk_bin_get_child (GTK_BIN (widget)) == NULL)
2258 if ((!priv->enabled) || (!priv->button_pressed) ||
2259 ((event->time == priv->last_time) && (priv->last_type == 2))) {
2260 gdk_window_get_pointer (widget->window, NULL, NULL, 0);
2264 if (priv->last_type == 1) {
2265 priv->first_drag = TRUE;
2268 x = event->x - priv->x;
2269 y = event->y - priv->y;
2271 if (priv->first_drag && (!priv->moved) &&
2272 ((ABS (x) > (priv->panning_threshold))
2273 || (ABS (y) > (priv->panning_threshold)))) {
2278 if (priv->first_drag) {
2279 gboolean vscroll_visible;
2280 gboolean hscroll_visible;
2282 if (ABS (priv->iy - event->y) >=
2283 ABS (priv->ix - event->x)) {
2285 g_signal_emit (area,
2286 pannable_area_signals[VERTICAL_MOVEMENT],
2287 0, (priv->iy > event->y) ?
2288 HILDON_MOVEMENT_UP :
2289 HILDON_MOVEMENT_DOWN,
2290 (gdouble)priv->ix, (gdouble)priv->iy);
2292 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
2293 priv->vadjust->page_size);
2295 if (!((vscroll_visible)&&
2296 (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT))) {
2298 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
2299 priv->hadjust->page_size);
2301 /* even in case we do not have to move we check if this
2302 could be a fake horizontal movement */
2303 if (!((hscroll_visible)&&
2304 (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ)) ||
2305 (ABS (priv->iy - event->y) -
2306 ABS (priv->ix - event->x) >= priv->direction_error_margin))
2307 priv->moved = FALSE;
2311 g_signal_emit (area,
2312 pannable_area_signals[HORIZONTAL_MOVEMENT],
2313 0, (priv->ix > event->x) ?
2314 HILDON_MOVEMENT_LEFT :
2315 HILDON_MOVEMENT_RIGHT,
2316 (gdouble)priv->ix, (gdouble)priv->iy);
2318 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
2319 priv->hadjust->page_size);
2321 if (!((hscroll_visible)&&
2322 (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ))) {
2324 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
2325 priv->vadjust->page_size);
2327 /* even in case we do not have to move we check if this
2328 could be a fake vertical movement */
2329 if (!((vscroll_visible) &&
2330 (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT)) ||
2331 (ABS (priv->ix - event->x) -
2332 ABS (priv->iy - event->y) >= priv->direction_error_margin))
2333 priv->moved = FALSE;
2337 if ((priv->moved)&&(priv->child)) {
2340 pos_x = priv->cx + (event->x - priv->ix);
2341 pos_y = priv->cy + (event->y - priv->iy);
2343 synth_crossing (priv->child, pos_x, pos_y, event->x_root,
2344 event->y_root, event->time, FALSE);
2348 priv->first_drag = FALSE;
2350 if ((priv->mode != HILDON_PANNABLE_AREA_MODE_PUSH) &&
2351 (priv->mode != HILDON_PANNABLE_AREA_MODE_AUTO)) {
2354 priv->idle_id = gdk_threads_add_timeout ((gint)
2355 (1000.0 / (gdouble) priv->sps),
2357 hildon_pannable_area_timeout, area);
2362 switch (priv->mode) {
2363 case HILDON_PANNABLE_AREA_MODE_PUSH:
2364 /* Scroll by the amount of pixels the cursor has moved
2365 * since the last motion event.
2367 hildon_pannable_area_motion_event_scroll (area, x, y);
2371 case HILDON_PANNABLE_AREA_MODE_ACCEL:
2372 /* Set acceleration relative to the initial click */
2373 priv->ex = event->x;
2374 priv->ey = event->y;
2375 priv->vel_x = ((x > 0) ? 1 : -1) *
2377 (gdouble) widget->allocation.width) *
2378 (priv->vmax - priv->vmin)) + priv->vmin);
2379 priv->vel_y = ((y > 0) ? 1 : -1) *
2381 (gdouble) widget->allocation.height) *
2382 (priv->vmax - priv->vmin)) + priv->vmin);
2384 case HILDON_PANNABLE_AREA_MODE_AUTO:
2386 delta = event->time - priv->last_time;
2388 if (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT) {
2389 gdouble dist = event->y - priv->y;
2391 hildon_pannable_area_calculate_velocity (&priv->vel_y,
2403 if (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ) {
2404 gdouble dist = event->x - priv->x;
2406 hildon_pannable_area_calculate_velocity (&priv->vel_x,
2418 hildon_pannable_area_motion_event_scroll (area, x, y);
2420 if (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ)
2422 if (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT)
2430 } else if (priv->child) {
2434 pos_x = priv->cx + (event->x - priv->ix);
2435 pos_y = priv->cy + (event->y - priv->iy);
2437 in = (((0 <= pos_x)&&(priv->child_width >= pos_x)) &&
2438 ((0 <= pos_y)&&(priv->child_height >= pos_y)));
2440 if (((!priv->last_in)&&in)||((priv->last_in)&&(!in))) {
2442 synth_crossing (priv->child, pos_x, pos_y, event->x_root,
2443 event->y_root, event->time, in);
2449 priv->last_time = event->time;
2450 priv->last_type = 2;
2453 /* Send motion notify to child */
2454 event = (GdkEventMotion *) gdk_event_copy ((GdkEvent *) event);
2455 event->x = priv->cx + (event->x - priv->ix);
2456 event->y = priv->cy + (event->y - priv->iy);
2457 event->window = g_object_ref (priv->child);
2458 gdk_event_put ((GdkEvent *) event);
2459 gdk_event_free ((GdkEvent *) event);
2462 gdk_window_get_pointer (widget->window, NULL, NULL, 0);
2468 hildon_pannable_leave_notify_event (GtkWidget *widget,
2469 GdkEventCrossing *event)
2471 HildonPannableArea *area = HILDON_PANNABLE_AREA (widget);
2472 HildonPannableAreaPrivate *priv = area->priv;
2474 if ((priv->child)&&(priv->last_in)) {
2475 priv->last_in = FALSE;
2477 synth_crossing (priv->child, 0, 0, event->x_root,
2478 event->y_root, event->time, FALSE);
2485 hildon_pannable_area_button_release_cb (GtkWidget * widget,
2486 GdkEventButton * event)
2488 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
2492 if (((event->time == priv->last_time) && (priv->last_type == 3))
2493 || (gtk_bin_get_child (GTK_BIN (widget)) == NULL)
2494 || (!priv->button_pressed) || (!priv->enabled) || (event->button != 1))
2497 priv->scroll_indicator_event_interrupt = 0;
2498 priv->scroll_delay_counter = priv->scrollbar_fade_delay;
2500 /* move all the way to the last position */
2501 if (priv->motion_event_scroll_timeout) {
2502 g_source_remove (priv->motion_event_scroll_timeout);
2503 hildon_pannable_area_motion_event_scroll_timeout (HILDON_PANNABLE_AREA (widget));
2508 if (priv->last_type == 2) {
2509 gdouble delta = event->time - priv->last_time;
2511 if (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT) {
2512 gdouble dist = event->y - priv->y;
2514 if (ABS (dist) >= 1.0) {
2515 hildon_pannable_area_calculate_velocity (&priv->vel_y,
2523 priv->motion_y = dist;
2524 hildon_pannable_area_motion_event_scroll_timeout (HILDON_PANNABLE_AREA (widget));
2526 if (delta >= CURSOR_STOPPED_TIMEOUT) {
2532 if (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ) {
2533 gdouble dist = event->x - priv->x;
2535 if (ABS (dist) >= 1.0) {
2536 hildon_pannable_area_calculate_velocity (&priv->vel_x,
2543 priv->motion_x = dist;
2544 hildon_pannable_area_motion_event_scroll_timeout (HILDON_PANNABLE_AREA (widget));
2546 if (delta >= CURSOR_STOPPED_TIMEOUT) {
2553 if ((ABS (priv->vel_y) > 1.0)||
2554 (ABS (priv->vel_x) > 1.0)) {
2555 priv->scroll_indicator_alpha = 1.0;
2558 hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget),
2559 priv->scroll_indicator_alpha);
2561 priv->button_pressed = FALSE;
2563 if (priv->mode == HILDON_PANNABLE_AREA_MODE_AUTO ||
2564 priv->mode == HILDON_PANNABLE_AREA_MODE_ACCEL) {
2566 /* If overshoot has been initiated with a finger down, on release set max speed */
2567 if (priv->overshot_dist_y != 0) {
2568 priv->overshooting_y = priv->bounce_steps; /* Hack to stop a bounce in the finger down case */
2569 priv->vel_y = priv->vmax_overshooting;
2572 if (priv->overshot_dist_x != 0) {
2573 priv->overshooting_x = priv->bounce_steps; /* Hack to stop a bounce in the finger down case */
2574 priv->vel_x = priv->vmax_overshooting;
2577 if ((ABS (priv->vel_y) >= priv->vmin) ||
2578 (ABS (priv->vel_x) >= priv->vmin)) {
2581 priv->idle_id = gdk_threads_add_timeout ((gint) (1000.0 / (gdouble) priv->sps),
2583 hildon_pannable_area_timeout, widget);
2587 priv->last_time = event->time;
2588 priv->last_type = 3;
2591 priv->moved = FALSE;
2596 hildon_pannable_area_get_topmost (gtk_bin_get_child (GTK_BIN (widget))->window,
2597 event->x, event->y, &x, &y, GDK_BUTTON_RELEASE_MASK);
2599 event = (GdkEventButton *) gdk_event_copy ((GdkEvent *) event);
2603 /* Leave the widget if we've moved - This doesn't break selection,
2604 * but stops buttons from being clicked.
2606 if ((child != priv->child) || (priv->moved)) {
2607 /* Send synthetic leave event */
2608 synth_crossing (priv->child, x, y, event->x_root,
2609 event->y_root, event->time, FALSE);
2610 /* Send synthetic button release event */
2611 ((GdkEventAny *) event)->window = g_object_ref (priv->child);
2612 gdk_event_put ((GdkEvent *) event);
2614 /* Send synthetic button release event */
2615 ((GdkEventAny *) event)->window = g_object_ref (child);
2616 gdk_event_put ((GdkEvent *) event);
2617 /* Send synthetic leave event */
2618 synth_crossing (priv->child, x, y, event->x_root,
2619 event->y_root, event->time, FALSE);
2621 g_object_remove_weak_pointer ((GObject *) priv->child,
2622 (gpointer) & priv->child);
2624 priv->moved = FALSE;
2625 gdk_event_free ((GdkEvent *) event);
2630 /* utility event handler */
2632 hildon_pannable_area_scroll_cb (GtkWidget *widget,
2633 GdkEventScroll *event)
2635 GtkAdjustment *adj = NULL;
2636 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
2638 if ((!priv->enabled) ||
2639 (gtk_bin_get_child (GTK_BIN (widget)) == NULL))
2642 priv->scroll_indicator_event_interrupt = 0;
2643 priv->scroll_delay_counter = priv->scrollbar_fade_delay + 20;
2645 hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget), 1.0);
2647 /* Stop inertial scrolling */
2648 if (priv->idle_id) {
2651 priv->overshooting_x = 0;
2652 priv->overshooting_y = 0;
2654 if ((priv->overshot_dist_x>0)||(priv->overshot_dist_y>0)) {
2655 priv->overshot_dist_x = 0;
2656 priv->overshot_dist_y = 0;
2658 gtk_widget_queue_resize (GTK_WIDGET (widget));
2661 g_source_remove (priv->idle_id);
2665 if (event->direction == GDK_SCROLL_UP || event->direction == GDK_SCROLL_DOWN)
2666 adj = priv->vadjust;
2668 adj = priv->hadjust;
2672 gdouble delta, new_value;
2674 /* from gtkrange.c calculate delta*/
2675 delta = pow (adj->page_size, 2.0 / 3.0);
2677 if (event->direction == GDK_SCROLL_UP ||
2678 event->direction == GDK_SCROLL_LEFT)
2681 new_value = CLAMP (adj->value + delta, adj->lower, adj->upper - adj->page_size);
2683 gtk_adjustment_set_value (adj, new_value);
2690 hildon_pannable_area_child_mapped (GtkWidget *widget,
2694 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (user_data)->priv;
2696 if (priv->event_window != NULL && priv->enabled)
2697 gdk_window_raise (priv->event_window);
2701 hildon_pannable_area_add (GtkContainer *container, GtkWidget *child)
2703 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (container)->priv;
2705 g_return_if_fail (gtk_bin_get_child (GTK_BIN (container)) == NULL);
2707 gtk_widget_set_parent (child, GTK_WIDGET (container));
2708 GTK_BIN (container)->child = child;
2710 g_signal_connect_after (child, "map-event",
2711 G_CALLBACK (hildon_pannable_area_child_mapped),
2714 if (!gtk_widget_set_scroll_adjustments (child, priv->hadjust, priv->vadjust)) {
2715 g_warning ("%s: cannot add non scrollable widget, "
2716 "wrap it in a viewport", __FUNCTION__);
2721 hildon_pannable_area_remove (GtkContainer *container, GtkWidget *child)
2723 g_return_if_fail (HILDON_IS_PANNABLE_AREA (container));
2724 g_return_if_fail (child != NULL);
2725 g_return_if_fail (gtk_bin_get_child (GTK_BIN (container)) == child);
2727 gtk_widget_set_scroll_adjustments (child, NULL, NULL);
2729 g_signal_handlers_disconnect_by_func (child,
2730 hildon_pannable_area_child_mapped,
2733 /* chain parent class handler to remove child */
2734 GTK_CONTAINER_CLASS (hildon_pannable_area_parent_class)->remove (container, child);
2738 * This method calculates a factor necessary to determine the initial distance
2739 * to jump in hildon_pannable_area_scroll_to(). For fixed time and frames per
2740 * second, we know in how many frames 'n' we need to reach the destination
2741 * point. We know that, for a distance d,
2743 * d = d_0 + d_1 + ... + d_n
2745 * where d_i is the distance travelled in the i-th frame and decel_factor is
2746 * the deceleration factor. This can be rewritten as
2748 * d = d_0 + (d_0 * decel_factor) + ... + (d_n-1 * decel_factor),
2750 * since the distance travelled on each frame is the distance travelled in the
2751 * previous frame reduced by the deceleration factor. Reducing this and
2752 * factoring d_0 out, we get
2754 * d = d_0 (1 + decel_factor + ... + decel_factor^(n-1)).
2756 * Since the sum is independent of the distance to be travelled, we can define
2759 * vel_factor = 1 + decel_factor + ... + decel_factor^(n-1).
2761 * That's the gem we calculate in this method.
2764 hildon_pannable_calculate_vel_factor (HildonPannableArea * self)
2766 HildonPannableAreaPrivate *priv = self->priv;
2771 n = ceil (priv->sps * priv->scroll_time);
2773 for (i = 1; i < n && fct_i >= RATIO_TOLERANCE; i++) {
2774 fct_i *= priv->decel;
2778 priv->vel_factor = fct;
2782 * hildon_pannable_area_new:
2784 * Create a new pannable area widget
2786 * Returns: the newly created #HildonPannableArea
2792 hildon_pannable_area_new (void)
2794 return g_object_new (HILDON_TYPE_PANNABLE_AREA, NULL);
2798 * hildon_pannable_area_new_full:
2799 * @mode: #HildonPannableAreaMode
2800 * @enabled: Value for the enabled property
2801 * @vel_min: Value for the velocity-min property
2802 * @vel_max: Value for the velocity-max property
2803 * @decel: Value for the deceleration property
2804 * @sps: Value for the sps property
2806 * Create a new #HildonPannableArea widget and set various properties
2808 * returns: the newly create #HildonPannableArea
2814 hildon_pannable_area_new_full (gint mode, gboolean enabled,
2815 gdouble vel_min, gdouble vel_max,
2816 gdouble decel, guint sps)
2818 return g_object_new (HILDON_TYPE_PANNABLE_AREA,
2821 "velocity_min", vel_min,
2822 "velocity_max", vel_max,
2823 "deceleration", decel, "sps", sps, NULL);
2827 * hildon_pannable_area_add_with_viewport:
2828 * @area: A #HildonPannableArea
2829 * @child: Child widget to add to the viewport
2831 * Convenience function used to add a child to a #GtkViewport, and add the
2832 * viewport to the scrolled window.
2838 hildon_pannable_area_add_with_viewport (HildonPannableArea * area,
2842 GtkWidget *viewport;
2844 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
2845 g_return_if_fail (GTK_IS_WIDGET (child));
2846 g_return_if_fail (child->parent == NULL);
2848 bin = GTK_BIN (area);
2850 if (bin->child != NULL)
2852 g_return_if_fail (GTK_IS_VIEWPORT (bin->child));
2853 g_return_if_fail (GTK_BIN (bin->child)->child == NULL);
2855 viewport = bin->child;
2859 HildonPannableAreaPrivate *priv = area->priv;
2861 viewport = gtk_viewport_new (priv->hadjust,
2863 gtk_viewport_set_shadow_type (GTK_VIEWPORT (viewport), GTK_SHADOW_NONE);
2864 gtk_container_add (GTK_CONTAINER (area), viewport);
2867 gtk_widget_show (viewport);
2868 gtk_container_add (GTK_CONTAINER (viewport), child);
2872 * hildon_pannable_area_scroll_to:
2873 * @area: A #HildonPannableArea.
2874 * @x: The x coordinate of the destination point or -1 to ignore this axis.
2875 * @y: The y coordinate of the destination point or -1 to ignore this axis.
2877 * Smoothly scrolls @area to ensure that (@x, @y) is a visible point
2878 * on the widget. To move in only one coordinate, you must set the other one
2879 * to -1. Notice that, in %HILDON_PANNABLE_AREA_MODE_PUSH mode, this function
2880 * works just like hildon_pannable_area_jump_to().
2882 * This function is useful if you need to present the user with a particular
2883 * element inside a scrollable widget, like #GtkTreeView. For instance,
2884 * the following example shows how to scroll inside a #GtkTreeView to
2885 * make visible an item, indicated by the #GtkTreeIter @iter.
2889 * GtkTreePath *path;
2890 * GdkRectangle *rect;
2892 * path = gtk_tree_model_get_path (model, &iter);
2893 * gtk_tree_view_get_background_area (GTK_TREE_VIEW (treeview),
2894 * path, NULL, &rect);
2895 * gtk_tree_view_convert_bin_window_to_tree_coords (GTK_TREE_VIEW (treeview),
2896 * 0, rect.y, NULL, &y);
2897 * hildon_pannable_area_scroll_to (panarea, -1, y);
2898 * gtk_tree_path_free (path);
2902 * If you want to present a child widget in simpler scenarios,
2903 * use hildon_pannable_area_scroll_to_child() instead.
2905 * There is a precondition to this function: the widget must be
2906 * already realized. Check the hildon_pannable_area_jump_to_child() for
2907 * more tips regarding how to call this function during
2913 hildon_pannable_area_scroll_to (HildonPannableArea *area,
2914 const gint x, const gint y)
2916 HildonPannableAreaPrivate *priv;
2918 gint dist_x, dist_y;
2919 gboolean hscroll_visible, vscroll_visible;
2921 g_return_if_fail (GTK_WIDGET_REALIZED (area));
2922 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
2926 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
2927 priv->vadjust->page_size);
2928 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
2929 priv->hadjust->page_size);
2931 if (((!vscroll_visible)&&(!hscroll_visible)) ||
2932 (x == -1 && y == -1)) {
2936 if (priv->mode == HILDON_PANNABLE_AREA_MODE_PUSH)
2937 hildon_pannable_area_jump_to (area, x, y);
2939 width = priv->hadjust->upper - priv->hadjust->lower;
2940 height = priv->vadjust->upper - priv->vadjust->lower;
2942 g_return_if_fail (x < width || y < height);
2944 if ((x > -1)&&(hscroll_visible)) {
2945 priv->scroll_to_x = x - priv->hadjust->page_size/2;
2946 dist_x = priv->scroll_to_x - priv->hadjust->value;
2948 priv->scroll_to_x = -1;
2950 priv->vel_x = - dist_x/priv->vel_factor;
2953 priv->scroll_to_x = -1;
2956 if ((y > -1)&&(vscroll_visible)) {
2957 priv->scroll_to_y = y - priv->vadjust->page_size/2;
2958 dist_y = priv->scroll_to_y - priv->vadjust->value;
2960 priv->scroll_to_y = -1;
2962 priv->vel_y = - dist_y/priv->vel_factor;
2965 priv->scroll_to_y = y;
2968 if ((priv->scroll_to_y == -1) && (priv->scroll_to_y == -1)) {
2972 hildon_pannable_area_launch_fade_timeout (area, 1.0);
2975 priv->idle_id = gdk_threads_add_timeout ((gint) (1000.0 / (gdouble) priv->sps),
2977 hildon_pannable_area_timeout, area);
2981 * hildon_pannable_area_jump_to:
2982 * @area: A #HildonPannableArea.
2983 * @x: The x coordinate of the destination point or -1 to ignore this axis.
2984 * @y: The y coordinate of the destination point or -1 to ignore this axis.
2986 * Jumps the position of @area to ensure that (@x, @y) is a visible
2987 * point in the widget. In order to move in only one coordinate, you
2988 * must set the other one to -1. See hildon_pannable_area_scroll_to()
2989 * function for an example of how to calculate the position of
2990 * children in scrollable widgets like #GtkTreeview.
2992 * There is a precondition to this function: the widget must be
2993 * already realized. Check the hildon_pannable_area_jump_to_child() for
2994 * more tips regarding how to call this function during
3000 hildon_pannable_area_jump_to (HildonPannableArea *area,
3001 const gint x, const gint y)
3003 HildonPannableAreaPrivate *priv;
3006 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3007 g_return_if_fail (GTK_WIDGET_REALIZED (area));
3008 g_return_if_fail (x >= -1 && y >= -1);
3010 if (x == -1 && y == -1) {
3016 width = priv->hadjust->upper - priv->hadjust->lower;
3017 height = priv->vadjust->upper - priv->vadjust->lower;
3019 g_return_if_fail (x < width || y < height);
3022 gdouble jump_to = x - priv->hadjust->page_size/2;
3024 if (jump_to > priv->hadjust->upper - priv->hadjust->page_size) {
3025 jump_to = priv->hadjust->upper - priv->hadjust->page_size;
3028 gtk_adjustment_set_value (priv->hadjust, jump_to);
3032 gdouble jump_to = y - priv->vadjust->page_size/2;
3034 if (jump_to > priv->vadjust->upper - priv->vadjust->page_size) {
3035 jump_to = priv->vadjust->upper - priv->vadjust->page_size;
3038 gtk_adjustment_set_value (priv->vadjust, jump_to);
3041 priv->scroll_indicator_alpha = 1.0;
3043 if (priv->scroll_indicator_timeout) {
3044 g_source_remove (priv->scroll_indicator_timeout);
3045 priv->scroll_indicator_timeout = 0;
3048 if (priv->idle_id) {
3051 priv->overshooting_x = 0;
3052 priv->overshooting_y = 0;
3054 if ((priv->overshot_dist_x>0)||(priv->overshot_dist_y>0)) {
3055 priv->overshot_dist_x = 0;
3056 priv->overshot_dist_y = 0;
3058 gtk_widget_queue_resize (GTK_WIDGET (area));
3061 g_source_remove (priv->idle_id);
3067 * hildon_pannable_area_scroll_to_child:
3068 * @area: A #HildonPannableArea.
3069 * @child: A #GtkWidget, descendant of @area.
3071 * Smoothly scrolls until @child is visible inside @area. @child must
3072 * be a descendant of @area. If you need to scroll inside a scrollable
3073 * widget, e.g., #GtkTreeview, see hildon_pannable_area_scroll_to().
3075 * There is a precondition to this function: the widget must be
3076 * already realized. Check the hildon_pannable_area_jump_to_child() for
3077 * more tips regarding how to call this function during
3083 hildon_pannable_area_scroll_to_child (HildonPannableArea *area, GtkWidget *child)
3085 GtkWidget *bin_child;
3088 g_return_if_fail (GTK_WIDGET_REALIZED (area));
3089 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3090 g_return_if_fail (GTK_IS_WIDGET (child));
3091 g_return_if_fail (gtk_widget_is_ancestor (child, GTK_WIDGET (area)));
3093 if (GTK_BIN (area)->child == NULL)
3096 /* We need to get to check the child of the inside the area */
3097 bin_child = GTK_BIN (area)->child;
3099 /* we check if we added a viewport */
3100 if (GTK_IS_VIEWPORT (bin_child)) {
3101 bin_child = GTK_BIN (bin_child)->child;
3104 if (gtk_widget_translate_coordinates (child, bin_child, 0, 0, &x, &y))
3105 hildon_pannable_area_scroll_to (area, x, y);
3109 * hildon_pannable_area_jump_to_child:
3110 * @area: A #HildonPannableArea.
3111 * @child: A #GtkWidget, descendant of @area.
3113 * Jumps to make sure @child is visible inside @area. @child must
3114 * be a descendant of @area. If you want to move inside a scrollable
3115 * widget, like, #GtkTreeview, see hildon_pannable_area_scroll_to().
3117 * There is a precondition to this function: the widget must be
3118 * already realized. You can control if the widget is ready with the
3119 * GTK_WIDGET_REALIZED macro. If you want to call this function during
3120 * the initialization process of the widget do it inside a callback to
3121 * the ::realize signal, using g_signal_connect_after() function.
3126 hildon_pannable_area_jump_to_child (HildonPannableArea *area, GtkWidget *child)
3128 GtkWidget *bin_child;
3131 g_return_if_fail (GTK_WIDGET_REALIZED (area));
3132 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3133 g_return_if_fail (GTK_IS_WIDGET (child));
3134 g_return_if_fail (gtk_widget_is_ancestor (child, GTK_WIDGET (area)));
3136 if (gtk_bin_get_child (GTK_BIN (area)) == NULL)
3139 /* We need to get to check the child of the inside the area */
3140 bin_child = gtk_bin_get_child (GTK_BIN (area));
3142 /* we check if we added a viewport */
3143 if (GTK_IS_VIEWPORT (bin_child)) {
3144 bin_child = gtk_bin_get_child (GTK_BIN (bin_child));
3147 if (gtk_widget_translate_coordinates (child, bin_child, 0, 0, &x, &y))
3148 hildon_pannable_area_jump_to (area, x, y);
3152 * hildon_pannable_get_child_widget_at:
3153 * @area: A #HildonPannableArea.
3154 * @x: horizontal coordinate of the point
3155 * @y: vertical coordinate of the point
3157 * Get the widget at the point (x, y) inside the pannable area. In
3158 * case no widget found it returns NULL.
3160 * returns: the #GtkWidget if we find a widget, NULL in any other case
3165 hildon_pannable_get_child_widget_at (HildonPannableArea *area,
3166 gdouble x, gdouble y)
3168 GdkWindow *window = NULL;
3169 GtkWidget *child_widget = NULL;
3171 window = hildon_pannable_area_get_topmost
3172 (gtk_bin_get_child (GTK_BIN (area))->window,
3173 x, y, NULL, NULL, GDK_ALL_EVENTS_MASK);
3175 gdk_window_get_user_data (window, (gpointer) &child_widget);
3177 return child_widget;
3182 * hildon_pannable_area_get_hadjustment:
3183 * @area: A #HildonPannableArea.
3185 * Returns the horizontal adjustment. This adjustment is the internal
3186 * widget adjustment used to control the animations. Do not modify it
3187 * directly to change the position of the pannable, to do that use the
3188 * pannable API. If you modify the object directly it could cause
3189 * artifacts in the animations.
3191 * returns: The horizontal #GtkAdjustment
3196 hildon_pannable_area_get_hadjustment (HildonPannableArea *area)
3199 g_return_val_if_fail (HILDON_IS_PANNABLE_AREA (area), NULL);
3201 return area->priv->hadjust;
3205 * hildon_pannable_area_get_vadjustment:
3206 * @area: A #HildonPannableArea.
3208 * Returns the vertical adjustment. This adjustment is the internal
3209 * widget adjustment used to control the animations. Do not modify it
3210 * directly to change the position of the pannable, to do that use the
3211 * pannable API. If you modify the object directly it could cause
3212 * artifacts in the animations.
3214 * returns: The vertical #GtkAdjustment
3219 hildon_pannable_area_get_vadjustment (HildonPannableArea *area)
3221 g_return_val_if_fail (HILDON_IS_PANNABLE_AREA (area), NULL);
3223 return area->priv->vadjust;
3228 * hildon_pannable_area_get_size_request_policy:
3229 * @area: A #HildonPannableArea.
3231 * This function returns the current size request policy of the
3232 * widget. That policy controls the way the size_request is done in
3233 * the pannable area. Check
3234 * hildon_pannable_area_set_size_request_policy() for a more detailed
3237 * returns: the policy is currently being used in the widget
3238 * #HildonSizeRequestPolicy.
3242 HildonSizeRequestPolicy
3243 hildon_pannable_area_get_size_request_policy (HildonPannableArea *area)
3245 HildonPannableAreaPrivate *priv;
3247 g_return_val_if_fail (HILDON_IS_PANNABLE_AREA (area), FALSE);
3251 return priv->size_request_policy;
3255 * hildon_pannable_area_set_size_request_policy:
3256 * @area: A #HildonPannableArea.
3257 * @size_request_policy: One of the allowed #HildonSizeRequestPolicy
3259 * This function sets the pannable area size request policy. That
3260 * policy controls the way the size_request is done in the pannable
3261 * area. Pannable can use the size request of its children
3262 * (#HILDON_SIZE_REQUEST_CHILDREN) or the minimum size required for
3263 * the area itself (#HILDON_SIZE_REQUEST_MINIMUM), the latter is the
3264 * default. Recall this size depends on the scrolling policy you are
3265 * requesting to the pannable area, if you set #GTK_POLICY_NEVER this
3266 * parameter will not have any effect with
3267 * #HILDON_SIZE_REQUEST_MINIMUM set.
3271 * Deprecated: This method and the policy request is deprecated, DO
3272 * NOT use it in future code, the only policy properly supported in
3273 * gtk+ nowadays is the minimum size. Use #gtk_window_set_default_size
3274 * or #gtk_window_set_geometry_hints with the proper size in your case
3275 * to define the height of your dialogs.
3278 hildon_pannable_area_set_size_request_policy (HildonPannableArea *area,
3279 HildonSizeRequestPolicy size_request_policy)
3281 HildonPannableAreaPrivate *priv;
3283 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3287 if (priv->size_request_policy == size_request_policy)
3290 priv->size_request_policy = size_request_policy;
3292 gtk_widget_queue_resize (GTK_WIDGET (area));
3294 g_object_notify (G_OBJECT (area), "size-request-policy");