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 */
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->clicked = 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 = priv->scrollbar_fade_delay;
651 priv->scroll_to_x = -1;
652 priv->scroll_to_y = -1;
653 priv->first_drag = TRUE;
654 priv->initial_effect = TRUE;
655 priv->child_width = 0;
656 priv->child_height = 0;
657 priv->last_in = TRUE;
659 hildon_pannable_calculate_vel_factor (area);
661 gtk_widget_add_events (GTK_WIDGET (area), GDK_POINTER_MOTION_HINT_MASK);
664 GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
666 GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
668 g_object_ref_sink (G_OBJECT (priv->hadjust));
669 g_object_ref_sink (G_OBJECT (priv->vadjust));
671 g_signal_connect_swapped (priv->hadjust, "value-changed",
672 G_CALLBACK (hildon_pannable_area_adjust_value_changed), area);
673 g_signal_connect_swapped (priv->vadjust, "value-changed",
674 G_CALLBACK (hildon_pannable_area_adjust_value_changed), area);
675 g_signal_connect_swapped (priv->hadjust, "changed",
676 G_CALLBACK (hildon_pannable_area_adjust_changed), area);
677 g_signal_connect_swapped (priv->vadjust, "changed",
678 G_CALLBACK (hildon_pannable_area_adjust_changed), area);
679 g_signal_connect (area, "grab-notify",
680 G_CALLBACK (hildon_pannable_area_grab_notify), NULL);
684 hildon_pannable_area_get_property (GObject * object,
689 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (object)->priv;
691 switch (property_id) {
693 g_value_set_boolean (value, priv->enabled);
696 g_value_set_enum (value, priv->mode);
698 case PROP_MOVEMENT_MODE:
699 g_value_set_flags (value, priv->mov_mode);
701 case PROP_VELOCITY_MIN:
702 g_value_set_double (value, priv->vmin);
704 case PROP_VELOCITY_MAX:
705 g_value_set_double (value, priv->vmax);
707 case PROP_VEL_MAX_OVERSHOOTING:
708 g_value_set_double (value, priv->vmax_overshooting);
710 case PROP_VELOCITY_FAST_FACTOR:
711 g_value_set_double (value, priv->vfast_factor);
713 case PROP_DECELERATION:
714 g_value_set_double (value, priv->decel);
716 case PROP_DRAG_INERTIA:
717 g_value_set_double (value, priv->drag_inertia);
720 g_value_set_uint (value, priv->sps);
722 case PROP_PANNING_THRESHOLD:
723 g_value_set_uint (value, priv->panning_threshold);
725 case PROP_SCROLLBAR_FADE_DELAY:
726 /* convert to miliseconds */
727 g_value_set_uint (value, priv->scrollbar_fade_delay * SCROLL_FADE_TIMEOUT);
729 case PROP_BOUNCE_STEPS:
730 g_value_set_uint (value, priv->bounce_steps);
733 g_value_set_uint (value, priv->force);
735 case PROP_DIRECTION_ERROR_MARGIN:
736 g_value_set_uint (value, priv->direction_error_margin);
738 case PROP_VSCROLLBAR_POLICY:
739 g_value_set_enum (value, priv->vscrollbar_policy);
741 case PROP_HSCROLLBAR_POLICY:
742 g_value_set_enum (value, priv->hscrollbar_policy);
744 case PROP_VOVERSHOOT_MAX:
745 g_value_set_int (value, priv->vovershoot_max);
747 case PROP_HOVERSHOOT_MAX:
748 g_value_set_int (value, priv->hovershoot_max);
750 case PROP_SCROLL_TIME:
751 g_value_set_double (value, priv->scroll_time);
753 case PROP_INITIAL_HINT:
754 g_value_set_boolean (value, priv->initial_hint);
756 case PROP_LOW_FRICTION_MODE:
757 g_value_set_boolean (value, priv->low_friction_mode);
759 case PROP_SIZE_REQUEST_POLICY:
760 g_value_set_enum (value, priv->size_request_policy);
762 case PROP_HADJUSTMENT:
763 g_value_set_object (value,
764 hildon_pannable_area_get_hadjustment
765 (HILDON_PANNABLE_AREA (object)));
767 case PROP_VADJUSTMENT:
768 g_value_set_object (value,
769 hildon_pannable_area_get_vadjustment
770 (HILDON_PANNABLE_AREA (object)));
773 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
778 hildon_pannable_area_set_property (GObject * object,
780 const GValue * value,
783 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (object)->priv;
786 switch (property_id) {
788 enabled = g_value_get_boolean (value);
790 if ((priv->enabled != enabled) && (GTK_WIDGET_REALIZED (object))) {
792 gdk_window_raise (priv->event_window);
794 gdk_window_lower (priv->event_window);
797 priv->enabled = enabled;
800 priv->mode = g_value_get_enum (value);
802 case PROP_MOVEMENT_MODE:
803 priv->mov_mode = g_value_get_flags (value);
805 case PROP_VELOCITY_MIN:
806 priv->vmin = g_value_get_double (value);
808 case PROP_VELOCITY_MAX:
809 priv->vmax = g_value_get_double (value);
811 case PROP_VEL_MAX_OVERSHOOTING:
812 priv->vmax_overshooting = g_value_get_double (value);
814 case PROP_VELOCITY_FAST_FACTOR:
815 priv->vfast_factor = g_value_get_double (value);
817 case PROP_DECELERATION:
818 hildon_pannable_calculate_vel_factor (HILDON_PANNABLE_AREA (object));
820 priv->decel = g_value_get_double (value);
822 case PROP_DRAG_INERTIA:
823 priv->drag_inertia = g_value_get_double (value);
826 priv->sps = g_value_get_uint (value);
828 case PROP_PANNING_THRESHOLD:
829 priv->panning_threshold = g_value_get_uint (value);
831 case PROP_SCROLLBAR_FADE_DELAY:
832 /* convert to miliseconds */
833 priv->scrollbar_fade_delay = g_value_get_uint (value)/(SCROLL_FADE_TIMEOUT);
835 case PROP_BOUNCE_STEPS:
836 priv->bounce_steps = g_value_get_uint (value);
839 priv->force = g_value_get_uint (value);
841 case PROP_DIRECTION_ERROR_MARGIN:
842 priv->direction_error_margin = g_value_get_uint (value);
844 case PROP_VSCROLLBAR_POLICY:
845 priv->vscrollbar_policy = g_value_get_enum (value);
847 gtk_widget_queue_resize (GTK_WIDGET (object));
849 case PROP_HSCROLLBAR_POLICY:
850 priv->hscrollbar_policy = g_value_get_enum (value);
852 gtk_widget_queue_resize (GTK_WIDGET (object));
854 case PROP_VOVERSHOOT_MAX:
855 priv->vovershoot_max = g_value_get_int (value);
857 case PROP_HOVERSHOOT_MAX:
858 priv->hovershoot_max = g_value_get_int (value);
860 case PROP_SCROLL_TIME:
861 priv->scroll_time = g_value_get_double (value);
863 hildon_pannable_calculate_vel_factor (HILDON_PANNABLE_AREA (object));
865 case PROP_INITIAL_HINT:
866 priv->initial_hint = g_value_get_boolean (value);
868 case PROP_LOW_FRICTION_MODE:
869 priv->low_friction_mode = g_value_get_boolean (value);
871 case PROP_SIZE_REQUEST_POLICY:
872 hildon_pannable_area_set_size_request_policy (HILDON_PANNABLE_AREA (object),
873 g_value_get_enum (value));
877 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
882 hildon_pannable_area_dispose (GObject * object)
884 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (object)->priv;
885 GtkWidget *child = gtk_bin_get_child (GTK_BIN (object));
888 g_source_remove (priv->idle_id);
892 if (priv->scroll_indicator_timeout){
893 g_source_remove (priv->scroll_indicator_timeout);
894 priv->scroll_indicator_timeout = 0;
897 if (priv->motion_event_scroll_timeout){
898 g_source_remove (priv->motion_event_scroll_timeout);
899 priv->motion_event_scroll_timeout = 0;
903 g_signal_handlers_disconnect_by_func (child,
904 hildon_pannable_area_child_mapped,
908 g_signal_handlers_disconnect_by_func (object,
909 hildon_pannable_area_grab_notify,
913 g_signal_handlers_disconnect_by_func (priv->hadjust,
914 hildon_pannable_area_adjust_value_changed,
916 g_signal_handlers_disconnect_by_func (priv->hadjust,
917 hildon_pannable_area_adjust_changed,
919 g_object_unref (priv->hadjust);
920 priv->hadjust = NULL;
924 g_signal_handlers_disconnect_by_func (priv->vadjust,
925 hildon_pannable_area_adjust_value_changed,
927 g_signal_handlers_disconnect_by_func (priv->vadjust,
928 hildon_pannable_area_adjust_changed,
930 g_object_unref (priv->vadjust);
931 priv->vadjust = NULL;
934 if (G_OBJECT_CLASS (hildon_pannable_area_parent_class)->dispose)
935 G_OBJECT_CLASS (hildon_pannable_area_parent_class)->dispose (object);
939 hildon_pannable_area_realize (GtkWidget * widget)
941 GdkWindowAttr attributes;
942 gint attributes_mask;
944 HildonPannableAreaPrivate *priv;
946 priv = HILDON_PANNABLE_AREA (widget)->priv;
948 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
950 border_width = GTK_CONTAINER (widget)->border_width;
952 attributes.x = widget->allocation.x + border_width;
953 attributes.y = widget->allocation.y + border_width;
954 attributes.width = MAX (widget->allocation.width - 2 * border_width, 0);
955 attributes.height = MAX (widget->allocation.height - 2 * border_width, 0);
956 attributes.window_type = GDK_WINDOW_CHILD;
958 /* avoid using the hildon_window */
959 attributes.visual = gtk_widget_get_visual (widget);
960 attributes.colormap = gtk_widget_get_colormap (widget);
961 attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK;
962 attributes.wclass = GDK_INPUT_OUTPUT;
964 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
966 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
967 &attributes, attributes_mask);
968 gdk_window_set_user_data (widget->window, widget);
970 /* create the events window */
973 attributes.event_mask = gtk_widget_get_events (widget)
974 | GDK_BUTTON_MOTION_MASK
975 | GDK_BUTTON_PRESS_MASK
976 | GDK_BUTTON_RELEASE_MASK
978 | GDK_EXPOSURE_MASK | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK;
979 attributes.wclass = GDK_INPUT_ONLY;
981 attributes_mask = GDK_WA_X | GDK_WA_Y;
983 priv->event_window = gdk_window_new (widget->window,
984 &attributes, attributes_mask);
985 gdk_window_set_user_data (priv->event_window, widget);
987 widget->style = gtk_style_attach (widget->style, widget->window);
988 gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
990 priv->scrollbars_gc = gdk_gc_new (GDK_DRAWABLE (widget->window));
991 gdk_gc_copy (priv->scrollbars_gc, widget->style->fg_gc[GTK_STATE_INSENSITIVE]);
995 hildon_pannable_area_unrealize (GtkWidget * widget)
997 HildonPannableAreaPrivate *priv;
999 priv = HILDON_PANNABLE_AREA (widget)->priv;
1001 if (priv->event_window != NULL) {
1002 gdk_window_set_user_data (priv->event_window, NULL);
1003 gdk_window_destroy (priv->event_window);
1004 priv->event_window = NULL;
1007 gdk_gc_unref (priv->scrollbars_gc);
1009 if (GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->unrealize)
1010 (*GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->unrealize)(widget);
1014 hildon_pannable_area_size_request (GtkWidget * widget,
1015 GtkRequisition * requisition)
1017 GtkRequisition child_requisition = {0};
1018 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1019 GtkWidget *child = gtk_bin_get_child (GTK_BIN (widget));
1021 if (child && GTK_WIDGET_VISIBLE (child))
1023 gtk_widget_size_request (child, &child_requisition);
1026 if (priv->hscrollbar_policy == GTK_POLICY_NEVER) {
1027 requisition->width = child_requisition.width;
1029 switch (priv->size_request_policy) {
1030 case HILDON_SIZE_REQUEST_CHILDREN:
1031 requisition->width = MIN (PANNABLE_MAX_WIDTH,
1032 child_requisition.width);
1034 case HILDON_SIZE_REQUEST_MINIMUM:
1036 requisition->width = priv->indicator_width;
1040 if (priv->vscrollbar_policy == GTK_POLICY_NEVER) {
1041 requisition->height = child_requisition.height;
1043 switch (priv->size_request_policy) {
1044 case HILDON_SIZE_REQUEST_CHILDREN:
1045 requisition->height = MIN (PANNABLE_MAX_HEIGHT,
1046 child_requisition.height);
1048 case HILDON_SIZE_REQUEST_MINIMUM:
1050 requisition->height = priv->indicator_width;
1054 requisition->width += 2 * GTK_CONTAINER (widget)->border_width;
1055 requisition->height += 2 * GTK_CONTAINER (widget)->border_width;
1059 hildon_pannable_area_child_allocate_calculate (GtkWidget * widget,
1060 GtkAllocation * allocation,
1061 GtkAllocation * child_allocation)
1064 HildonPannableAreaPrivate *priv;
1066 border_width = GTK_CONTAINER (widget)->border_width;
1068 priv = HILDON_PANNABLE_AREA (widget)->priv;
1070 child_allocation->x = 0;
1071 child_allocation->y = 0;
1072 child_allocation->width = MAX (allocation->width - 2 * border_width -
1073 (priv->vscroll_visible ? priv->vscroll_rect.width : 0), 0);
1074 child_allocation->height = MAX (allocation->height - 2 * border_width -
1075 (priv->hscroll_visible ? priv->hscroll_rect.height : 0), 0);
1077 if (priv->overshot_dist_y > 0) {
1078 child_allocation->y = MIN (child_allocation->y + priv->overshot_dist_y,
1079 child_allocation->height);
1080 child_allocation->height = MAX (child_allocation->height - priv->overshot_dist_y, 0);
1081 } else if (priv->overshot_dist_y < 0) {
1082 child_allocation->height = MAX (child_allocation->height + priv->overshot_dist_y, 0);
1085 if (priv->overshot_dist_x > 0) {
1086 child_allocation->x = MIN (child_allocation->x + priv->overshot_dist_x,
1087 child_allocation->width);
1088 child_allocation->width = MAX (child_allocation->width - priv->overshot_dist_x, 0);
1089 } else if (priv->overshot_dist_x < 0) {
1090 child_allocation->width = MAX (child_allocation->width + priv->overshot_dist_x, 0);
1095 hildon_pannable_area_size_allocate (GtkWidget * widget,
1096 GtkAllocation * allocation)
1098 GtkAllocation child_allocation;
1099 HildonPannableAreaPrivate *priv;
1100 GtkWidget *child = gtk_bin_get_child (GTK_BIN (widget));
1103 border_width = GTK_CONTAINER (widget)->border_width;
1105 widget->allocation = *allocation;
1107 priv = HILDON_PANNABLE_AREA (widget)->priv;
1109 if (GTK_WIDGET_REALIZED (widget)) {
1110 gdk_window_move_resize (widget->window,
1111 allocation->x + border_width,
1112 allocation->y + border_width,
1113 allocation->width - border_width * 2,
1114 allocation->height - border_width * 2);
1115 gdk_window_move_resize (priv->event_window,
1118 allocation->width - border_width * 2,
1119 allocation->height - border_width * 2);
1122 if (child && GTK_WIDGET_VISIBLE (child)) {
1124 hildon_pannable_area_child_allocate_calculate (widget,
1128 gtk_widget_size_allocate (child, &child_allocation);
1130 if (hildon_pannable_area_check_scrollbars (HILDON_PANNABLE_AREA (widget))) {
1131 hildon_pannable_area_child_allocate_calculate (widget,
1135 gtk_widget_size_allocate (child, &child_allocation);
1138 /* we have to do this after child size_allocate because page_size is
1139 * changed when we allocate the size of the children */
1140 if (priv->overshot_dist_y < 0) {
1141 gtk_adjustment_set_value (priv->vadjust, priv->vadjust->upper -
1142 priv->vadjust->page_size);
1145 if (priv->overshot_dist_x < 0) {
1146 gtk_adjustment_set_value (priv->hadjust, priv->hadjust->upper -
1147 priv->hadjust->page_size);
1151 hildon_pannable_area_check_scrollbars (HILDON_PANNABLE_AREA (widget));
1156 hildon_pannable_area_style_set (GtkWidget * widget,
1157 GtkStyle * previous_style)
1159 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1161 GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->
1162 style_set (widget, previous_style);
1164 gtk_widget_style_get (widget, "indicator-width", &priv->indicator_width, NULL);
1168 hildon_pannable_area_map (GtkWidget * widget)
1170 HildonPannableAreaPrivate *priv;
1172 priv = HILDON_PANNABLE_AREA (widget)->priv;
1174 gdk_window_show (widget->window);
1176 if (priv->event_window != NULL && !priv->enabled)
1177 gdk_window_show (priv->event_window);
1179 (*GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->map) (widget);
1181 if (priv->event_window != NULL && priv->enabled)
1182 gdk_window_show (priv->event_window);
1186 hildon_pannable_area_unmap (GtkWidget * widget)
1188 HildonPannableAreaPrivate *priv;
1190 priv = HILDON_PANNABLE_AREA (widget)->priv;
1192 if (priv->event_window != NULL)
1193 gdk_window_hide (priv->event_window);
1195 gdk_window_hide (widget->window);
1197 (*GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->unmap) (widget);
1201 hildon_pannable_area_grab_notify (GtkWidget *widget,
1202 gboolean was_grabbed,
1205 /* an internal widget has grabbed the focus and now has returned it,
1206 we have to do some release actions */
1208 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1210 priv->scroll_indicator_event_interrupt = 0;
1212 if ((!priv->scroll_indicator_timeout)&&(priv->scroll_indicator_alpha)>0.1) {
1213 priv->scroll_delay_counter = priv->scrollbar_fade_delay;
1215 hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget),
1216 priv->scroll_indicator_alpha);
1219 priv->last_type = 3;
1220 priv->moved = FALSE;
1224 #if USE_CAIRO_SCROLLBARS == 1
1227 rgb_from_gdkcolor (GdkColor *color, gdouble *r, gdouble *g, gdouble *b)
1229 *r = (color->red >> 8) / 255.0;
1230 *g = (color->green >> 8) / 255.0;
1231 *b = (color->blue >> 8) / 255.0;
1235 hildon_pannable_draw_vscroll (GtkWidget * widget,
1236 GdkColor *back_color,
1237 GdkColor *scroll_color)
1239 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1242 cairo_pattern_t *pattern;
1244 gint radius = (priv->vscroll_rect.width/2) - 1;
1246 cr = gdk_cairo_create(widget->window);
1248 /* Draw the background */
1249 rgb_from_gdkcolor (back_color, &r, &g, &b);
1250 cairo_set_source_rgb (cr, r, g, b);
1251 cairo_rectangle(cr, priv->vscroll_rect.x, priv->vscroll_rect.y,
1252 priv->vscroll_rect.width,
1253 priv->vscroll_rect.height);
1254 cairo_fill_preserve (cr);
1257 /* Calculate the scroll bar height and position */
1258 y = ((priv->vadjust->value - priv->vadjust->lower) / (priv->vadjust->upper - priv->vadjust->lower)) *
1259 (widget->allocation.height -
1260 (priv->hscroll_visible ? priv->indicator_width : 0));
1261 height = ((((priv->vadjust->value - priv->vadjust->lower) +
1262 priv->vadjust->page_size) /
1263 (priv->vadjust->upper - priv->vadjust->lower)) *
1264 (widget->allocation.height -
1265 (priv->hscroll_visible ? priv->indicator_width : 0))) - y;
1267 /* Set a minimum height */
1268 height = MAX (SCROLL_BAR_MIN_SIZE, height);
1270 /* Check the max y position */
1271 y = MIN (y, widget->allocation.height -
1272 (priv->hscroll_visible ? priv->hscroll_rect.height : 0) -
1275 /* Draw the scrollbar */
1276 rgb_from_gdkcolor (scroll_color, &r, &g, &b);
1278 pattern = cairo_pattern_create_linear(radius+1, y, radius+1,y + height);
1279 cairo_pattern_add_color_stop_rgb(pattern, 0, r, g, b);
1280 cairo_pattern_add_color_stop_rgb(pattern, 1, r/2, g/2, b/2);
1281 cairo_set_source(cr, pattern);
1283 cairo_pattern_destroy(pattern);
1285 cairo_arc(cr, priv->vscroll_rect.x + radius + 1, y + radius + 1, radius, G_PI, 0);
1286 cairo_line_to(cr, priv->vscroll_rect.x + (radius * 2) + 1, y + height - radius);
1287 cairo_arc(cr, priv->vscroll_rect.x + radius + 1, y + height - radius, radius, 0, G_PI);
1288 cairo_line_to(cr, priv->vscroll_rect.x + 1, y + height - radius);
1291 cairo_paint_with_alpha(cr, priv->scroll_indicator_alpha);
1297 hildon_pannable_draw_hscroll (GtkWidget * widget,
1298 GdkColor *back_color,
1299 GdkColor *scroll_color)
1301 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1304 cairo_pattern_t *pattern;
1306 gint radius = (priv->hscroll_rect.height/2) - 1;
1308 cr = gdk_cairo_create(widget->window);
1310 /* Draw the background */
1311 rgb_from_gdkcolor (back_color, &r, &g, &b);
1312 cairo_set_source_rgb (cr, r, g, b);
1313 cairo_rectangle(cr, priv->hscroll_rect.x, priv->hscroll_rect.y,
1314 priv->hscroll_rect.width,
1315 priv->hscroll_rect.height);
1316 cairo_fill_preserve (cr);
1319 /* calculate the scrollbar width and position */
1320 x = ((priv->hadjust->value - priv->hadjust->lower) / (priv->hadjust->upper - priv->hadjust->lower)) *
1321 (widget->allocation.width - (priv->vscroll_visible ? priv->indicator_width : 0));
1322 width =((((priv->hadjust->value - priv->hadjust->lower) +
1323 priv->hadjust->page_size) / (priv->hadjust->upper - priv->hadjust->lower)) *
1324 (widget->allocation.width -
1325 (priv->vscroll_visible ? priv->indicator_width : 0))) - x;
1327 /* Set a minimum width */
1328 width = MAX (SCROLL_BAR_MIN_SIZE, width);
1330 /* Check the max x position */
1331 x = MIN (x, widget->allocation.width -
1332 (priv->vscroll_visible ? priv->vscroll_rect.width : 0) -
1335 /* Draw the scrollbar */
1336 rgb_from_gdkcolor (scroll_color, &r, &g, &b);
1338 pattern = cairo_pattern_create_linear(x, radius+1, x+width, radius+1);
1339 cairo_pattern_add_color_stop_rgb(pattern, 0, r, g, b);
1340 cairo_pattern_add_color_stop_rgb(pattern, 1, r/2, g/2, b/2);
1341 cairo_set_source(cr, pattern);
1343 cairo_pattern_destroy(pattern);
1345 cairo_arc_negative(cr, x + radius + 1, priv->hscroll_rect.y + radius + 1, radius, 3*G_PI_2, G_PI_2);
1346 cairo_line_to(cr, x + width - radius, priv->hscroll_rect.y + (radius * 2) + 1);
1347 cairo_arc_negative(cr, x + width - radius, priv->hscroll_rect.y + radius + 1, radius, G_PI_2, 3*G_PI_2);
1348 cairo_line_to(cr, x + width - radius, priv->hscroll_rect.y + 1);
1351 cairo_paint_with_alpha(cr, priv->scroll_indicator_alpha);
1356 #else /* USE_CAIRO_SCROLLBARS */
1359 tranparency_color (GdkColor *color,
1362 gdouble transparency)
1366 diff = colora.red - colorb.red;
1367 color->red = colora.red-diff*transparency;
1369 diff = colora.green - colorb.green;
1370 color->green = colora.green-diff*transparency;
1372 diff = colora.blue - colorb.blue;
1373 color->blue = colora.blue-diff*transparency;
1377 hildon_pannable_draw_vscroll (GtkWidget *widget,
1378 GdkColor *back_color,
1379 GdkColor *scroll_color)
1381 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1383 GdkColor transp_color;
1384 GdkGC *gc = widget->style->fg_gc[GTK_STATE_INSENSITIVE];
1386 gdk_draw_rectangle (widget->window,
1387 widget->style->bg_gc[GTK_STATE_NORMAL],
1389 priv->vscroll_rect.x, priv->vscroll_rect.y,
1390 priv->vscroll_rect.width,
1391 priv->vscroll_rect.height);
1393 y = ((priv->vadjust->value - priv->vadjust->lower) / (priv->vadjust->upper - priv->vadjust->lower)) *
1394 (widget->allocation.height - (priv->hscroll_visible ? priv->indicator_width : 0));
1395 height = ((((priv->vadjust->value - priv->vadjust->lower) + priv->vadjust->page_size) /
1396 (priv->vadjust->upper - priv->vadjust->lower)) *
1397 (widget->allocation.height -
1398 (priv->hscroll_visible ? priv->indicator_width : 0))) - y;
1400 /* Set a minimum height */
1401 height = MAX (SCROLL_BAR_MIN_SIZE, height);
1403 /* Check the max y position */
1404 y = MIN (y, widget->allocation.height -
1405 (priv->hscroll_visible ? priv->hscroll_rect.height : 0) -
1408 if (priv->scroll_indicator_alpha < 1.0) {
1409 tranparency_color (&transp_color, *back_color, *scroll_color,
1410 priv->scroll_indicator_alpha);
1412 gdk_gc_set_rgb_fg_color (priv->scrollbars_gc, &transp_color);
1414 gc = priv->scrollbars_gc;
1417 gdk_draw_rectangle (widget->window, gc,
1418 TRUE, priv->vscroll_rect.x, y,
1419 priv->vscroll_rect.width, height);
1423 hildon_pannable_draw_hscroll (GtkWidget *widget,
1424 GdkColor *back_color,
1425 GdkColor *scroll_color)
1427 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1429 GdkColor transp_color;
1430 GdkGC *gc = widget->style->fg_gc[GTK_STATE_INSENSITIVE];
1432 gdk_draw_rectangle (widget->window,
1433 widget->style->bg_gc[GTK_STATE_INSENSITIVE],
1435 priv->hscroll_rect.x, priv->hscroll_rect.y,
1436 priv->hscroll_rect.width,
1437 priv->hscroll_rect.height);
1439 /* calculate the scrollbar width and position */
1440 x = ((priv->hadjust->value - priv->hadjust->lower) / (priv->hadjust->upper - priv->hadjust->lower)) *
1441 (widget->allocation.width - (priv->vscroll_visible ? priv->indicator_width : 0));
1442 width =((((priv->hadjust->value - priv->hadjust->lower) +
1443 priv->hadjust->page_size) / (priv->hadjust->upper - priv->hadjust->lower)) *
1444 (widget->allocation.width -
1445 (priv->vscroll_visible ? priv->indicator_width : 0))) - x;
1447 /* Set a minimum width */
1448 width = MAX (SCROLL_BAR_MIN_SIZE, width);
1450 /* Check the max x position */
1451 x = MIN (x, widget->allocation.width -
1452 (priv->vscroll_visible ? priv->vscroll_rect.width : 0) -
1455 if (priv->scroll_indicator_alpha < 1.0) {
1456 tranparency_color (&transp_color, *back_color, *scroll_color,
1457 priv->scroll_indicator_alpha);
1459 gdk_gc_set_rgb_fg_color (priv->scrollbars_gc, &transp_color);
1461 gc = priv->scrollbars_gc;
1464 gdk_draw_rectangle (widget->window, gc,
1465 TRUE, x, priv->hscroll_rect.y, width,
1466 priv->hscroll_rect.height);
1469 #endif /* USE_CAIRO_SCROLLBARS */
1472 hildon_pannable_area_initial_effect (GtkWidget * widget)
1474 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1475 gboolean hscroll_visible, vscroll_visible;
1477 if (priv->initial_hint) {
1479 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
1480 priv->vadjust->page_size);
1481 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
1482 priv->hadjust->page_size);
1484 if (priv->vscroll_visible || priv->hscroll_visible) {
1486 priv->scroll_indicator_event_interrupt = 0;
1487 priv->scroll_delay_counter = priv->scrollbar_fade_delay;
1489 hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget), 1.0);
1495 hildon_pannable_area_launch_fade_timeout (HildonPannableArea * area,
1498 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (area)->priv;
1500 priv->scroll_indicator_alpha = alpha;
1502 if (!priv->scroll_indicator_timeout)
1503 priv->scroll_indicator_timeout =
1504 gdk_threads_add_timeout (SCROLL_FADE_TIMEOUT,
1505 (GSourceFunc) hildon_pannable_area_scroll_indicator_fade,
1510 hildon_pannable_area_adjust_changed (HildonPannableArea * area,
1513 if (GTK_WIDGET_REALIZED (area))
1514 hildon_pannable_area_refresh (area);
1518 hildon_pannable_area_adjust_value_changed (HildonPannableArea * area,
1521 if (GTK_WIDGET_REALIZED (area)) {
1522 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (area)->priv;
1524 hildon_pannable_area_redraw (area);
1526 if ((priv->vscroll_visible) || (priv->hscroll_visible)) {
1527 priv->scroll_indicator_event_interrupt = 0;
1528 priv->scroll_delay_counter = priv->scrollbar_fade_delay;
1530 hildon_pannable_area_launch_fade_timeout (area, 1.0);
1536 hildon_pannable_area_redraw (HildonPannableArea * area)
1538 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (area)->priv;
1540 /* Redraw scroll indicators */
1541 if (GTK_WIDGET_DRAWABLE (area)) {
1542 if (priv->hscroll_visible) {
1543 gdk_window_invalidate_rect (GTK_WIDGET (area)->window,
1544 &priv->hscroll_rect, FALSE);
1547 if (priv->vscroll_visible) {
1548 gdk_window_invalidate_rect (GTK_WIDGET (area)->window,
1549 &priv->vscroll_rect, FALSE);
1555 hildon_pannable_area_scroll_indicator_fade(HildonPannableArea * area)
1557 HildonPannableAreaPrivate *priv = area->priv;
1559 /* if moving do not fade out */
1560 if (((ABS (priv->vel_y)>1.0)||
1561 (ABS (priv->vel_x)>1.0))&&(!priv->clicked)) {
1566 if (priv->scroll_indicator_event_interrupt) {
1567 /* Stop a fade out, and fade back in */
1568 if (priv->scroll_indicator_alpha > 0.9) {
1569 priv->scroll_indicator_alpha = 1.0;
1570 priv->scroll_indicator_timeout = 0;
1574 priv->scroll_indicator_alpha += 0.2;
1575 hildon_pannable_area_redraw (area);
1581 if ((priv->scroll_indicator_alpha > 0.9) &&
1582 (priv->scroll_delay_counter > 0)) {
1583 priv->scroll_delay_counter--;
1588 if (!priv->scroll_indicator_event_interrupt) {
1589 /* Continue fade out */
1590 if (priv->scroll_indicator_alpha < 0.1) {
1591 priv->scroll_indicator_timeout = 0;
1592 priv->scroll_indicator_alpha = 0.0;
1596 priv->scroll_indicator_alpha -= 0.2;
1597 hildon_pannable_area_redraw (area);
1607 hildon_pannable_area_expose_event (GtkWidget * widget,
1608 GdkEventExpose * event)
1611 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1612 #if USE_CAIRO_SCROLLBARS == 1
1613 GdkColor back_color = widget->style->bg[GTK_STATE_NORMAL];
1614 GdkColor scroll_color = widget->style->base[GTK_STATE_SELECTED];
1615 #else /* USE_CAIRO_SCROLLBARS */
1616 GdkColor back_color = widget->style->bg[GTK_STATE_NORMAL];
1617 GdkColor scroll_color = widget->style->fg[GTK_STATE_INSENSITIVE];
1620 if (gtk_bin_get_child (GTK_BIN (widget))) {
1622 if (priv->scroll_indicator_alpha > 0.1) {
1623 if (priv->vscroll_visible) {
1624 hildon_pannable_draw_vscroll (widget, &back_color, &scroll_color);
1626 if (priv->hscroll_visible) {
1627 hildon_pannable_draw_hscroll (widget, &back_color, &scroll_color);
1631 /* draw overshooting rectangles */
1632 if (priv->overshot_dist_y > 0) {
1633 gint overshot_height;
1635 overshot_height = MIN (priv->overshot_dist_y, widget->allocation.height -
1636 (priv->hscroll_visible ? priv->hscroll_rect.height : 0));
1638 gdk_draw_rectangle (widget->window,
1639 widget->style->bg_gc[GTK_STATE_NORMAL],
1643 widget->allocation.width -
1644 (priv->vscroll_visible ? priv->vscroll_rect.width : 0),
1646 } else if (priv->overshot_dist_y < 0) {
1647 gint overshot_height;
1651 MAX (priv->overshot_dist_y,
1652 -(widget->allocation.height -
1653 (priv->hscroll_visible ? priv->hscroll_rect.height : 0)));
1655 overshot_y = MAX (widget->allocation.height +
1657 (priv->hscroll_visible ? priv->hscroll_rect.height : 0), 0);
1659 gdk_draw_rectangle (widget->window,
1660 widget->style->bg_gc[GTK_STATE_NORMAL],
1664 widget->allocation.width -
1665 priv->vscroll_rect.width,
1669 if (priv->overshot_dist_x > 0) {
1670 gint overshot_width;
1672 overshot_width = MIN (priv->overshot_dist_x, widget->allocation.width -
1673 (priv->vscroll_visible ? priv->vscroll_rect.width : 0));
1675 gdk_draw_rectangle (widget->window,
1676 widget->style->bg_gc[GTK_STATE_NORMAL],
1681 widget->allocation.height -
1682 (priv->hscroll_visible ? priv->hscroll_rect.height : 0));
1683 } else if (priv->overshot_dist_x < 0) {
1684 gint overshot_width;
1688 MAX (priv->overshot_dist_x,
1689 -(widget->allocation.width -
1690 (priv->vscroll_visible ? priv->vscroll_rect.width : 0)));
1692 overshot_x = MAX (widget->allocation.width +
1694 (priv->vscroll_visible ? priv->vscroll_rect.width : 0), 0);
1696 gdk_draw_rectangle (widget->window,
1697 widget->style->bg_gc[GTK_STATE_NORMAL],
1702 widget->allocation.height -
1703 priv->hscroll_rect.height);
1708 if (G_UNLIKELY (priv->initial_effect)) {
1710 hildon_pannable_area_initial_effect (widget);
1712 priv->initial_effect = FALSE;
1715 return GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->expose_event (widget, event);
1719 hildon_pannable_area_get_topmost (GdkWindow * window,
1721 gint * tx, gint * ty,
1724 /* Find the GdkWindow at the given point, by recursing from a given
1725 * parent GdkWindow. Optionally return the co-ordinates transformed
1726 * relative to the child window.
1729 GList *c, *children;
1730 GdkWindow *selected_window = NULL;
1732 gdk_drawable_get_size (GDK_DRAWABLE (window), &width, &height);
1733 if ((x < 0) || (x >= width) || (y < 0) || (y >= height))
1736 children = gdk_window_peek_children (window);
1743 selected_window = window;
1746 for (c = children; c; c = c->next) {
1747 GdkWindow *child = (GdkWindow *) c->data;
1750 gdk_drawable_get_size (GDK_DRAWABLE (child), &width, &height);
1751 gdk_window_get_position (child, &wx, &wy);
1753 if ((x >= wx) && (x < (wx + width)) && (y >= wy) && (y < (wy + height)) &&
1754 (gdk_window_is_visible (child))) {
1756 if (gdk_window_peek_children (child)) {
1757 selected_window = hildon_pannable_area_get_topmost (child, x-wx, y-wy,
1759 if (!selected_window) {
1764 selected_window = child;
1767 if ((gdk_window_get_events (child)&mask)) {
1772 selected_window = child;
1778 return selected_window;
1782 synth_crossing (GdkWindow * child,
1784 gint x_root, gint y_root,
1785 guint32 time, gboolean in)
1787 GdkEventCrossing *crossing_event;
1788 GdkEventType type = in ? GDK_ENTER_NOTIFY : GDK_LEAVE_NOTIFY;
1790 /* Send synthetic enter event */
1791 crossing_event = (GdkEventCrossing *) gdk_event_new (type);
1792 ((GdkEventAny *) crossing_event)->type = type;
1793 ((GdkEventAny *) crossing_event)->window = g_object_ref (child);
1794 ((GdkEventAny *) crossing_event)->send_event = FALSE;
1795 crossing_event->subwindow = g_object_ref (child);
1796 crossing_event->time = time;
1797 crossing_event->x = x;
1798 crossing_event->y = y;
1799 crossing_event->x_root = x_root;
1800 crossing_event->y_root = y_root;
1801 crossing_event->mode = GDK_CROSSING_NORMAL;
1802 crossing_event->detail = GDK_NOTIFY_UNKNOWN;
1803 crossing_event->focus = FALSE;
1804 crossing_event->state = 0;
1805 gdk_event_put ((GdkEvent *) crossing_event);
1806 gdk_event_free ((GdkEvent *) crossing_event);
1810 hildon_pannable_area_button_press_cb (GtkWidget * widget,
1811 GdkEventButton * event)
1814 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1816 if ((!priv->enabled) || (event->button != 1) ||
1817 ((event->time == priv->last_time) &&
1818 (priv->last_type == 1)) || (gtk_bin_get_child (GTK_BIN (widget)) == NULL))
1821 priv->scroll_indicator_event_interrupt = 1;
1823 hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget),
1824 priv->scroll_indicator_alpha);
1826 priv->last_time = event->time;
1827 priv->last_type = 1;
1829 priv->scroll_to_x = -1;
1830 priv->scroll_to_y = -1;
1832 if (priv->clicked && priv->child) {
1833 /* Widget stole focus on last click, send crossing-out event */
1834 synth_crossing (priv->child, 0, 0, event->x_root, event->y_root,
1835 event->time, FALSE);
1843 /* Don't allow a click if we're still moving fast */
1844 if ((ABS (priv->vel_x) <= (priv->vmax * priv->vfast_factor)) &&
1845 (ABS (priv->vel_y) <= (priv->vmax * priv->vfast_factor)))
1847 hildon_pannable_area_get_topmost (gtk_bin_get_child (GTK_BIN (widget))->window,
1848 event->x, event->y, &x, &y, GDK_BUTTON_PRESS_MASK);
1852 priv->clicked = TRUE;
1854 /* Stop scrolling on mouse-down (so you can flick, then hold to stop) */
1860 gdk_drawable_get_size (priv->child, &priv->child_width,
1861 &priv->child_height);
1862 priv->last_in = TRUE;
1864 g_object_add_weak_pointer ((GObject *) priv->child,
1865 (gpointer) & priv->child);
1867 event = (GdkEventButton *) gdk_event_copy ((GdkEvent *) event);
1873 synth_crossing (priv->child, x, y, event->x_root,
1874 event->y_root, event->time, TRUE);
1876 /* Send synthetic click (button press/release) event */
1877 ((GdkEventAny *) event)->window = g_object_ref (priv->child);
1879 gdk_event_put ((GdkEvent *) event);
1880 gdk_event_free ((GdkEvent *) event);
1888 hildon_pannable_area_check_scrollbars (HildonPannableArea * area)
1890 HildonPannableAreaPrivate *priv = area->priv;
1891 gboolean prev_hscroll_visible, prev_vscroll_visible;
1893 prev_hscroll_visible = priv->hscroll_visible;
1894 prev_vscroll_visible = priv->vscroll_visible;
1896 if (!gtk_bin_get_child (GTK_BIN (area))) {
1897 priv->vscroll_visible = FALSE;
1898 priv->hscroll_visible = FALSE;
1900 switch (priv->hscrollbar_policy) {
1901 case GTK_POLICY_ALWAYS:
1902 priv->hscroll_visible = TRUE;
1904 case GTK_POLICY_NEVER:
1905 priv->hscroll_visible = FALSE;
1908 priv->hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
1909 priv->hadjust->page_size);
1912 switch (priv->vscrollbar_policy) {
1913 case GTK_POLICY_ALWAYS:
1914 priv->vscroll_visible = TRUE;
1916 case GTK_POLICY_NEVER:
1917 priv->vscroll_visible = FALSE;
1920 priv->vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
1921 priv->vadjust->page_size);
1924 /* Store the vscroll/hscroll areas for redrawing */
1925 if (priv->vscroll_visible) {
1926 GtkAllocation *allocation = >K_WIDGET (area)->allocation;
1927 priv->vscroll_rect.x = allocation->width - priv->indicator_width;
1928 priv->vscroll_rect.y = 0;
1929 priv->vscroll_rect.width = priv->indicator_width;
1930 priv->vscroll_rect.height = allocation->height -
1931 (priv->hscroll_visible ? priv->indicator_width : 0);
1933 if (priv->hscroll_visible) {
1934 GtkAllocation *allocation = >K_WIDGET (area)->allocation;
1935 priv->hscroll_rect.y = allocation->height - priv->indicator_width;
1936 priv->hscroll_rect.x = 0;
1937 priv->hscroll_rect.height = priv->indicator_width;
1938 priv->hscroll_rect.width = allocation->width -
1939 (priv->vscroll_visible ? priv->indicator_width : 0);
1943 return ((priv->hscroll_visible != prev_hscroll_visible) ||
1944 (priv->vscroll_visible != prev_vscroll_visible));
1948 hildon_pannable_area_refresh (HildonPannableArea * area)
1950 if (GTK_WIDGET_DRAWABLE (area) &&
1951 hildon_pannable_area_check_scrollbars (area)) {
1952 gtk_widget_queue_resize (GTK_WIDGET (area));
1954 hildon_pannable_area_redraw (area);
1958 /* Scroll by a particular amount (in pixels). Optionally, return if
1959 * the scroll on a particular axis was successful.
1962 hildon_pannable_axis_scroll (HildonPannableArea *area,
1963 GtkAdjustment *adjust,
1967 gint *overshot_dist,
1973 HildonPannableAreaPrivate *priv = area->priv;
1975 dist = gtk_adjustment_get_value (adjust) - inc;
1978 * We use overshot_dist to define the distance of the current overshoot,
1979 * and overshooting to define the direction/whether or not we are overshot
1981 if (!(*overshooting)) {
1983 /* Initiation of the overshoot happens when the finger is released
1984 * and the current position of the pannable contents are out of range
1986 if (dist < adjust->lower) {
1989 dist = adjust->lower;
1991 if (overshoot_max!=0) {
1994 *overshot_dist = CLAMP (*overshot_dist + *vel, 0, overshoot_max);
1995 *vel = MIN (priv->vmax_overshooting, *vel);
1996 gtk_widget_queue_resize (GTK_WIDGET (area));
2000 } else if (dist > adjust->upper - adjust->page_size) {
2003 dist = adjust->upper - adjust->page_size;
2005 if (overshoot_max!=0) {
2008 *overshot_dist = CLAMP (*overshot_dist + *vel, -overshoot_max, 0);
2009 *vel = MAX (-priv->vmax_overshooting, *vel);
2010 gtk_widget_queue_resize (GTK_WIDGET (area));
2015 if ((*scroll_to) != -1) {
2016 if (((inc < 0)&&(*scroll_to <= dist))||
2017 ((inc > 0)&&(*scroll_to >= dist))) {
2025 gtk_adjustment_set_value (adjust, dist);
2027 if (!priv->clicked) {
2029 /* When the overshoot has started we continue for
2030 * PROP_BOUNCE_STEPS more steps into the overshoot before we
2031 * reverse direction. The deceleration factor is calculated
2032 * based on the percentage distance from the first item with
2033 * each iteration, therefore always returning us to the
2034 * top/bottom most element
2036 if (*overshot_dist > 0) {
2038 if ((*overshooting < priv->bounce_steps) && (*vel > 0)) {
2040 *vel = (((gdouble)*overshot_dist)/overshoot_max) * (*vel);
2041 } else if ((*overshooting >= priv->bounce_steps) && (*vel > 0)) {
2043 } else if ((*overshooting > 1) && (*vel < 0)) {
2044 /* we add the MIN in order to avoid very small speeds */
2045 *vel = MIN ((((gdouble)*overshot_dist)*0.4) * -1, -2.0);
2048 *overshot_dist = CLAMP (*overshot_dist + *vel, 0, overshoot_max);
2050 gtk_widget_queue_resize (GTK_WIDGET (area));
2052 } else if (*overshot_dist < 0) {
2054 if ((*overshooting < priv->bounce_steps) && (*vel < 0)) {
2056 *vel = (((gdouble)*overshot_dist)/overshoot_max) * (*vel) * -1;
2057 } else if ((*overshooting >= priv->bounce_steps) && (*vel < 0)) {
2059 } else if ((*overshooting > 1) && (*vel > 0)) {
2060 /* we add the MAX in order to avoid very small speeds */
2061 *vel = MAX ((((gdouble)*overshot_dist)*0.4) * -1, 2.0);
2064 *overshot_dist = CLAMP (*overshot_dist + (*vel), -overshoot_max, 0);
2066 gtk_widget_queue_resize (GTK_WIDGET (area));
2071 gtk_widget_queue_resize (GTK_WIDGET (area));
2075 gint overshot_dist_old = *overshot_dist;
2077 if (*overshot_dist > 0) {
2078 *overshot_dist = CLAMP ((*overshot_dist) + inc, 0, overshoot_max);
2079 } else if (*overshot_dist < 0) {
2080 *overshot_dist = CLAMP ((*overshot_dist) + inc, -1 * overshoot_max, 0);
2083 gtk_adjustment_set_value (adjust, dist);
2086 if (*overshot_dist != overshot_dist_old)
2087 gtk_widget_queue_resize (GTK_WIDGET (area));
2093 hildon_pannable_area_scroll (HildonPannableArea *area,
2094 gdouble x, gdouble y)
2097 HildonPannableAreaPrivate *priv = area->priv;
2098 gboolean hscroll_visible, vscroll_visible;
2100 if (gtk_bin_get_child (GTK_BIN (area)) == NULL)
2103 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
2104 priv->vadjust->page_size);
2105 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
2106 priv->hadjust->page_size);
2111 if (vscroll_visible) {
2112 hildon_pannable_axis_scroll (area, priv->vadjust, &priv->vel_y, y,
2113 &priv->overshooting_y, &priv->overshot_dist_y,
2114 &priv->scroll_to_y, priv->vovershoot_max, &sy);
2119 if (hscroll_visible) {
2120 hildon_pannable_axis_scroll (area, priv->hadjust, &priv->vel_x, x,
2121 &priv->overshooting_x, &priv->overshot_dist_x,
2122 &priv->scroll_to_x, priv->hovershoot_max, &sx);
2127 /* If the scroll on a particular axis wasn't succesful, reset the
2128 * initial scroll position to the new mouse co-ordinate. This means
2129 * when you get to the top of the page, dragging down works immediately.
2131 if (priv->mode == HILDON_PANNABLE_AREA_MODE_ACCEL) {
2143 hildon_pannable_area_timeout (HildonPannableArea * area)
2145 HildonPannableAreaPrivate *priv = area->priv;
2147 if ((!priv->enabled) || (priv->mode == HILDON_PANNABLE_AREA_MODE_PUSH)) {
2153 if (!priv->clicked) {
2154 /* Decelerate gradually when pointer is raised */
2155 if ((!priv->overshot_dist_y) &&
2156 (!priv->overshot_dist_x)) {
2158 /* in case we move to a specific point do not decelerate when arriving */
2159 if ((priv->scroll_to_x != -1)||(priv->scroll_to_y != -1)) {
2161 if (ABS (priv->vel_x) >= 1.5) {
2162 priv->vel_x *= priv->decel;
2165 if (ABS (priv->vel_y) >= 1.5) {
2166 priv->vel_y *= priv->decel;
2170 if ((!priv->low_friction_mode) ||
2171 ((priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ) &&
2172 (ABS (priv->vel_x) < 0.8*priv->vmax)))
2173 priv->vel_x *= priv->decel;
2175 if ((!priv->low_friction_mode) ||
2176 ((priv->mov_mode&HILDON_MOVEMENT_MODE_VERT) &&
2177 (ABS (priv->vel_y) < 0.8*priv->vmax)))
2178 priv->vel_y *= priv->decel;
2180 if ((ABS (priv->vel_x) < 1.0) && (ABS (priv->vel_y) < 1.0)) {
2189 } else if (priv->mode == HILDON_PANNABLE_AREA_MODE_AUTO) {
2195 hildon_pannable_area_scroll (area, priv->vel_x, priv->vel_y);
2201 hildon_pannable_area_calculate_velocity (gdouble *vel,
2205 gdouble drag_inertia,
2211 if (ABS (dist) >= RATIO_TOLERANCE) {
2212 rawvel = (dist / ABS (delta)) * force;
2213 *vel = *vel * (1 - drag_inertia) +
2214 rawvel * drag_inertia;
2215 *vel = *vel > 0 ? MIN (*vel, vmax)
2216 : MAX (*vel, -1 * vmax);
2221 hildon_pannable_area_motion_event_scroll_timeout (HildonPannableArea *area)
2223 HildonPannableAreaPrivate *priv = area->priv;
2225 if ((priv->motion_x != 0)||(priv->motion_y != 0))
2226 hildon_pannable_area_scroll (area, priv->motion_x, priv->motion_y);
2228 priv->motion_event_scroll_timeout = 0;
2234 hildon_pannable_area_motion_event_scroll (HildonPannableArea *area,
2235 gdouble x, gdouble y)
2237 HildonPannableAreaPrivate *priv = area->priv;
2239 if (priv->motion_event_scroll_timeout) {
2241 priv->motion_x += x;
2242 priv->motion_y += y;
2246 /* we do not delay the first event but the next ones */
2247 hildon_pannable_area_scroll (area, x, y);
2252 priv->motion_event_scroll_timeout = gdk_threads_add_timeout
2253 ((gint) (1000.0 / (gdouble) MOTION_EVENTS_PER_SECOND),
2254 (GSourceFunc) hildon_pannable_area_motion_event_scroll_timeout, area);
2259 hildon_pannable_area_motion_notify_cb (GtkWidget * widget,
2260 GdkEventMotion * event)
2262 HildonPannableArea *area = HILDON_PANNABLE_AREA (widget);
2263 HildonPannableAreaPrivate *priv = area->priv;
2267 if (gtk_bin_get_child (GTK_BIN (widget)) == NULL)
2270 if ((!priv->enabled) || (!priv->clicked) ||
2271 ((event->time == priv->last_time) && (priv->last_type == 2))) {
2272 gdk_window_get_pointer (widget->window, NULL, NULL, 0);
2276 if (priv->last_type == 1) {
2277 priv->first_drag = TRUE;
2280 x = event->x - priv->x;
2281 y = event->y - priv->y;
2283 if (priv->first_drag && (!priv->moved) &&
2284 ((ABS (x) > (priv->panning_threshold))
2285 || (ABS (y) > (priv->panning_threshold)))) {
2290 if (priv->first_drag) {
2291 gboolean vscroll_visible;
2292 gboolean hscroll_visible;
2294 if (ABS (priv->iy - event->y) >=
2295 ABS (priv->ix - event->x)) {
2297 g_signal_emit (area,
2298 pannable_area_signals[VERTICAL_MOVEMENT],
2299 0, (priv->iy > event->y) ?
2300 HILDON_MOVEMENT_UP :
2301 HILDON_MOVEMENT_DOWN,
2302 (gdouble)priv->ix, (gdouble)priv->iy);
2304 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
2305 priv->vadjust->page_size);
2307 if (!((vscroll_visible)&&
2308 (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT))) {
2310 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
2311 priv->hadjust->page_size);
2313 /* even in case we do not have to move we check if this
2314 could be a fake horizontal movement */
2315 if (!((hscroll_visible)&&
2316 (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ)) ||
2317 (ABS (priv->iy - event->y) -
2318 ABS (priv->ix - event->x) >= priv->direction_error_margin))
2319 priv->moved = FALSE;
2323 g_signal_emit (area,
2324 pannable_area_signals[HORIZONTAL_MOVEMENT],
2325 0, (priv->ix > event->x) ?
2326 HILDON_MOVEMENT_LEFT :
2327 HILDON_MOVEMENT_RIGHT,
2328 (gdouble)priv->ix, (gdouble)priv->iy);
2330 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
2331 priv->hadjust->page_size);
2333 if (!((hscroll_visible)&&
2334 (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ))) {
2336 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
2337 priv->vadjust->page_size);
2339 /* even in case we do not have to move we check if this
2340 could be a fake vertical movement */
2341 if (!((vscroll_visible) &&
2342 (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT)) ||
2343 (ABS (priv->ix - event->x) -
2344 ABS (priv->iy - event->y) >= priv->direction_error_margin))
2345 priv->moved = FALSE;
2349 if ((priv->moved)&&(priv->child)) {
2352 pos_x = priv->cx + (event->x - priv->ix);
2353 pos_y = priv->cy + (event->y - priv->iy);
2355 synth_crossing (priv->child, pos_x, pos_y, event->x_root,
2356 event->y_root, event->time, FALSE);
2360 priv->first_drag = FALSE;
2362 if ((priv->mode != HILDON_PANNABLE_AREA_MODE_PUSH) &&
2363 (priv->mode != HILDON_PANNABLE_AREA_MODE_AUTO)) {
2366 priv->idle_id = gdk_threads_add_timeout ((gint)
2367 (1000.0 / (gdouble) priv->sps),
2369 hildon_pannable_area_timeout, area);
2374 switch (priv->mode) {
2375 case HILDON_PANNABLE_AREA_MODE_PUSH:
2376 /* Scroll by the amount of pixels the cursor has moved
2377 * since the last motion event.
2379 hildon_pannable_area_motion_event_scroll (area, x, y);
2383 case HILDON_PANNABLE_AREA_MODE_ACCEL:
2384 /* Set acceleration relative to the initial click */
2385 priv->ex = event->x;
2386 priv->ey = event->y;
2387 priv->vel_x = ((x > 0) ? 1 : -1) *
2389 (gdouble) widget->allocation.width) *
2390 (priv->vmax - priv->vmin)) + priv->vmin);
2391 priv->vel_y = ((y > 0) ? 1 : -1) *
2393 (gdouble) widget->allocation.height) *
2394 (priv->vmax - priv->vmin)) + priv->vmin);
2396 case HILDON_PANNABLE_AREA_MODE_AUTO:
2398 delta = event->time - priv->last_time;
2400 if (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT) {
2401 gdouble dist = event->y - priv->y;
2403 hildon_pannable_area_calculate_velocity (&priv->vel_y,
2415 if (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ) {
2416 gdouble dist = event->x - priv->x;
2418 hildon_pannable_area_calculate_velocity (&priv->vel_x,
2430 hildon_pannable_area_motion_event_scroll (area, x, y);
2432 if (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ)
2434 if (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT)
2442 } else if (priv->child) {
2446 pos_x = priv->cx + (event->x - priv->ix);
2447 pos_y = priv->cy + (event->y - priv->iy);
2449 in = (((0 <= pos_x)&&(priv->child_width >= pos_x)) &&
2450 ((0 <= pos_y)&&(priv->child_height >= pos_y)));
2452 if (((!priv->last_in)&&in)||((priv->last_in)&&(!in))) {
2454 synth_crossing (priv->child, pos_x, pos_y, event->x_root,
2455 event->y_root, event->time, in);
2461 priv->last_time = event->time;
2462 priv->last_type = 2;
2465 /* Send motion notify to child */
2466 event = (GdkEventMotion *) gdk_event_copy ((GdkEvent *) event);
2467 event->x = priv->cx + (event->x - priv->ix);
2468 event->y = priv->cy + (event->y - priv->iy);
2469 event->window = g_object_ref (priv->child);
2470 gdk_event_put ((GdkEvent *) event);
2471 gdk_event_free ((GdkEvent *) event);
2474 gdk_window_get_pointer (widget->window, NULL, NULL, 0);
2480 hildon_pannable_leave_notify_event (GtkWidget *widget,
2481 GdkEventCrossing *event)
2483 HildonPannableArea *area = HILDON_PANNABLE_AREA (widget);
2484 HildonPannableAreaPrivate *priv = area->priv;
2486 if ((priv->child)&&(priv->last_in)) {
2487 priv->last_in = FALSE;
2489 synth_crossing (priv->child, 0, 0, event->x_root,
2490 event->y_root, event->time, FALSE);
2497 hildon_pannable_area_button_release_cb (GtkWidget * widget,
2498 GdkEventButton * event)
2500 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
2504 if (((event->time == priv->last_time) && (priv->last_type == 3))
2505 || (gtk_bin_get_child (GTK_BIN (widget)) == NULL)
2506 || (!priv->clicked) || (!priv->enabled) || (event->button != 1))
2509 priv->scroll_indicator_event_interrupt = 0;
2510 priv->scroll_delay_counter = priv->scrollbar_fade_delay;
2512 /* move all the way to the last position */
2513 if (priv->motion_event_scroll_timeout) {
2514 g_source_remove (priv->motion_event_scroll_timeout);
2515 hildon_pannable_area_motion_event_scroll_timeout (HILDON_PANNABLE_AREA (widget));
2520 if (priv->last_type == 2) {
2521 gdouble delta = event->time - priv->last_time;
2523 if (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT) {
2524 gdouble dist = event->y - priv->y;
2526 if (ABS (dist) >= 1.0) {
2527 hildon_pannable_area_calculate_velocity (&priv->vel_y,
2535 priv->motion_y = dist;
2536 hildon_pannable_area_motion_event_scroll_timeout (HILDON_PANNABLE_AREA (widget));
2538 if (delta >= CURSOR_STOPPED_TIMEOUT) {
2544 if (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ) {
2545 gdouble dist = event->x - priv->x;
2547 if (ABS (dist) >= 1.0) {
2548 hildon_pannable_area_calculate_velocity (&priv->vel_x,
2555 priv->motion_x = dist;
2556 hildon_pannable_area_motion_event_scroll_timeout (HILDON_PANNABLE_AREA (widget));
2558 if (delta >= CURSOR_STOPPED_TIMEOUT) {
2565 if ((ABS (priv->vel_y) > 1.0)||
2566 (ABS (priv->vel_x) > 1.0)) {
2567 priv->scroll_indicator_alpha = 1.0;
2570 hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget),
2571 priv->scroll_indicator_alpha);
2573 priv->clicked = FALSE;
2575 if (priv->mode == HILDON_PANNABLE_AREA_MODE_AUTO ||
2576 priv->mode == HILDON_PANNABLE_AREA_MODE_ACCEL) {
2578 /* If overshoot has been initiated with a finger down, on release set max speed */
2579 if (priv->overshot_dist_y != 0) {
2580 priv->overshooting_y = priv->bounce_steps; /* Hack to stop a bounce in the finger down case */
2581 priv->vel_y = priv->vmax_overshooting;
2584 if (priv->overshot_dist_x != 0) {
2585 priv->overshooting_x = priv->bounce_steps; /* Hack to stop a bounce in the finger down case */
2586 priv->vel_x = priv->vmax_overshooting;
2589 if ((ABS (priv->vel_y) >= priv->vmin) ||
2590 (ABS (priv->vel_x) >= priv->vmin)) {
2593 priv->idle_id = gdk_threads_add_timeout ((gint) (1000.0 / (gdouble) priv->sps),
2595 hildon_pannable_area_timeout, widget);
2599 priv->last_time = event->time;
2600 priv->last_type = 3;
2603 priv->moved = FALSE;
2608 hildon_pannable_area_get_topmost (gtk_bin_get_child (GTK_BIN (widget))->window,
2609 event->x, event->y, &x, &y, GDK_BUTTON_RELEASE_MASK);
2611 event = (GdkEventButton *) gdk_event_copy ((GdkEvent *) event);
2615 /* Leave the widget if we've moved - This doesn't break selection,
2616 * but stops buttons from being clicked.
2618 if ((child != priv->child) || (priv->moved)) {
2619 /* Send synthetic leave event */
2620 synth_crossing (priv->child, x, y, event->x_root,
2621 event->y_root, event->time, FALSE);
2622 /* Send synthetic button release event */
2623 ((GdkEventAny *) event)->window = g_object_ref (priv->child);
2624 gdk_event_put ((GdkEvent *) event);
2626 /* Send synthetic button release event */
2627 ((GdkEventAny *) event)->window = g_object_ref (child);
2628 gdk_event_put ((GdkEvent *) event);
2629 /* Send synthetic leave event */
2630 synth_crossing (priv->child, x, y, event->x_root,
2631 event->y_root, event->time, FALSE);
2633 g_object_remove_weak_pointer ((GObject *) priv->child,
2634 (gpointer) & priv->child);
2636 priv->moved = FALSE;
2637 gdk_event_free ((GdkEvent *) event);
2642 /* utility event handler */
2644 hildon_pannable_area_scroll_cb (GtkWidget *widget,
2645 GdkEventScroll *event)
2647 GtkAdjustment *adj = NULL;
2648 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
2650 if ((!priv->enabled) ||
2651 (gtk_bin_get_child (GTK_BIN (widget)) == NULL))
2654 priv->scroll_indicator_event_interrupt = 0;
2655 priv->scroll_delay_counter = priv->scrollbar_fade_delay + 20;
2657 hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget), 1.0);
2659 /* Stop inertial scrolling */
2660 if (priv->idle_id) {
2663 priv->overshooting_x = 0;
2664 priv->overshooting_y = 0;
2666 if ((priv->overshot_dist_x>0)||(priv->overshot_dist_y>0)) {
2667 priv->overshot_dist_x = 0;
2668 priv->overshot_dist_y = 0;
2670 gtk_widget_queue_resize (GTK_WIDGET (widget));
2673 g_source_remove (priv->idle_id);
2677 if (event->direction == GDK_SCROLL_UP || event->direction == GDK_SCROLL_DOWN)
2678 adj = priv->vadjust;
2680 adj = priv->hadjust;
2684 gdouble delta, new_value;
2686 /* from gtkrange.c calculate delta*/
2687 delta = pow (adj->page_size, 2.0 / 3.0);
2689 if (event->direction == GDK_SCROLL_UP ||
2690 event->direction == GDK_SCROLL_LEFT)
2693 new_value = CLAMP (adj->value + delta, adj->lower, adj->upper - adj->page_size);
2695 gtk_adjustment_set_value (adj, new_value);
2702 hildon_pannable_area_child_mapped (GtkWidget *widget,
2706 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (user_data)->priv;
2708 if (priv->event_window != NULL && priv->enabled)
2709 gdk_window_raise (priv->event_window);
2713 hildon_pannable_area_add (GtkContainer *container, GtkWidget *child)
2715 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (container)->priv;
2717 g_return_if_fail (gtk_bin_get_child (GTK_BIN (container)) == NULL);
2719 gtk_widget_set_parent (child, GTK_WIDGET (container));
2720 GTK_BIN (container)->child = child;
2722 g_signal_connect_after (child, "map-event",
2723 G_CALLBACK (hildon_pannable_area_child_mapped),
2726 if (!gtk_widget_set_scroll_adjustments (child, priv->hadjust, priv->vadjust)) {
2727 g_warning ("%s: cannot add non scrollable widget, "
2728 "wrap it in a viewport", __FUNCTION__);
2733 hildon_pannable_area_remove (GtkContainer *container, GtkWidget *child)
2735 g_return_if_fail (HILDON_IS_PANNABLE_AREA (container));
2736 g_return_if_fail (child != NULL);
2737 g_return_if_fail (gtk_bin_get_child (GTK_BIN (container)) == child);
2739 gtk_widget_set_scroll_adjustments (child, NULL, NULL);
2741 g_signal_handlers_disconnect_by_func (child,
2742 hildon_pannable_area_child_mapped,
2745 /* chain parent class handler to remove child */
2746 GTK_CONTAINER_CLASS (hildon_pannable_area_parent_class)->remove (container, child);
2750 hildon_pannable_calculate_vel_factor (HildonPannableArea * self)
2752 HildonPannableAreaPrivate *priv = self->priv;
2757 n = ceil (priv->sps * priv->scroll_time);
2759 for (i = 0; i < n && fct_i >= RATIO_TOLERANCE; i++) {
2760 fct_i *= priv->decel;
2764 priv->vel_factor = fct;
2768 * hildon_pannable_area_new:
2770 * Create a new pannable area widget
2772 * Returns: the newly created #HildonPannableArea
2778 hildon_pannable_area_new (void)
2780 return g_object_new (HILDON_TYPE_PANNABLE_AREA, NULL);
2784 * hildon_pannable_area_new_full:
2785 * @mode: #HildonPannableAreaMode
2786 * @enabled: Value for the enabled property
2787 * @vel_min: Value for the velocity-min property
2788 * @vel_max: Value for the velocity-max property
2789 * @decel: Value for the deceleration property
2790 * @sps: Value for the sps property
2792 * Create a new #HildonPannableArea widget and set various properties
2794 * returns: the newly create #HildonPannableArea
2800 hildon_pannable_area_new_full (gint mode, gboolean enabled,
2801 gdouble vel_min, gdouble vel_max,
2802 gdouble decel, guint sps)
2804 return g_object_new (HILDON_TYPE_PANNABLE_AREA,
2807 "velocity_min", vel_min,
2808 "velocity_max", vel_max,
2809 "deceleration", decel, "sps", sps, NULL);
2813 * hildon_pannable_area_add_with_viewport:
2814 * @area: A #HildonPannableArea
2815 * @child: Child widget to add to the viewport
2817 * Convenience function used to add a child to a #GtkViewport, and add the
2818 * viewport to the scrolled window.
2824 hildon_pannable_area_add_with_viewport (HildonPannableArea * area,
2828 GtkWidget *viewport;
2830 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
2831 g_return_if_fail (GTK_IS_WIDGET (child));
2832 g_return_if_fail (child->parent == NULL);
2834 bin = GTK_BIN (area);
2836 if (bin->child != NULL)
2838 g_return_if_fail (GTK_IS_VIEWPORT (bin->child));
2839 g_return_if_fail (GTK_BIN (bin->child)->child == NULL);
2841 viewport = bin->child;
2845 HildonPannableAreaPrivate *priv = area->priv;
2847 viewport = gtk_viewport_new (priv->hadjust,
2849 gtk_viewport_set_shadow_type (GTK_VIEWPORT (viewport), GTK_SHADOW_NONE);
2850 gtk_container_add (GTK_CONTAINER (area), viewport);
2853 gtk_widget_show (viewport);
2854 gtk_container_add (GTK_CONTAINER (viewport), child);
2858 * hildon_pannable_area_scroll_to:
2859 * @area: A #HildonPannableArea.
2860 * @x: The x coordinate of the destination point or -1 to ignore this axis.
2861 * @y: The y coordinate of the destination point or -1 to ignore this axis.
2863 * Smoothly scrolls @area to ensure that (@x, @y) is a visible point
2864 * on the widget. To move in only one coordinate, you must set the other one
2865 * to -1. Notice that, in %HILDON_PANNABLE_AREA_MODE_PUSH mode, this function
2866 * works just like hildon_pannable_area_jump_to().
2868 * This function is useful if you need to present the user with a particular
2869 * element inside a scrollable widget, like #GtkTreeView. For instance,
2870 * the following example shows how to scroll inside a #GtkTreeView to
2871 * make visible an item, indicated by the #GtkTreeIter @iter.
2875 * GtkTreePath *path;
2876 * GdkRectangle *rect;
2878 * path = gtk_tree_model_get_path (model, &iter);
2879 * gtk_tree_view_get_background_area (GTK_TREE_VIEW (treeview),
2880 * path, NULL, &rect);
2881 * gtk_tree_view_convert_bin_window_to_tree_coords (GTK_TREE_VIEW (treeview),
2882 * 0, rect.y, NULL, &y);
2883 * hildon_pannable_area_scroll_to (panarea, -1, y);
2884 * gtk_tree_path_free (path);
2888 * If you want to present a child widget in simpler scenarios,
2889 * use hildon_pannable_area_scroll_to_child() instead.
2891 * There is a precondition to this function: the widget must be
2892 * already realized. Check the hildon_pannable_area_jump_to_child() for
2893 * more tips regarding how to call this function during
2899 hildon_pannable_area_scroll_to (HildonPannableArea *area,
2900 const gint x, const gint y)
2902 HildonPannableAreaPrivate *priv;
2904 gint dist_x, dist_y;
2905 gboolean hscroll_visible, vscroll_visible;
2907 g_return_if_fail (GTK_WIDGET_REALIZED (area));
2908 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
2912 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
2913 priv->vadjust->page_size);
2914 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
2915 priv->hadjust->page_size);
2917 if (((!vscroll_visible)&&(!hscroll_visible)) ||
2918 (x == -1 && y == -1)) {
2922 if (priv->mode == HILDON_PANNABLE_AREA_MODE_PUSH)
2923 hildon_pannable_area_jump_to (area, x, y);
2925 width = priv->hadjust->upper - priv->hadjust->lower;
2926 height = priv->vadjust->upper - priv->vadjust->lower;
2928 g_return_if_fail (x < width || y < height);
2930 if ((x > -1)&&(hscroll_visible)) {
2931 priv->scroll_to_x = x - priv->hadjust->page_size/2;
2932 dist_x = priv->scroll_to_x - priv->hadjust->value;
2934 priv->scroll_to_x = -1;
2936 priv->vel_x = - dist_x/priv->vel_factor;
2939 priv->scroll_to_x = -1;
2942 if ((y > -1)&&(vscroll_visible)) {
2943 priv->scroll_to_y = y - priv->vadjust->page_size/2;
2944 dist_y = priv->scroll_to_y - priv->vadjust->value;
2946 priv->scroll_to_y = -1;
2948 priv->vel_y = - dist_y/priv->vel_factor;
2951 priv->scroll_to_y = y;
2954 if ((priv->scroll_to_y == -1) && (priv->scroll_to_y == -1)) {
2958 hildon_pannable_area_launch_fade_timeout (area, 1.0);
2961 priv->idle_id = gdk_threads_add_timeout ((gint) (1000.0 / (gdouble) priv->sps),
2963 hildon_pannable_area_timeout, area);
2967 * hildon_pannable_area_jump_to:
2968 * @area: A #HildonPannableArea.
2969 * @x: The x coordinate of the destination point or -1 to ignore this axis.
2970 * @y: The y coordinate of the destination point or -1 to ignore this axis.
2972 * Jumps the position of @area to ensure that (@x, @y) is a visible
2973 * point in the widget. In order to move in only one coordinate, you
2974 * must set the other one to -1. See hildon_pannable_area_scroll_to()
2975 * function for an example of how to calculate the position of
2976 * children in scrollable widgets like #GtkTreeview.
2978 * There is a precondition to this function: the widget must be
2979 * already realized. Check the hildon_pannable_area_jump_to_child() for
2980 * more tips regarding how to call this function during
2986 hildon_pannable_area_jump_to (HildonPannableArea *area,
2987 const gint x, const gint y)
2989 HildonPannableAreaPrivate *priv;
2992 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
2993 g_return_if_fail (GTK_WIDGET_REALIZED (area));
2994 g_return_if_fail (x >= -1 && y >= -1);
2996 if (x == -1 && y == -1) {
3002 width = priv->hadjust->upper - priv->hadjust->lower;
3003 height = priv->vadjust->upper - priv->vadjust->lower;
3005 g_return_if_fail (x < width || y < height);
3008 gdouble jump_to = x - priv->hadjust->page_size/2;
3010 if (jump_to > priv->hadjust->upper - priv->hadjust->page_size) {
3011 jump_to = priv->hadjust->upper - priv->hadjust->page_size;
3014 gtk_adjustment_set_value (priv->hadjust, jump_to);
3018 gdouble jump_to = y - priv->vadjust->page_size/2;
3020 if (jump_to > priv->vadjust->upper - priv->vadjust->page_size) {
3021 jump_to = priv->vadjust->upper - priv->vadjust->page_size;
3024 gtk_adjustment_set_value (priv->vadjust, jump_to);
3027 priv->scroll_indicator_alpha = 1.0;
3029 if (priv->scroll_indicator_timeout) {
3030 g_source_remove (priv->scroll_indicator_timeout);
3031 priv->scroll_indicator_timeout = 0;
3034 if (priv->idle_id) {
3037 priv->overshooting_x = 0;
3038 priv->overshooting_y = 0;
3040 if ((priv->overshot_dist_x>0)||(priv->overshot_dist_y>0)) {
3041 priv->overshot_dist_x = 0;
3042 priv->overshot_dist_y = 0;
3044 gtk_widget_queue_resize (GTK_WIDGET (area));
3047 g_source_remove (priv->idle_id);
3053 * hildon_pannable_area_scroll_to_child:
3054 * @area: A #HildonPannableArea.
3055 * @child: A #GtkWidget, descendant of @area.
3057 * Smoothly scrolls until @child is visible inside @area. @child must
3058 * be a descendant of @area. If you need to scroll inside a scrollable
3059 * widget, e.g., #GtkTreeview, see hildon_pannable_area_scroll_to().
3061 * There is a precondition to this function: the widget must be
3062 * already realized. Check the hildon_pannable_area_jump_to_child() for
3063 * more tips regarding how to call this function during
3069 hildon_pannable_area_scroll_to_child (HildonPannableArea *area, GtkWidget *child)
3071 GtkWidget *bin_child;
3074 g_return_if_fail (GTK_WIDGET_REALIZED (area));
3075 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3076 g_return_if_fail (GTK_IS_WIDGET (child));
3077 g_return_if_fail (gtk_widget_is_ancestor (child, GTK_WIDGET (area)));
3079 if (GTK_BIN (area)->child == NULL)
3082 /* We need to get to check the child of the inside the area */
3083 bin_child = GTK_BIN (area)->child;
3085 /* we check if we added a viewport */
3086 if (GTK_IS_VIEWPORT (bin_child)) {
3087 bin_child = GTK_BIN (bin_child)->child;
3090 if (gtk_widget_translate_coordinates (child, bin_child, 0, 0, &x, &y))
3091 hildon_pannable_area_scroll_to (area, x, y);
3095 * hildon_pannable_area_jump_to_child:
3096 * @area: A #HildonPannableArea.
3097 * @child: A #GtkWidget, descendant of @area.
3099 * Jumps to make sure @child is visible inside @area. @child must
3100 * be a descendant of @area. If you want to move inside a scrollable
3101 * widget, like, #GtkTreeview, see hildon_pannable_area_scroll_to().
3103 * There is a precondition to this function: the widget must be
3104 * already realized. You can control if the widget is ready with the
3105 * GTK_WIDGET_REALIZED macro. If you want to call this function during
3106 * the initialization process of the widget do it inside a callback to
3107 * the ::realize signal, using g_signal_connect_after() function.
3112 hildon_pannable_area_jump_to_child (HildonPannableArea *area, GtkWidget *child)
3114 GtkWidget *bin_child;
3117 g_return_if_fail (GTK_WIDGET_REALIZED (area));
3118 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3119 g_return_if_fail (GTK_IS_WIDGET (child));
3120 g_return_if_fail (gtk_widget_is_ancestor (child, GTK_WIDGET (area)));
3122 if (gtk_bin_get_child (GTK_BIN (area)) == NULL)
3125 /* We need to get to check the child of the inside the area */
3126 bin_child = gtk_bin_get_child (GTK_BIN (area));
3128 /* we check if we added a viewport */
3129 if (GTK_IS_VIEWPORT (bin_child)) {
3130 bin_child = gtk_bin_get_child (GTK_BIN (bin_child));
3133 if (gtk_widget_translate_coordinates (child, bin_child, 0, 0, &x, &y))
3134 hildon_pannable_area_jump_to (area, x, y);
3138 * hildon_pannable_get_child_widget_at:
3139 * @area: A #HildonPannableArea.
3140 * @x: horizontal coordinate of the point
3141 * @y: vertical coordinate of the point
3143 * Get the widget at the point (x, y) inside the pannable area. In
3144 * case no widget found it returns NULL.
3146 * returns: the #GtkWidget if we find a widget, NULL in any other case
3151 hildon_pannable_get_child_widget_at (HildonPannableArea *area,
3152 gdouble x, gdouble y)
3154 GdkWindow *window = NULL;
3155 GtkWidget *child_widget = NULL;
3157 window = hildon_pannable_area_get_topmost
3158 (gtk_bin_get_child (GTK_BIN (area))->window,
3159 x, y, NULL, NULL, GDK_ALL_EVENTS_MASK);
3161 gdk_window_get_user_data (window, (gpointer) &child_widget);
3163 return child_widget;
3168 * hildon_pannable_area_get_hadjustment:
3169 * @area: A #HildonPannableArea.
3171 * Returns the horizontal adjustment. This adjustment is the internal
3172 * widget adjustment used to control the animations. Do not modify it
3173 * directly to change the position of the pannable, to do that use the
3174 * pannable API. If you modify the object directly it could cause
3175 * artifacts in the animations.
3177 * returns: The horizontal #GtkAdjustment
3182 hildon_pannable_area_get_hadjustment (HildonPannableArea *area)
3185 g_return_val_if_fail (HILDON_IS_PANNABLE_AREA (area), NULL);
3187 return area->priv->hadjust;
3191 * hildon_pannable_area_get_vadjustment:
3192 * @area: A #HildonPannableArea.
3194 * Returns the vertical adjustment. This adjustment is the internal
3195 * widget adjustment used to control the animations. Do not modify it
3196 * directly to change the position of the pannable, to do that use the
3197 * pannable API. If you modify the object directly it could cause
3198 * artifacts in the animations.
3200 * returns: The vertical #GtkAdjustment
3205 hildon_pannable_area_get_vadjustment (HildonPannableArea *area)
3207 g_return_val_if_fail (HILDON_IS_PANNABLE_AREA (area), NULL);
3209 return area->priv->vadjust;
3214 * hildon_pannable_area_get_size_request_policy:
3215 * @area: A #HildonPannableArea.
3217 * This function returns the current size request policy of the
3218 * widget. That policy controls the way the size_request is done in
3219 * the pannable area. Check
3220 * hildon_pannable_area_set_size_request_policy() for a more detailed
3223 * returns: the policy is currently being used in the widget
3224 * #HildonSizeRequestPolicy.
3228 HildonSizeRequestPolicy
3229 hildon_pannable_area_get_size_request_policy (HildonPannableArea *area)
3231 HildonPannableAreaPrivate *priv;
3233 g_return_val_if_fail (HILDON_IS_PANNABLE_AREA (area), FALSE);
3237 return priv->size_request_policy;
3241 * hildon_pannable_area_set_size_request_policy:
3242 * @area: A #HildonPannableArea.
3243 * @size_request_policy: One of the allowed #HildonSizeRequestPolicy
3245 * This function sets the pannable area size request policy. That
3246 * policy controls the way the size_request is done in the pannable
3247 * area. Pannable can use the size request of its children
3248 * (#HILDON_SIZE_REQUEST_CHILDREN) or the minimum size required for
3249 * the area itself (#HILDON_SIZE_REQUEST_MINIMUM), the latter is the
3250 * default. Recall this size depends on the scrolling policy you are
3251 * requesting to the pannable area, if you set #GTK_POLICY_NEVER this
3252 * parameter will not have any effect with
3253 * #HILDON_SIZE_REQUEST_MINIMUM set.
3257 * Deprecated: This method and the policy request is deprecated, DO
3258 * NOT use it in future code, the only policy properly supported in
3259 * gtk+ nowadays is the minimum size. Use #gtk_window_set_default_size
3260 * or #gtk_window_set_geometry_hints with the proper size in your case
3261 * to define the height of your dialogs.
3264 hildon_pannable_area_set_size_request_policy (HildonPannableArea *area,
3265 HildonSizeRequestPolicy size_request_policy)
3267 HildonPannableAreaPrivate *priv;
3269 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3273 if (priv->size_request_policy == size_request_policy)
3276 priv->size_request_policy = size_request_policy;
3278 gtk_widget_queue_resize (GTK_WIDGET (area));
3280 g_object_notify (G_OBJECT (area), "size-request-policy");