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 gtk_widget_add_events (GTK_WIDGET (area), GDK_POINTER_MOTION_HINT_MASK);
662 GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
664 GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
666 g_object_ref_sink (G_OBJECT (priv->hadjust));
667 g_object_ref_sink (G_OBJECT (priv->vadjust));
669 g_signal_connect_swapped (priv->hadjust, "value-changed",
670 G_CALLBACK (hildon_pannable_area_adjust_value_changed), area);
671 g_signal_connect_swapped (priv->vadjust, "value-changed",
672 G_CALLBACK (hildon_pannable_area_adjust_value_changed), area);
673 g_signal_connect_swapped (priv->hadjust, "changed",
674 G_CALLBACK (hildon_pannable_area_adjust_changed), area);
675 g_signal_connect_swapped (priv->vadjust, "changed",
676 G_CALLBACK (hildon_pannable_area_adjust_changed), area);
677 g_signal_connect (area, "grab-notify",
678 G_CALLBACK (hildon_pannable_area_grab_notify), NULL);
682 hildon_pannable_area_get_property (GObject * object,
687 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (object)->priv;
689 switch (property_id) {
691 g_value_set_boolean (value, priv->enabled);
694 g_value_set_enum (value, priv->mode);
696 case PROP_MOVEMENT_MODE:
697 g_value_set_flags (value, priv->mov_mode);
699 case PROP_VELOCITY_MIN:
700 g_value_set_double (value, priv->vmin);
702 case PROP_VELOCITY_MAX:
703 g_value_set_double (value, priv->vmax);
705 case PROP_VEL_MAX_OVERSHOOTING:
706 g_value_set_double (value, priv->vmax_overshooting);
708 case PROP_VELOCITY_FAST_FACTOR:
709 g_value_set_double (value, priv->vfast_factor);
711 case PROP_DECELERATION:
712 g_value_set_double (value, priv->decel);
714 case PROP_DRAG_INERTIA:
715 g_value_set_double (value, priv->drag_inertia);
718 g_value_set_uint (value, priv->sps);
720 case PROP_PANNING_THRESHOLD:
721 g_value_set_uint (value, priv->panning_threshold);
723 case PROP_SCROLLBAR_FADE_DELAY:
724 /* convert to miliseconds */
725 g_value_set_uint (value, priv->scrollbar_fade_delay * SCROLL_FADE_TIMEOUT);
727 case PROP_BOUNCE_STEPS:
728 g_value_set_uint (value, priv->bounce_steps);
731 g_value_set_uint (value, priv->force);
733 case PROP_DIRECTION_ERROR_MARGIN:
734 g_value_set_uint (value, priv->direction_error_margin);
736 case PROP_VSCROLLBAR_POLICY:
737 g_value_set_enum (value, priv->vscrollbar_policy);
739 case PROP_HSCROLLBAR_POLICY:
740 g_value_set_enum (value, priv->hscrollbar_policy);
742 case PROP_VOVERSHOOT_MAX:
743 g_value_set_int (value, priv->vovershoot_max);
745 case PROP_HOVERSHOOT_MAX:
746 g_value_set_int (value, priv->hovershoot_max);
748 case PROP_SCROLL_TIME:
749 g_value_set_double (value, priv->scroll_time);
751 case PROP_INITIAL_HINT:
752 g_value_set_boolean (value, priv->initial_hint);
754 case PROP_LOW_FRICTION_MODE:
755 g_value_set_boolean (value, priv->low_friction_mode);
757 case PROP_SIZE_REQUEST_POLICY:
758 g_value_set_enum (value, priv->size_request_policy);
760 case PROP_HADJUSTMENT:
761 g_value_set_object (value,
762 hildon_pannable_area_get_hadjustment
763 (HILDON_PANNABLE_AREA (object)));
765 case PROP_VADJUSTMENT:
766 g_value_set_object (value,
767 hildon_pannable_area_get_vadjustment
768 (HILDON_PANNABLE_AREA (object)));
771 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
776 hildon_pannable_area_set_property (GObject * object,
778 const GValue * value,
781 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (object)->priv;
784 switch (property_id) {
786 enabled = g_value_get_boolean (value);
788 if ((priv->enabled != enabled) && (GTK_WIDGET_REALIZED (object))) {
790 gdk_window_raise (priv->event_window);
792 gdk_window_lower (priv->event_window);
795 priv->enabled = enabled;
798 priv->mode = g_value_get_enum (value);
800 case PROP_MOVEMENT_MODE:
801 priv->mov_mode = g_value_get_flags (value);
803 case PROP_VELOCITY_MIN:
804 priv->vmin = g_value_get_double (value);
806 case PROP_VELOCITY_MAX:
807 priv->vmax = g_value_get_double (value);
809 case PROP_VEL_MAX_OVERSHOOTING:
810 priv->vmax_overshooting = g_value_get_double (value);
812 case PROP_VELOCITY_FAST_FACTOR:
813 priv->vfast_factor = g_value_get_double (value);
815 case PROP_DECELERATION:
816 priv->decel = g_value_get_double (value);
817 hildon_pannable_calculate_vel_factor (HILDON_PANNABLE_AREA (object));
819 case PROP_DRAG_INERTIA:
820 priv->drag_inertia = g_value_get_double (value);
823 priv->sps = g_value_get_uint (value);
825 case PROP_PANNING_THRESHOLD:
826 priv->panning_threshold = g_value_get_uint (value);
828 case PROP_SCROLLBAR_FADE_DELAY:
829 /* convert to miliseconds */
830 priv->scrollbar_fade_delay = g_value_get_uint (value)/(SCROLL_FADE_TIMEOUT);
832 case PROP_BOUNCE_STEPS:
833 priv->bounce_steps = g_value_get_uint (value);
836 priv->force = g_value_get_uint (value);
838 case PROP_DIRECTION_ERROR_MARGIN:
839 priv->direction_error_margin = g_value_get_uint (value);
841 case PROP_VSCROLLBAR_POLICY:
842 priv->vscrollbar_policy = g_value_get_enum (value);
844 gtk_widget_queue_resize (GTK_WIDGET (object));
846 case PROP_HSCROLLBAR_POLICY:
847 priv->hscrollbar_policy = g_value_get_enum (value);
849 gtk_widget_queue_resize (GTK_WIDGET (object));
851 case PROP_VOVERSHOOT_MAX:
852 priv->vovershoot_max = g_value_get_int (value);
854 case PROP_HOVERSHOOT_MAX:
855 priv->hovershoot_max = g_value_get_int (value);
857 case PROP_SCROLL_TIME:
858 priv->scroll_time = g_value_get_double (value);
860 hildon_pannable_calculate_vel_factor (HILDON_PANNABLE_AREA (object));
862 case PROP_INITIAL_HINT:
863 priv->initial_hint = g_value_get_boolean (value);
865 case PROP_LOW_FRICTION_MODE:
866 priv->low_friction_mode = g_value_get_boolean (value);
868 case PROP_SIZE_REQUEST_POLICY:
869 hildon_pannable_area_set_size_request_policy (HILDON_PANNABLE_AREA (object),
870 g_value_get_enum (value));
874 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
879 hildon_pannable_area_dispose (GObject * object)
881 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (object)->priv;
882 GtkWidget *child = gtk_bin_get_child (GTK_BIN (object));
885 g_source_remove (priv->idle_id);
889 if (priv->scroll_indicator_timeout){
890 g_source_remove (priv->scroll_indicator_timeout);
891 priv->scroll_indicator_timeout = 0;
894 if (priv->motion_event_scroll_timeout){
895 g_source_remove (priv->motion_event_scroll_timeout);
896 priv->motion_event_scroll_timeout = 0;
900 g_signal_handlers_disconnect_by_func (child,
901 hildon_pannable_area_child_mapped,
905 g_signal_handlers_disconnect_by_func (object,
906 hildon_pannable_area_grab_notify,
910 g_signal_handlers_disconnect_by_func (priv->hadjust,
911 hildon_pannable_area_adjust_value_changed,
913 g_signal_handlers_disconnect_by_func (priv->hadjust,
914 hildon_pannable_area_adjust_changed,
916 g_object_unref (priv->hadjust);
917 priv->hadjust = NULL;
921 g_signal_handlers_disconnect_by_func (priv->vadjust,
922 hildon_pannable_area_adjust_value_changed,
924 g_signal_handlers_disconnect_by_func (priv->vadjust,
925 hildon_pannable_area_adjust_changed,
927 g_object_unref (priv->vadjust);
928 priv->vadjust = NULL;
931 if (G_OBJECT_CLASS (hildon_pannable_area_parent_class)->dispose)
932 G_OBJECT_CLASS (hildon_pannable_area_parent_class)->dispose (object);
936 hildon_pannable_area_realize (GtkWidget * widget)
938 GdkWindowAttr attributes;
939 gint attributes_mask;
941 HildonPannableAreaPrivate *priv;
943 priv = HILDON_PANNABLE_AREA (widget)->priv;
945 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
947 border_width = GTK_CONTAINER (widget)->border_width;
949 attributes.x = widget->allocation.x + border_width;
950 attributes.y = widget->allocation.y + border_width;
951 attributes.width = MAX (widget->allocation.width - 2 * border_width, 0);
952 attributes.height = MAX (widget->allocation.height - 2 * border_width, 0);
953 attributes.window_type = GDK_WINDOW_CHILD;
955 /* avoid using the hildon_window */
956 attributes.visual = gtk_widget_get_visual (widget);
957 attributes.colormap = gtk_widget_get_colormap (widget);
958 attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK;
959 attributes.wclass = GDK_INPUT_OUTPUT;
961 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
963 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
964 &attributes, attributes_mask);
965 gdk_window_set_user_data (widget->window, widget);
967 /* create the events window */
970 attributes.event_mask = gtk_widget_get_events (widget)
971 | GDK_BUTTON_MOTION_MASK
972 | GDK_BUTTON_PRESS_MASK
973 | GDK_BUTTON_RELEASE_MASK
975 | GDK_EXPOSURE_MASK | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK;
976 attributes.wclass = GDK_INPUT_ONLY;
978 attributes_mask = GDK_WA_X | GDK_WA_Y;
980 priv->event_window = gdk_window_new (widget->window,
981 &attributes, attributes_mask);
982 gdk_window_set_user_data (priv->event_window, widget);
984 widget->style = gtk_style_attach (widget->style, widget->window);
985 gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
987 priv->scrollbars_gc = gdk_gc_new (GDK_DRAWABLE (widget->window));
988 gdk_gc_copy (priv->scrollbars_gc, widget->style->fg_gc[GTK_STATE_INSENSITIVE]);
992 hildon_pannable_area_unrealize (GtkWidget * widget)
994 HildonPannableAreaPrivate *priv;
996 priv = HILDON_PANNABLE_AREA (widget)->priv;
998 if (priv->event_window != NULL) {
999 gdk_window_set_user_data (priv->event_window, NULL);
1000 gdk_window_destroy (priv->event_window);
1001 priv->event_window = NULL;
1004 gdk_gc_unref (priv->scrollbars_gc);
1006 if (GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->unrealize)
1007 (*GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->unrealize)(widget);
1011 hildon_pannable_area_size_request (GtkWidget * widget,
1012 GtkRequisition * requisition)
1014 GtkRequisition child_requisition = {0};
1015 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1016 GtkWidget *child = gtk_bin_get_child (GTK_BIN (widget));
1018 if (child && GTK_WIDGET_VISIBLE (child))
1020 gtk_widget_size_request (child, &child_requisition);
1023 if (priv->hscrollbar_policy == GTK_POLICY_NEVER) {
1024 requisition->width = child_requisition.width;
1026 switch (priv->size_request_policy) {
1027 case HILDON_SIZE_REQUEST_CHILDREN:
1028 requisition->width = MIN (PANNABLE_MAX_WIDTH,
1029 child_requisition.width);
1031 case HILDON_SIZE_REQUEST_MINIMUM:
1033 requisition->width = priv->indicator_width;
1037 if (priv->vscrollbar_policy == GTK_POLICY_NEVER) {
1038 requisition->height = child_requisition.height;
1040 switch (priv->size_request_policy) {
1041 case HILDON_SIZE_REQUEST_CHILDREN:
1042 requisition->height = MIN (PANNABLE_MAX_HEIGHT,
1043 child_requisition.height);
1045 case HILDON_SIZE_REQUEST_MINIMUM:
1047 requisition->height = priv->indicator_width;
1051 requisition->width += 2 * GTK_CONTAINER (widget)->border_width;
1052 requisition->height += 2 * GTK_CONTAINER (widget)->border_width;
1056 hildon_pannable_area_child_allocate_calculate (GtkWidget * widget,
1057 GtkAllocation * allocation,
1058 GtkAllocation * child_allocation)
1061 HildonPannableAreaPrivate *priv;
1063 border_width = GTK_CONTAINER (widget)->border_width;
1065 priv = HILDON_PANNABLE_AREA (widget)->priv;
1067 child_allocation->x = 0;
1068 child_allocation->y = 0;
1069 child_allocation->width = MAX (allocation->width - 2 * border_width -
1070 (priv->vscroll_visible ? priv->vscroll_rect.width : 0), 0);
1071 child_allocation->height = MAX (allocation->height - 2 * border_width -
1072 (priv->hscroll_visible ? priv->hscroll_rect.height : 0), 0);
1074 if (priv->overshot_dist_y > 0) {
1075 child_allocation->y = MIN (child_allocation->y + priv->overshot_dist_y,
1076 child_allocation->height);
1077 child_allocation->height = MAX (child_allocation->height - priv->overshot_dist_y, 0);
1078 } else if (priv->overshot_dist_y < 0) {
1079 child_allocation->height = MAX (child_allocation->height + priv->overshot_dist_y, 0);
1082 if (priv->overshot_dist_x > 0) {
1083 child_allocation->x = MIN (child_allocation->x + priv->overshot_dist_x,
1084 child_allocation->width);
1085 child_allocation->width = MAX (child_allocation->width - priv->overshot_dist_x, 0);
1086 } else if (priv->overshot_dist_x < 0) {
1087 child_allocation->width = MAX (child_allocation->width + priv->overshot_dist_x, 0);
1092 hildon_pannable_area_size_allocate (GtkWidget * widget,
1093 GtkAllocation * allocation)
1095 GtkAllocation child_allocation;
1096 HildonPannableAreaPrivate *priv;
1097 GtkWidget *child = gtk_bin_get_child (GTK_BIN (widget));
1100 border_width = GTK_CONTAINER (widget)->border_width;
1102 widget->allocation = *allocation;
1104 priv = HILDON_PANNABLE_AREA (widget)->priv;
1106 if (GTK_WIDGET_REALIZED (widget)) {
1107 gdk_window_move_resize (widget->window,
1108 allocation->x + border_width,
1109 allocation->y + border_width,
1110 allocation->width - border_width * 2,
1111 allocation->height - border_width * 2);
1112 gdk_window_move_resize (priv->event_window,
1115 allocation->width - border_width * 2,
1116 allocation->height - border_width * 2);
1119 if (child && GTK_WIDGET_VISIBLE (child)) {
1121 hildon_pannable_area_child_allocate_calculate (widget,
1125 gtk_widget_size_allocate (child, &child_allocation);
1127 if (hildon_pannable_area_check_scrollbars (HILDON_PANNABLE_AREA (widget))) {
1128 hildon_pannable_area_child_allocate_calculate (widget,
1132 gtk_widget_size_allocate (child, &child_allocation);
1135 /* we have to do this after child size_allocate because page_size is
1136 * changed when we allocate the size of the children */
1137 if (priv->overshot_dist_y < 0) {
1138 gtk_adjustment_set_value (priv->vadjust, priv->vadjust->upper -
1139 priv->vadjust->page_size);
1142 if (priv->overshot_dist_x < 0) {
1143 gtk_adjustment_set_value (priv->hadjust, priv->hadjust->upper -
1144 priv->hadjust->page_size);
1148 hildon_pannable_area_check_scrollbars (HILDON_PANNABLE_AREA (widget));
1153 hildon_pannable_area_style_set (GtkWidget * widget,
1154 GtkStyle * previous_style)
1156 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1158 GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->
1159 style_set (widget, previous_style);
1161 gtk_widget_style_get (widget, "indicator-width", &priv->indicator_width, NULL);
1165 hildon_pannable_area_map (GtkWidget * widget)
1167 HildonPannableAreaPrivate *priv;
1169 priv = HILDON_PANNABLE_AREA (widget)->priv;
1171 gdk_window_show (widget->window);
1173 if (priv->event_window != NULL && !priv->enabled)
1174 gdk_window_show (priv->event_window);
1176 (*GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->map) (widget);
1178 if (priv->event_window != NULL && priv->enabled)
1179 gdk_window_show (priv->event_window);
1183 hildon_pannable_area_unmap (GtkWidget * widget)
1185 HildonPannableAreaPrivate *priv;
1187 priv = HILDON_PANNABLE_AREA (widget)->priv;
1189 if (priv->event_window != NULL)
1190 gdk_window_hide (priv->event_window);
1192 gdk_window_hide (widget->window);
1194 (*GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->unmap) (widget);
1198 hildon_pannable_area_grab_notify (GtkWidget *widget,
1199 gboolean was_grabbed,
1202 /* an internal widget has grabbed the focus and now has returned it,
1203 we have to do some release actions */
1205 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1207 priv->scroll_indicator_event_interrupt = 0;
1209 if ((!priv->scroll_indicator_timeout)&&(priv->scroll_indicator_alpha)>0.1) {
1210 priv->scroll_delay_counter = priv->scrollbar_fade_delay;
1212 hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget),
1213 priv->scroll_indicator_alpha);
1216 priv->last_type = 3;
1217 priv->moved = FALSE;
1221 #if USE_CAIRO_SCROLLBARS == 1
1224 rgb_from_gdkcolor (GdkColor *color, gdouble *r, gdouble *g, gdouble *b)
1226 *r = (color->red >> 8) / 255.0;
1227 *g = (color->green >> 8) / 255.0;
1228 *b = (color->blue >> 8) / 255.0;
1232 hildon_pannable_draw_vscroll (GtkWidget * widget,
1233 GdkColor *back_color,
1234 GdkColor *scroll_color)
1236 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1239 cairo_pattern_t *pattern;
1241 gint radius = (priv->vscroll_rect.width/2) - 1;
1243 cr = gdk_cairo_create(widget->window);
1245 /* Draw the background */
1246 rgb_from_gdkcolor (back_color, &r, &g, &b);
1247 cairo_set_source_rgb (cr, r, g, b);
1248 cairo_rectangle(cr, priv->vscroll_rect.x, priv->vscroll_rect.y,
1249 priv->vscroll_rect.width,
1250 priv->vscroll_rect.height);
1251 cairo_fill_preserve (cr);
1254 /* Calculate the scroll bar height and position */
1255 y = ((priv->vadjust->value - priv->vadjust->lower) / (priv->vadjust->upper - priv->vadjust->lower)) *
1256 (widget->allocation.height -
1257 (priv->hscroll_visible ? priv->indicator_width : 0));
1258 height = ((((priv->vadjust->value - priv->vadjust->lower) +
1259 priv->vadjust->page_size) /
1260 (priv->vadjust->upper - priv->vadjust->lower)) *
1261 (widget->allocation.height -
1262 (priv->hscroll_visible ? priv->indicator_width : 0))) - y;
1264 /* Set a minimum height */
1265 height = MAX (SCROLL_BAR_MIN_SIZE, height);
1267 /* Check the max y position */
1268 y = MIN (y, widget->allocation.height -
1269 (priv->hscroll_visible ? priv->hscroll_rect.height : 0) -
1272 /* Draw the scrollbar */
1273 rgb_from_gdkcolor (scroll_color, &r, &g, &b);
1275 pattern = cairo_pattern_create_linear(radius+1, y, radius+1,y + height);
1276 cairo_pattern_add_color_stop_rgb(pattern, 0, r, g, b);
1277 cairo_pattern_add_color_stop_rgb(pattern, 1, r/2, g/2, b/2);
1278 cairo_set_source(cr, pattern);
1280 cairo_pattern_destroy(pattern);
1282 cairo_arc(cr, priv->vscroll_rect.x + radius + 1, y + radius + 1, radius, G_PI, 0);
1283 cairo_line_to(cr, priv->vscroll_rect.x + (radius * 2) + 1, y + height - radius);
1284 cairo_arc(cr, priv->vscroll_rect.x + radius + 1, y + height - radius, radius, 0, G_PI);
1285 cairo_line_to(cr, priv->vscroll_rect.x + 1, y + height - radius);
1288 cairo_paint_with_alpha(cr, priv->scroll_indicator_alpha);
1294 hildon_pannable_draw_hscroll (GtkWidget * widget,
1295 GdkColor *back_color,
1296 GdkColor *scroll_color)
1298 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1301 cairo_pattern_t *pattern;
1303 gint radius = (priv->hscroll_rect.height/2) - 1;
1305 cr = gdk_cairo_create(widget->window);
1307 /* Draw the background */
1308 rgb_from_gdkcolor (back_color, &r, &g, &b);
1309 cairo_set_source_rgb (cr, r, g, b);
1310 cairo_rectangle(cr, priv->hscroll_rect.x, priv->hscroll_rect.y,
1311 priv->hscroll_rect.width,
1312 priv->hscroll_rect.height);
1313 cairo_fill_preserve (cr);
1316 /* calculate the scrollbar width and position */
1317 x = ((priv->hadjust->value - priv->hadjust->lower) / (priv->hadjust->upper - priv->hadjust->lower)) *
1318 (widget->allocation.width - (priv->vscroll_visible ? priv->indicator_width : 0));
1319 width =((((priv->hadjust->value - priv->hadjust->lower) +
1320 priv->hadjust->page_size) / (priv->hadjust->upper - priv->hadjust->lower)) *
1321 (widget->allocation.width -
1322 (priv->vscroll_visible ? priv->indicator_width : 0))) - x;
1324 /* Set a minimum width */
1325 width = MAX (SCROLL_BAR_MIN_SIZE, width);
1327 /* Check the max x position */
1328 x = MIN (x, widget->allocation.width -
1329 (priv->vscroll_visible ? priv->vscroll_rect.width : 0) -
1332 /* Draw the scrollbar */
1333 rgb_from_gdkcolor (scroll_color, &r, &g, &b);
1335 pattern = cairo_pattern_create_linear(x, radius+1, x+width, radius+1);
1336 cairo_pattern_add_color_stop_rgb(pattern, 0, r, g, b);
1337 cairo_pattern_add_color_stop_rgb(pattern, 1, r/2, g/2, b/2);
1338 cairo_set_source(cr, pattern);
1340 cairo_pattern_destroy(pattern);
1342 cairo_arc_negative(cr, x + radius + 1, priv->hscroll_rect.y + radius + 1, radius, 3*G_PI_2, G_PI_2);
1343 cairo_line_to(cr, x + width - radius, priv->hscroll_rect.y + (radius * 2) + 1);
1344 cairo_arc_negative(cr, x + width - radius, priv->hscroll_rect.y + radius + 1, radius, G_PI_2, 3*G_PI_2);
1345 cairo_line_to(cr, x + width - radius, priv->hscroll_rect.y + 1);
1348 cairo_paint_with_alpha(cr, priv->scroll_indicator_alpha);
1353 #else /* USE_CAIRO_SCROLLBARS */
1356 tranparency_color (GdkColor *color,
1359 gdouble transparency)
1363 diff = colora.red - colorb.red;
1364 color->red = colora.red-diff*transparency;
1366 diff = colora.green - colorb.green;
1367 color->green = colora.green-diff*transparency;
1369 diff = colora.blue - colorb.blue;
1370 color->blue = colora.blue-diff*transparency;
1374 hildon_pannable_draw_vscroll (GtkWidget *widget,
1375 GdkColor *back_color,
1376 GdkColor *scroll_color)
1378 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1380 GdkColor transp_color;
1381 GdkGC *gc = widget->style->fg_gc[GTK_STATE_INSENSITIVE];
1383 gdk_draw_rectangle (widget->window,
1384 widget->style->bg_gc[GTK_STATE_NORMAL],
1386 priv->vscroll_rect.x, priv->vscroll_rect.y,
1387 priv->vscroll_rect.width,
1388 priv->vscroll_rect.height);
1390 y = ((priv->vadjust->value - priv->vadjust->lower) / (priv->vadjust->upper - priv->vadjust->lower)) *
1391 (widget->allocation.height - (priv->hscroll_visible ? priv->indicator_width : 0));
1392 height = ((((priv->vadjust->value - priv->vadjust->lower) + priv->vadjust->page_size) /
1393 (priv->vadjust->upper - priv->vadjust->lower)) *
1394 (widget->allocation.height -
1395 (priv->hscroll_visible ? priv->indicator_width : 0))) - y;
1397 /* Set a minimum height */
1398 height = MAX (SCROLL_BAR_MIN_SIZE, height);
1400 /* Check the max y position */
1401 y = MIN (y, widget->allocation.height -
1402 (priv->hscroll_visible ? priv->hscroll_rect.height : 0) -
1405 if (priv->scroll_indicator_alpha < 1.0) {
1406 tranparency_color (&transp_color, *back_color, *scroll_color,
1407 priv->scroll_indicator_alpha);
1409 gdk_gc_set_rgb_fg_color (priv->scrollbars_gc, &transp_color);
1411 gc = priv->scrollbars_gc;
1414 gdk_draw_rectangle (widget->window, gc,
1415 TRUE, priv->vscroll_rect.x, y,
1416 priv->vscroll_rect.width, height);
1420 hildon_pannable_draw_hscroll (GtkWidget *widget,
1421 GdkColor *back_color,
1422 GdkColor *scroll_color)
1424 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1426 GdkColor transp_color;
1427 GdkGC *gc = widget->style->fg_gc[GTK_STATE_INSENSITIVE];
1429 gdk_draw_rectangle (widget->window,
1430 widget->style->bg_gc[GTK_STATE_INSENSITIVE],
1432 priv->hscroll_rect.x, priv->hscroll_rect.y,
1433 priv->hscroll_rect.width,
1434 priv->hscroll_rect.height);
1436 /* calculate the scrollbar width and position */
1437 x = ((priv->hadjust->value - priv->hadjust->lower) / (priv->hadjust->upper - priv->hadjust->lower)) *
1438 (widget->allocation.width - (priv->vscroll_visible ? priv->indicator_width : 0));
1439 width =((((priv->hadjust->value - priv->hadjust->lower) +
1440 priv->hadjust->page_size) / (priv->hadjust->upper - priv->hadjust->lower)) *
1441 (widget->allocation.width -
1442 (priv->vscroll_visible ? priv->indicator_width : 0))) - x;
1444 /* Set a minimum width */
1445 width = MAX (SCROLL_BAR_MIN_SIZE, width);
1447 /* Check the max x position */
1448 x = MIN (x, widget->allocation.width -
1449 (priv->vscroll_visible ? priv->vscroll_rect.width : 0) -
1452 if (priv->scroll_indicator_alpha < 1.0) {
1453 tranparency_color (&transp_color, *back_color, *scroll_color,
1454 priv->scroll_indicator_alpha);
1456 gdk_gc_set_rgb_fg_color (priv->scrollbars_gc, &transp_color);
1458 gc = priv->scrollbars_gc;
1461 gdk_draw_rectangle (widget->window, gc,
1462 TRUE, x, priv->hscroll_rect.y, width,
1463 priv->hscroll_rect.height);
1466 #endif /* USE_CAIRO_SCROLLBARS */
1469 hildon_pannable_area_initial_effect (GtkWidget * widget)
1471 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1472 gboolean hscroll_visible, vscroll_visible;
1474 if (priv->initial_hint) {
1476 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
1477 priv->vadjust->page_size);
1478 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
1479 priv->hadjust->page_size);
1481 if (priv->vscroll_visible || priv->hscroll_visible) {
1483 priv->scroll_indicator_event_interrupt = 0;
1484 priv->scroll_delay_counter = priv->scrollbar_fade_delay;
1486 hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget), 1.0);
1492 hildon_pannable_area_launch_fade_timeout (HildonPannableArea * area,
1495 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (area)->priv;
1497 priv->scroll_indicator_alpha = alpha;
1499 if (!priv->scroll_indicator_timeout)
1500 priv->scroll_indicator_timeout =
1501 gdk_threads_add_timeout (SCROLL_FADE_TIMEOUT,
1502 (GSourceFunc) hildon_pannable_area_scroll_indicator_fade,
1507 hildon_pannable_area_adjust_changed (HildonPannableArea * area,
1510 if (GTK_WIDGET_REALIZED (area))
1511 hildon_pannable_area_refresh (area);
1515 hildon_pannable_area_adjust_value_changed (HildonPannableArea * area,
1518 if (GTK_WIDGET_REALIZED (area)) {
1519 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (area)->priv;
1521 hildon_pannable_area_redraw (area);
1523 if ((priv->vscroll_visible) || (priv->hscroll_visible)) {
1524 priv->scroll_indicator_event_interrupt = 0;
1525 priv->scroll_delay_counter = priv->scrollbar_fade_delay;
1527 hildon_pannable_area_launch_fade_timeout (area, 1.0);
1533 hildon_pannable_area_redraw (HildonPannableArea * area)
1535 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (area)->priv;
1537 /* Redraw scroll indicators */
1538 if (GTK_WIDGET_DRAWABLE (area)) {
1539 if (priv->hscroll_visible) {
1540 gdk_window_invalidate_rect (GTK_WIDGET (area)->window,
1541 &priv->hscroll_rect, FALSE);
1544 if (priv->vscroll_visible) {
1545 gdk_window_invalidate_rect (GTK_WIDGET (area)->window,
1546 &priv->vscroll_rect, FALSE);
1552 hildon_pannable_area_scroll_indicator_fade(HildonPannableArea * area)
1554 HildonPannableAreaPrivate *priv = area->priv;
1556 /* if moving do not fade out */
1557 if (((ABS (priv->vel_y)>1.0)||
1558 (ABS (priv->vel_x)>1.0))&&(!priv->clicked)) {
1563 if (priv->scroll_indicator_event_interrupt) {
1564 /* Stop a fade out, and fade back in */
1565 if (priv->scroll_indicator_alpha > 0.9) {
1566 priv->scroll_indicator_alpha = 1.0;
1567 priv->scroll_indicator_timeout = 0;
1571 priv->scroll_indicator_alpha += 0.2;
1572 hildon_pannable_area_redraw (area);
1578 if ((priv->scroll_indicator_alpha > 0.9) &&
1579 (priv->scroll_delay_counter > 0)) {
1580 priv->scroll_delay_counter--;
1585 if (!priv->scroll_indicator_event_interrupt) {
1586 /* Continue fade out */
1587 if (priv->scroll_indicator_alpha < 0.1) {
1588 priv->scroll_indicator_timeout = 0;
1589 priv->scroll_indicator_alpha = 0.0;
1593 priv->scroll_indicator_alpha -= 0.2;
1594 hildon_pannable_area_redraw (area);
1604 hildon_pannable_area_expose_event (GtkWidget * widget,
1605 GdkEventExpose * event)
1608 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1609 #if USE_CAIRO_SCROLLBARS == 1
1610 GdkColor back_color = widget->style->bg[GTK_STATE_NORMAL];
1611 GdkColor scroll_color = widget->style->base[GTK_STATE_SELECTED];
1612 #else /* USE_CAIRO_SCROLLBARS */
1613 GdkColor back_color = widget->style->bg[GTK_STATE_NORMAL];
1614 GdkColor scroll_color = widget->style->fg[GTK_STATE_INSENSITIVE];
1617 if (gtk_bin_get_child (GTK_BIN (widget))) {
1619 if (priv->scroll_indicator_alpha > 0.1) {
1620 if (priv->vscroll_visible) {
1621 hildon_pannable_draw_vscroll (widget, &back_color, &scroll_color);
1623 if (priv->hscroll_visible) {
1624 hildon_pannable_draw_hscroll (widget, &back_color, &scroll_color);
1628 /* draw overshooting rectangles */
1629 if (priv->overshot_dist_y > 0) {
1630 gint overshot_height;
1632 overshot_height = MIN (priv->overshot_dist_y, widget->allocation.height -
1633 (priv->hscroll_visible ? priv->hscroll_rect.height : 0));
1635 gdk_draw_rectangle (widget->window,
1636 widget->style->bg_gc[GTK_STATE_NORMAL],
1640 widget->allocation.width -
1641 (priv->vscroll_visible ? priv->vscroll_rect.width : 0),
1643 } else if (priv->overshot_dist_y < 0) {
1644 gint overshot_height;
1648 MAX (priv->overshot_dist_y,
1649 -(widget->allocation.height -
1650 (priv->hscroll_visible ? priv->hscroll_rect.height : 0)));
1652 overshot_y = MAX (widget->allocation.height +
1654 (priv->hscroll_visible ? priv->hscroll_rect.height : 0), 0);
1656 gdk_draw_rectangle (widget->window,
1657 widget->style->bg_gc[GTK_STATE_NORMAL],
1661 widget->allocation.width -
1662 priv->vscroll_rect.width,
1666 if (priv->overshot_dist_x > 0) {
1667 gint overshot_width;
1669 overshot_width = MIN (priv->overshot_dist_x, widget->allocation.width -
1670 (priv->vscroll_visible ? priv->vscroll_rect.width : 0));
1672 gdk_draw_rectangle (widget->window,
1673 widget->style->bg_gc[GTK_STATE_NORMAL],
1678 widget->allocation.height -
1679 (priv->hscroll_visible ? priv->hscroll_rect.height : 0));
1680 } else if (priv->overshot_dist_x < 0) {
1681 gint overshot_width;
1685 MAX (priv->overshot_dist_x,
1686 -(widget->allocation.width -
1687 (priv->vscroll_visible ? priv->vscroll_rect.width : 0)));
1689 overshot_x = MAX (widget->allocation.width +
1691 (priv->vscroll_visible ? priv->vscroll_rect.width : 0), 0);
1693 gdk_draw_rectangle (widget->window,
1694 widget->style->bg_gc[GTK_STATE_NORMAL],
1699 widget->allocation.height -
1700 priv->hscroll_rect.height);
1705 if (G_UNLIKELY (priv->initial_effect)) {
1707 hildon_pannable_area_initial_effect (widget);
1709 priv->initial_effect = FALSE;
1712 return GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->expose_event (widget, event);
1716 hildon_pannable_area_get_topmost (GdkWindow * window,
1718 gint * tx, gint * ty,
1721 /* Find the GdkWindow at the given point, by recursing from a given
1722 * parent GdkWindow. Optionally return the co-ordinates transformed
1723 * relative to the child window.
1726 GList *c, *children;
1727 GdkWindow *selected_window = NULL;
1729 gdk_drawable_get_size (GDK_DRAWABLE (window), &width, &height);
1730 if ((x < 0) || (x >= width) || (y < 0) || (y >= height))
1733 children = gdk_window_peek_children (window);
1740 selected_window = window;
1743 for (c = children; c; c = c->next) {
1744 GdkWindow *child = (GdkWindow *) c->data;
1747 gdk_drawable_get_size (GDK_DRAWABLE (child), &width, &height);
1748 gdk_window_get_position (child, &wx, &wy);
1750 if ((x >= wx) && (x < (wx + width)) && (y >= wy) && (y < (wy + height)) &&
1751 (gdk_window_is_visible (child))) {
1753 if (gdk_window_peek_children (child)) {
1754 selected_window = hildon_pannable_area_get_topmost (child, x-wx, y-wy,
1756 if (!selected_window) {
1761 selected_window = child;
1764 if ((gdk_window_get_events (child)&mask)) {
1769 selected_window = child;
1775 return selected_window;
1779 synth_crossing (GdkWindow * child,
1781 gint x_root, gint y_root,
1782 guint32 time, gboolean in)
1784 GdkEventCrossing *crossing_event;
1785 GdkEventType type = in ? GDK_ENTER_NOTIFY : GDK_LEAVE_NOTIFY;
1787 /* Send synthetic enter event */
1788 crossing_event = (GdkEventCrossing *) gdk_event_new (type);
1789 ((GdkEventAny *) crossing_event)->type = type;
1790 ((GdkEventAny *) crossing_event)->window = g_object_ref (child);
1791 ((GdkEventAny *) crossing_event)->send_event = FALSE;
1792 crossing_event->subwindow = g_object_ref (child);
1793 crossing_event->time = time;
1794 crossing_event->x = x;
1795 crossing_event->y = y;
1796 crossing_event->x_root = x_root;
1797 crossing_event->y_root = y_root;
1798 crossing_event->mode = GDK_CROSSING_NORMAL;
1799 crossing_event->detail = GDK_NOTIFY_UNKNOWN;
1800 crossing_event->focus = FALSE;
1801 crossing_event->state = 0;
1802 gdk_event_put ((GdkEvent *) crossing_event);
1803 gdk_event_free ((GdkEvent *) crossing_event);
1807 hildon_pannable_area_button_press_cb (GtkWidget * widget,
1808 GdkEventButton * event)
1811 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1813 if ((!priv->enabled) || (event->button != 1) ||
1814 ((event->time == priv->last_time) &&
1815 (priv->last_type == 1)) || (gtk_bin_get_child (GTK_BIN (widget)) == NULL))
1818 priv->scroll_indicator_event_interrupt = 1;
1820 hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget),
1821 priv->scroll_indicator_alpha);
1823 priv->last_time = event->time;
1824 priv->last_type = 1;
1826 priv->scroll_to_x = -1;
1827 priv->scroll_to_y = -1;
1829 if (priv->clicked && priv->child) {
1830 /* Widget stole focus on last click, send crossing-out event */
1831 synth_crossing (priv->child, 0, 0, event->x_root, event->y_root,
1832 event->time, FALSE);
1840 /* Don't allow a click if we're still moving fast */
1841 if ((ABS (priv->vel_x) <= (priv->vmax * priv->vfast_factor)) &&
1842 (ABS (priv->vel_y) <= (priv->vmax * priv->vfast_factor)))
1844 hildon_pannable_area_get_topmost (gtk_bin_get_child (GTK_BIN (widget))->window,
1845 event->x, event->y, &x, &y, GDK_BUTTON_PRESS_MASK);
1849 priv->clicked = TRUE;
1851 /* Stop scrolling on mouse-down (so you can flick, then hold to stop) */
1857 gdk_drawable_get_size (priv->child, &priv->child_width,
1858 &priv->child_height);
1859 priv->last_in = TRUE;
1861 g_object_add_weak_pointer ((GObject *) priv->child,
1862 (gpointer) & priv->child);
1864 event = (GdkEventButton *) gdk_event_copy ((GdkEvent *) event);
1870 synth_crossing (priv->child, x, y, event->x_root,
1871 event->y_root, event->time, TRUE);
1873 /* Send synthetic click (button press/release) event */
1874 ((GdkEventAny *) event)->window = g_object_ref (priv->child);
1876 gdk_event_put ((GdkEvent *) event);
1877 gdk_event_free ((GdkEvent *) event);
1885 hildon_pannable_area_check_scrollbars (HildonPannableArea * area)
1887 HildonPannableAreaPrivate *priv = area->priv;
1888 gboolean prev_hscroll_visible, prev_vscroll_visible;
1890 prev_hscroll_visible = priv->hscroll_visible;
1891 prev_vscroll_visible = priv->vscroll_visible;
1893 if (!gtk_bin_get_child (GTK_BIN (area))) {
1894 priv->vscroll_visible = FALSE;
1895 priv->hscroll_visible = FALSE;
1897 switch (priv->hscrollbar_policy) {
1898 case GTK_POLICY_ALWAYS:
1899 priv->hscroll_visible = TRUE;
1901 case GTK_POLICY_NEVER:
1902 priv->hscroll_visible = FALSE;
1905 priv->hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
1906 priv->hadjust->page_size);
1909 switch (priv->vscrollbar_policy) {
1910 case GTK_POLICY_ALWAYS:
1911 priv->vscroll_visible = TRUE;
1913 case GTK_POLICY_NEVER:
1914 priv->vscroll_visible = FALSE;
1917 priv->vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
1918 priv->vadjust->page_size);
1921 /* Store the vscroll/hscroll areas for redrawing */
1922 if (priv->vscroll_visible) {
1923 GtkAllocation *allocation = >K_WIDGET (area)->allocation;
1924 priv->vscroll_rect.x = allocation->width - priv->indicator_width;
1925 priv->vscroll_rect.y = 0;
1926 priv->vscroll_rect.width = priv->indicator_width;
1927 priv->vscroll_rect.height = allocation->height -
1928 (priv->hscroll_visible ? priv->indicator_width : 0);
1930 if (priv->hscroll_visible) {
1931 GtkAllocation *allocation = >K_WIDGET (area)->allocation;
1932 priv->hscroll_rect.y = allocation->height - priv->indicator_width;
1933 priv->hscroll_rect.x = 0;
1934 priv->hscroll_rect.height = priv->indicator_width;
1935 priv->hscroll_rect.width = allocation->width -
1936 (priv->vscroll_visible ? priv->indicator_width : 0);
1940 return ((priv->hscroll_visible != prev_hscroll_visible) ||
1941 (priv->vscroll_visible != prev_vscroll_visible));
1945 hildon_pannable_area_refresh (HildonPannableArea * area)
1947 if (GTK_WIDGET_DRAWABLE (area) &&
1948 hildon_pannable_area_check_scrollbars (area)) {
1949 gtk_widget_queue_resize (GTK_WIDGET (area));
1951 hildon_pannable_area_redraw (area);
1955 /* Scroll by a particular amount (in pixels). Optionally, return if
1956 * the scroll on a particular axis was successful.
1959 hildon_pannable_axis_scroll (HildonPannableArea *area,
1960 GtkAdjustment *adjust,
1964 gint *overshot_dist,
1970 HildonPannableAreaPrivate *priv = area->priv;
1972 dist = gtk_adjustment_get_value (adjust) - inc;
1975 * We use overshot_dist to define the distance of the current overshoot,
1976 * and overshooting to define the direction/whether or not we are overshot
1978 if (!(*overshooting)) {
1980 /* Initiation of the overshoot happens when the finger is released
1981 * and the current position of the pannable contents are out of range
1983 if (dist < adjust->lower) {
1986 dist = adjust->lower;
1988 if (overshoot_max!=0) {
1991 *overshot_dist = CLAMP (*overshot_dist + *vel, 0, overshoot_max);
1992 *vel = MIN (priv->vmax_overshooting, *vel);
1993 gtk_widget_queue_resize (GTK_WIDGET (area));
1997 } else if (dist > adjust->upper - adjust->page_size) {
2000 dist = adjust->upper - adjust->page_size;
2002 if (overshoot_max!=0) {
2005 *overshot_dist = CLAMP (*overshot_dist + *vel, -overshoot_max, 0);
2006 *vel = MAX (-priv->vmax_overshooting, *vel);
2007 gtk_widget_queue_resize (GTK_WIDGET (area));
2012 if ((*scroll_to) != -1) {
2013 if (((inc < 0)&&(*scroll_to <= dist))||
2014 ((inc > 0)&&(*scroll_to >= dist))) {
2022 gtk_adjustment_set_value (adjust, dist);
2024 if (!priv->clicked) {
2026 /* When the overshoot has started we continue for
2027 * PROP_BOUNCE_STEPS more steps into the overshoot before we
2028 * reverse direction. The deceleration factor is calculated
2029 * based on the percentage distance from the first item with
2030 * each iteration, therefore always returning us to the
2031 * top/bottom most element
2033 if (*overshot_dist > 0) {
2035 if ((*overshooting < priv->bounce_steps) && (*vel > 0)) {
2037 *vel = (((gdouble)*overshot_dist)/overshoot_max) * (*vel);
2038 } else if ((*overshooting >= priv->bounce_steps) && (*vel > 0)) {
2040 } else if ((*overshooting > 1) && (*vel < 0)) {
2041 /* we add the MIN in order to avoid very small speeds */
2042 *vel = MIN ((((gdouble)*overshot_dist)*0.4) * -1, -2.0);
2045 *overshot_dist = CLAMP (*overshot_dist + *vel, 0, overshoot_max);
2047 gtk_widget_queue_resize (GTK_WIDGET (area));
2049 } else if (*overshot_dist < 0) {
2051 if ((*overshooting < priv->bounce_steps) && (*vel < 0)) {
2053 *vel = (((gdouble)*overshot_dist)/overshoot_max) * (*vel) * -1;
2054 } else if ((*overshooting >= priv->bounce_steps) && (*vel < 0)) {
2056 } else if ((*overshooting > 1) && (*vel > 0)) {
2057 /* we add the MAX in order to avoid very small speeds */
2058 *vel = MAX ((((gdouble)*overshot_dist)*0.4) * -1, 2.0);
2061 *overshot_dist = CLAMP (*overshot_dist + (*vel), -overshoot_max, 0);
2063 gtk_widget_queue_resize (GTK_WIDGET (area));
2068 gtk_widget_queue_resize (GTK_WIDGET (area));
2072 gint overshot_dist_old = *overshot_dist;
2074 if (*overshot_dist > 0) {
2075 *overshot_dist = CLAMP ((*overshot_dist) + inc, 0, overshoot_max);
2076 } else if (*overshot_dist < 0) {
2077 *overshot_dist = CLAMP ((*overshot_dist) + inc, -1 * overshoot_max, 0);
2080 gtk_adjustment_set_value (adjust, dist);
2083 if (*overshot_dist != overshot_dist_old)
2084 gtk_widget_queue_resize (GTK_WIDGET (area));
2090 hildon_pannable_area_scroll (HildonPannableArea *area,
2091 gdouble x, gdouble y)
2094 HildonPannableAreaPrivate *priv = area->priv;
2095 gboolean hscroll_visible, vscroll_visible;
2097 if (gtk_bin_get_child (GTK_BIN (area)) == NULL)
2100 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
2101 priv->vadjust->page_size);
2102 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
2103 priv->hadjust->page_size);
2108 if (vscroll_visible) {
2109 hildon_pannable_axis_scroll (area, priv->vadjust, &priv->vel_y, y,
2110 &priv->overshooting_y, &priv->overshot_dist_y,
2111 &priv->scroll_to_y, priv->vovershoot_max, &sy);
2116 if (hscroll_visible) {
2117 hildon_pannable_axis_scroll (area, priv->hadjust, &priv->vel_x, x,
2118 &priv->overshooting_x, &priv->overshot_dist_x,
2119 &priv->scroll_to_x, priv->hovershoot_max, &sx);
2124 /* If the scroll on a particular axis wasn't succesful, reset the
2125 * initial scroll position to the new mouse co-ordinate. This means
2126 * when you get to the top of the page, dragging down works immediately.
2128 if (priv->mode == HILDON_PANNABLE_AREA_MODE_ACCEL) {
2140 hildon_pannable_area_timeout (HildonPannableArea * area)
2142 HildonPannableAreaPrivate *priv = area->priv;
2144 if ((!priv->enabled) || (priv->mode == HILDON_PANNABLE_AREA_MODE_PUSH)) {
2150 if (!priv->clicked) {
2151 /* Decelerate gradually when pointer is raised */
2152 if ((!priv->overshot_dist_y) &&
2153 (!priv->overshot_dist_x)) {
2155 /* in case we move to a specific point do not decelerate when arriving */
2156 if ((priv->scroll_to_x != -1)||(priv->scroll_to_y != -1)) {
2158 if (ABS (priv->vel_x) >= 1.5) {
2159 priv->vel_x *= priv->decel;
2162 if (ABS (priv->vel_y) >= 1.5) {
2163 priv->vel_y *= priv->decel;
2167 if ((!priv->low_friction_mode) ||
2168 ((priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ) &&
2169 (ABS (priv->vel_x) < 0.8*priv->vmax)))
2170 priv->vel_x *= priv->decel;
2172 if ((!priv->low_friction_mode) ||
2173 ((priv->mov_mode&HILDON_MOVEMENT_MODE_VERT) &&
2174 (ABS (priv->vel_y) < 0.8*priv->vmax)))
2175 priv->vel_y *= priv->decel;
2177 if ((ABS (priv->vel_x) < 1.0) && (ABS (priv->vel_y) < 1.0)) {
2186 } else if (priv->mode == HILDON_PANNABLE_AREA_MODE_AUTO) {
2192 hildon_pannable_area_scroll (area, priv->vel_x, priv->vel_y);
2198 hildon_pannable_area_calculate_velocity (gdouble *vel,
2202 gdouble drag_inertia,
2208 if (ABS (dist) >= RATIO_TOLERANCE) {
2209 rawvel = (dist / ABS (delta)) * force;
2210 *vel = *vel * (1 - drag_inertia) +
2211 rawvel * drag_inertia;
2212 *vel = *vel > 0 ? MIN (*vel, vmax)
2213 : MAX (*vel, -1 * vmax);
2218 hildon_pannable_area_motion_event_scroll_timeout (HildonPannableArea *area)
2220 HildonPannableAreaPrivate *priv = area->priv;
2222 if ((priv->motion_x != 0)||(priv->motion_y != 0))
2223 hildon_pannable_area_scroll (area, priv->motion_x, priv->motion_y);
2225 priv->motion_event_scroll_timeout = 0;
2231 hildon_pannable_area_motion_event_scroll (HildonPannableArea *area,
2232 gdouble x, gdouble y)
2234 HildonPannableAreaPrivate *priv = area->priv;
2236 if (priv->motion_event_scroll_timeout) {
2238 priv->motion_x += x;
2239 priv->motion_y += y;
2243 /* we do not delay the first event but the next ones */
2244 hildon_pannable_area_scroll (area, x, y);
2249 priv->motion_event_scroll_timeout = gdk_threads_add_timeout
2250 ((gint) (1000.0 / (gdouble) MOTION_EVENTS_PER_SECOND),
2251 (GSourceFunc) hildon_pannable_area_motion_event_scroll_timeout, area);
2256 hildon_pannable_area_motion_notify_cb (GtkWidget * widget,
2257 GdkEventMotion * event)
2259 HildonPannableArea *area = HILDON_PANNABLE_AREA (widget);
2260 HildonPannableAreaPrivate *priv = area->priv;
2264 if (gtk_bin_get_child (GTK_BIN (widget)) == NULL)
2267 if ((!priv->enabled) || (!priv->clicked) ||
2268 ((event->time == priv->last_time) && (priv->last_type == 2))) {
2269 gdk_window_get_pointer (widget->window, NULL, NULL, 0);
2273 if (priv->last_type == 1) {
2274 priv->first_drag = TRUE;
2277 x = event->x - priv->x;
2278 y = event->y - priv->y;
2280 if (priv->first_drag && (!priv->moved) &&
2281 ((ABS (x) > (priv->panning_threshold))
2282 || (ABS (y) > (priv->panning_threshold)))) {
2287 if (priv->first_drag) {
2288 gboolean vscroll_visible;
2289 gboolean hscroll_visible;
2291 if (ABS (priv->iy - event->y) >=
2292 ABS (priv->ix - event->x)) {
2294 g_signal_emit (area,
2295 pannable_area_signals[VERTICAL_MOVEMENT],
2296 0, (priv->iy > event->y) ?
2297 HILDON_MOVEMENT_UP :
2298 HILDON_MOVEMENT_DOWN,
2299 (gdouble)priv->ix, (gdouble)priv->iy);
2301 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
2302 priv->vadjust->page_size);
2304 if (!((vscroll_visible)&&
2305 (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT))) {
2307 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
2308 priv->hadjust->page_size);
2310 /* even in case we do not have to move we check if this
2311 could be a fake horizontal movement */
2312 if (!((hscroll_visible)&&
2313 (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ)) ||
2314 (ABS (priv->iy - event->y) -
2315 ABS (priv->ix - event->x) >= priv->direction_error_margin))
2316 priv->moved = FALSE;
2320 g_signal_emit (area,
2321 pannable_area_signals[HORIZONTAL_MOVEMENT],
2322 0, (priv->ix > event->x) ?
2323 HILDON_MOVEMENT_LEFT :
2324 HILDON_MOVEMENT_RIGHT,
2325 (gdouble)priv->ix, (gdouble)priv->iy);
2327 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
2328 priv->hadjust->page_size);
2330 if (!((hscroll_visible)&&
2331 (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ))) {
2333 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
2334 priv->vadjust->page_size);
2336 /* even in case we do not have to move we check if this
2337 could be a fake vertical movement */
2338 if (!((vscroll_visible) &&
2339 (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT)) ||
2340 (ABS (priv->ix - event->x) -
2341 ABS (priv->iy - event->y) >= priv->direction_error_margin))
2342 priv->moved = FALSE;
2346 if ((priv->moved)&&(priv->child)) {
2349 pos_x = priv->cx + (event->x - priv->ix);
2350 pos_y = priv->cy + (event->y - priv->iy);
2352 synth_crossing (priv->child, pos_x, pos_y, event->x_root,
2353 event->y_root, event->time, FALSE);
2357 priv->first_drag = FALSE;
2359 if ((priv->mode != HILDON_PANNABLE_AREA_MODE_PUSH) &&
2360 (priv->mode != HILDON_PANNABLE_AREA_MODE_AUTO)) {
2363 priv->idle_id = gdk_threads_add_timeout ((gint)
2364 (1000.0 / (gdouble) priv->sps),
2366 hildon_pannable_area_timeout, area);
2371 switch (priv->mode) {
2372 case HILDON_PANNABLE_AREA_MODE_PUSH:
2373 /* Scroll by the amount of pixels the cursor has moved
2374 * since the last motion event.
2376 hildon_pannable_area_motion_event_scroll (area, x, y);
2380 case HILDON_PANNABLE_AREA_MODE_ACCEL:
2381 /* Set acceleration relative to the initial click */
2382 priv->ex = event->x;
2383 priv->ey = event->y;
2384 priv->vel_x = ((x > 0) ? 1 : -1) *
2386 (gdouble) widget->allocation.width) *
2387 (priv->vmax - priv->vmin)) + priv->vmin);
2388 priv->vel_y = ((y > 0) ? 1 : -1) *
2390 (gdouble) widget->allocation.height) *
2391 (priv->vmax - priv->vmin)) + priv->vmin);
2393 case HILDON_PANNABLE_AREA_MODE_AUTO:
2395 delta = event->time - priv->last_time;
2397 if (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT) {
2398 gdouble dist = event->y - priv->y;
2400 hildon_pannable_area_calculate_velocity (&priv->vel_y,
2412 if (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ) {
2413 gdouble dist = event->x - priv->x;
2415 hildon_pannable_area_calculate_velocity (&priv->vel_x,
2427 hildon_pannable_area_motion_event_scroll (area, x, y);
2429 if (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ)
2431 if (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT)
2439 } else if (priv->child) {
2443 pos_x = priv->cx + (event->x - priv->ix);
2444 pos_y = priv->cy + (event->y - priv->iy);
2446 in = (((0 <= pos_x)&&(priv->child_width >= pos_x)) &&
2447 ((0 <= pos_y)&&(priv->child_height >= pos_y)));
2449 if (((!priv->last_in)&&in)||((priv->last_in)&&(!in))) {
2451 synth_crossing (priv->child, pos_x, pos_y, event->x_root,
2452 event->y_root, event->time, in);
2458 priv->last_time = event->time;
2459 priv->last_type = 2;
2462 /* Send motion notify to child */
2463 event = (GdkEventMotion *) gdk_event_copy ((GdkEvent *) event);
2464 event->x = priv->cx + (event->x - priv->ix);
2465 event->y = priv->cy + (event->y - priv->iy);
2466 event->window = g_object_ref (priv->child);
2467 gdk_event_put ((GdkEvent *) event);
2468 gdk_event_free ((GdkEvent *) event);
2471 gdk_window_get_pointer (widget->window, NULL, NULL, 0);
2477 hildon_pannable_leave_notify_event (GtkWidget *widget,
2478 GdkEventCrossing *event)
2480 HildonPannableArea *area = HILDON_PANNABLE_AREA (widget);
2481 HildonPannableAreaPrivate *priv = area->priv;
2483 if ((priv->child)&&(priv->last_in)) {
2484 priv->last_in = FALSE;
2486 synth_crossing (priv->child, 0, 0, event->x_root,
2487 event->y_root, event->time, FALSE);
2494 hildon_pannable_area_button_release_cb (GtkWidget * widget,
2495 GdkEventButton * event)
2497 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
2501 if (((event->time == priv->last_time) && (priv->last_type == 3))
2502 || (gtk_bin_get_child (GTK_BIN (widget)) == NULL)
2503 || (!priv->clicked) || (!priv->enabled) || (event->button != 1))
2506 priv->scroll_indicator_event_interrupt = 0;
2507 priv->scroll_delay_counter = priv->scrollbar_fade_delay;
2509 /* move all the way to the last position */
2510 if (priv->motion_event_scroll_timeout) {
2511 g_source_remove (priv->motion_event_scroll_timeout);
2512 hildon_pannable_area_motion_event_scroll_timeout (HILDON_PANNABLE_AREA (widget));
2517 if (priv->last_type == 2) {
2518 gdouble delta = event->time - priv->last_time;
2520 if (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT) {
2521 gdouble dist = event->y - priv->y;
2523 if (ABS (dist) >= 1.0) {
2524 hildon_pannable_area_calculate_velocity (&priv->vel_y,
2532 priv->motion_y = dist;
2533 hildon_pannable_area_motion_event_scroll_timeout (HILDON_PANNABLE_AREA (widget));
2535 if (delta >= CURSOR_STOPPED_TIMEOUT) {
2541 if (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ) {
2542 gdouble dist = event->x - priv->x;
2544 if (ABS (dist) >= 1.0) {
2545 hildon_pannable_area_calculate_velocity (&priv->vel_x,
2552 priv->motion_x = dist;
2553 hildon_pannable_area_motion_event_scroll_timeout (HILDON_PANNABLE_AREA (widget));
2555 if (delta >= CURSOR_STOPPED_TIMEOUT) {
2562 if ((ABS (priv->vel_y) > 1.0)||
2563 (ABS (priv->vel_x) > 1.0)) {
2564 priv->scroll_indicator_alpha = 1.0;
2567 hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget),
2568 priv->scroll_indicator_alpha);
2570 priv->clicked = FALSE;
2572 if (priv->mode == HILDON_PANNABLE_AREA_MODE_AUTO ||
2573 priv->mode == HILDON_PANNABLE_AREA_MODE_ACCEL) {
2575 /* If overshoot has been initiated with a finger down, on release set max speed */
2576 if (priv->overshot_dist_y != 0) {
2577 priv->overshooting_y = priv->bounce_steps; /* Hack to stop a bounce in the finger down case */
2578 priv->vel_y = priv->vmax_overshooting;
2581 if (priv->overshot_dist_x != 0) {
2582 priv->overshooting_x = priv->bounce_steps; /* Hack to stop a bounce in the finger down case */
2583 priv->vel_x = priv->vmax_overshooting;
2586 if ((ABS (priv->vel_y) >= priv->vmin) ||
2587 (ABS (priv->vel_x) >= priv->vmin)) {
2590 priv->idle_id = gdk_threads_add_timeout ((gint) (1000.0 / (gdouble) priv->sps),
2592 hildon_pannable_area_timeout, widget);
2596 priv->last_time = event->time;
2597 priv->last_type = 3;
2600 priv->moved = FALSE;
2605 hildon_pannable_area_get_topmost (gtk_bin_get_child (GTK_BIN (widget))->window,
2606 event->x, event->y, &x, &y, GDK_BUTTON_RELEASE_MASK);
2608 event = (GdkEventButton *) gdk_event_copy ((GdkEvent *) event);
2612 /* Leave the widget if we've moved - This doesn't break selection,
2613 * but stops buttons from being clicked.
2615 if ((child != priv->child) || (priv->moved)) {
2616 /* Send synthetic leave event */
2617 synth_crossing (priv->child, x, y, event->x_root,
2618 event->y_root, event->time, FALSE);
2619 /* Send synthetic button release event */
2620 ((GdkEventAny *) event)->window = g_object_ref (priv->child);
2621 gdk_event_put ((GdkEvent *) event);
2623 /* Send synthetic button release event */
2624 ((GdkEventAny *) event)->window = g_object_ref (child);
2625 gdk_event_put ((GdkEvent *) event);
2626 /* Send synthetic leave event */
2627 synth_crossing (priv->child, x, y, event->x_root,
2628 event->y_root, event->time, FALSE);
2630 g_object_remove_weak_pointer ((GObject *) priv->child,
2631 (gpointer) & priv->child);
2633 priv->moved = FALSE;
2634 gdk_event_free ((GdkEvent *) event);
2639 /* utility event handler */
2641 hildon_pannable_area_scroll_cb (GtkWidget *widget,
2642 GdkEventScroll *event)
2644 GtkAdjustment *adj = NULL;
2645 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
2647 if ((!priv->enabled) ||
2648 (gtk_bin_get_child (GTK_BIN (widget)) == NULL))
2651 priv->scroll_indicator_event_interrupt = 0;
2652 priv->scroll_delay_counter = priv->scrollbar_fade_delay + 20;
2654 hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget), 1.0);
2656 /* Stop inertial scrolling */
2657 if (priv->idle_id) {
2660 priv->overshooting_x = 0;
2661 priv->overshooting_y = 0;
2663 if ((priv->overshot_dist_x>0)||(priv->overshot_dist_y>0)) {
2664 priv->overshot_dist_x = 0;
2665 priv->overshot_dist_y = 0;
2667 gtk_widget_queue_resize (GTK_WIDGET (widget));
2670 g_source_remove (priv->idle_id);
2674 if (event->direction == GDK_SCROLL_UP || event->direction == GDK_SCROLL_DOWN)
2675 adj = priv->vadjust;
2677 adj = priv->hadjust;
2681 gdouble delta, new_value;
2683 /* from gtkrange.c calculate delta*/
2684 delta = pow (adj->page_size, 2.0 / 3.0);
2686 if (event->direction == GDK_SCROLL_UP ||
2687 event->direction == GDK_SCROLL_LEFT)
2690 new_value = CLAMP (adj->value + delta, adj->lower, adj->upper - adj->page_size);
2692 gtk_adjustment_set_value (adj, new_value);
2699 hildon_pannable_area_child_mapped (GtkWidget *widget,
2703 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (user_data)->priv;
2705 if (priv->event_window != NULL && priv->enabled)
2706 gdk_window_raise (priv->event_window);
2710 hildon_pannable_area_add (GtkContainer *container, GtkWidget *child)
2712 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (container)->priv;
2714 g_return_if_fail (gtk_bin_get_child (GTK_BIN (container)) == NULL);
2716 gtk_widget_set_parent (child, GTK_WIDGET (container));
2717 GTK_BIN (container)->child = child;
2719 g_signal_connect_after (child, "map-event",
2720 G_CALLBACK (hildon_pannable_area_child_mapped),
2723 if (!gtk_widget_set_scroll_adjustments (child, priv->hadjust, priv->vadjust)) {
2724 g_warning ("%s: cannot add non scrollable widget, "
2725 "wrap it in a viewport", __FUNCTION__);
2730 hildon_pannable_area_remove (GtkContainer *container, GtkWidget *child)
2732 g_return_if_fail (HILDON_IS_PANNABLE_AREA (container));
2733 g_return_if_fail (child != NULL);
2734 g_return_if_fail (gtk_bin_get_child (GTK_BIN (container)) == child);
2736 gtk_widget_set_scroll_adjustments (child, NULL, NULL);
2738 g_signal_handlers_disconnect_by_func (child,
2739 hildon_pannable_area_child_mapped,
2742 /* chain parent class handler to remove child */
2743 GTK_CONTAINER_CLASS (hildon_pannable_area_parent_class)->remove (container, child);
2747 hildon_pannable_calculate_vel_factor (HildonPannableArea * self)
2749 HildonPannableAreaPrivate *priv = self->priv;
2754 n = ceil (priv->sps * priv->scroll_time);
2756 for (i = 1; i < n && fct_i >= RATIO_TOLERANCE; i++) {
2757 fct_i *= priv->decel;
2761 priv->vel_factor = fct;
2765 * hildon_pannable_area_new:
2767 * Create a new pannable area widget
2769 * Returns: the newly created #HildonPannableArea
2775 hildon_pannable_area_new (void)
2777 return g_object_new (HILDON_TYPE_PANNABLE_AREA, NULL);
2781 * hildon_pannable_area_new_full:
2782 * @mode: #HildonPannableAreaMode
2783 * @enabled: Value for the enabled property
2784 * @vel_min: Value for the velocity-min property
2785 * @vel_max: Value for the velocity-max property
2786 * @decel: Value for the deceleration property
2787 * @sps: Value for the sps property
2789 * Create a new #HildonPannableArea widget and set various properties
2791 * returns: the newly create #HildonPannableArea
2797 hildon_pannable_area_new_full (gint mode, gboolean enabled,
2798 gdouble vel_min, gdouble vel_max,
2799 gdouble decel, guint sps)
2801 return g_object_new (HILDON_TYPE_PANNABLE_AREA,
2804 "velocity_min", vel_min,
2805 "velocity_max", vel_max,
2806 "deceleration", decel, "sps", sps, NULL);
2810 * hildon_pannable_area_add_with_viewport:
2811 * @area: A #HildonPannableArea
2812 * @child: Child widget to add to the viewport
2814 * Convenience function used to add a child to a #GtkViewport, and add the
2815 * viewport to the scrolled window.
2821 hildon_pannable_area_add_with_viewport (HildonPannableArea * area,
2825 GtkWidget *viewport;
2827 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
2828 g_return_if_fail (GTK_IS_WIDGET (child));
2829 g_return_if_fail (child->parent == NULL);
2831 bin = GTK_BIN (area);
2833 if (bin->child != NULL)
2835 g_return_if_fail (GTK_IS_VIEWPORT (bin->child));
2836 g_return_if_fail (GTK_BIN (bin->child)->child == NULL);
2838 viewport = bin->child;
2842 HildonPannableAreaPrivate *priv = area->priv;
2844 viewport = gtk_viewport_new (priv->hadjust,
2846 gtk_viewport_set_shadow_type (GTK_VIEWPORT (viewport), GTK_SHADOW_NONE);
2847 gtk_container_add (GTK_CONTAINER (area), viewport);
2850 gtk_widget_show (viewport);
2851 gtk_container_add (GTK_CONTAINER (viewport), child);
2855 * hildon_pannable_area_scroll_to:
2856 * @area: A #HildonPannableArea.
2857 * @x: The x coordinate of the destination point or -1 to ignore this axis.
2858 * @y: The y coordinate of the destination point or -1 to ignore this axis.
2860 * Smoothly scrolls @area to ensure that (@x, @y) is a visible point
2861 * on the widget. To move in only one coordinate, you must set the other one
2862 * to -1. Notice that, in %HILDON_PANNABLE_AREA_MODE_PUSH mode, this function
2863 * works just like hildon_pannable_area_jump_to().
2865 * This function is useful if you need to present the user with a particular
2866 * element inside a scrollable widget, like #GtkTreeView. For instance,
2867 * the following example shows how to scroll inside a #GtkTreeView to
2868 * make visible an item, indicated by the #GtkTreeIter @iter.
2872 * GtkTreePath *path;
2873 * GdkRectangle *rect;
2875 * path = gtk_tree_model_get_path (model, &iter);
2876 * gtk_tree_view_get_background_area (GTK_TREE_VIEW (treeview),
2877 * path, NULL, &rect);
2878 * gtk_tree_view_convert_bin_window_to_tree_coords (GTK_TREE_VIEW (treeview),
2879 * 0, rect.y, NULL, &y);
2880 * hildon_pannable_area_scroll_to (panarea, -1, y);
2881 * gtk_tree_path_free (path);
2885 * If you want to present a child widget in simpler scenarios,
2886 * use hildon_pannable_area_scroll_to_child() instead.
2888 * There is a precondition to this function: the widget must be
2889 * already realized. Check the hildon_pannable_area_jump_to_child() for
2890 * more tips regarding how to call this function during
2896 hildon_pannable_area_scroll_to (HildonPannableArea *area,
2897 const gint x, const gint y)
2899 HildonPannableAreaPrivate *priv;
2901 gint dist_x, dist_y;
2902 gboolean hscroll_visible, vscroll_visible;
2904 g_return_if_fail (GTK_WIDGET_REALIZED (area));
2905 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
2909 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
2910 priv->vadjust->page_size);
2911 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
2912 priv->hadjust->page_size);
2914 if (((!vscroll_visible)&&(!hscroll_visible)) ||
2915 (x == -1 && y == -1)) {
2919 if (priv->mode == HILDON_PANNABLE_AREA_MODE_PUSH)
2920 hildon_pannable_area_jump_to (area, x, y);
2922 width = priv->hadjust->upper - priv->hadjust->lower;
2923 height = priv->vadjust->upper - priv->vadjust->lower;
2925 g_return_if_fail (x < width || y < height);
2927 if ((x > -1)&&(hscroll_visible)) {
2928 priv->scroll_to_x = x - priv->hadjust->page_size/2;
2929 dist_x = priv->scroll_to_x - priv->hadjust->value;
2931 priv->scroll_to_x = -1;
2933 priv->vel_x = - dist_x/priv->vel_factor;
2936 priv->scroll_to_x = -1;
2939 if ((y > -1)&&(vscroll_visible)) {
2940 priv->scroll_to_y = y - priv->vadjust->page_size/2;
2941 dist_y = priv->scroll_to_y - priv->vadjust->value;
2943 priv->scroll_to_y = -1;
2945 priv->vel_y = - dist_y/priv->vel_factor;
2948 priv->scroll_to_y = y;
2951 if ((priv->scroll_to_y == -1) && (priv->scroll_to_y == -1)) {
2955 hildon_pannable_area_launch_fade_timeout (area, 1.0);
2958 priv->idle_id = gdk_threads_add_timeout ((gint) (1000.0 / (gdouble) priv->sps),
2960 hildon_pannable_area_timeout, area);
2964 * hildon_pannable_area_jump_to:
2965 * @area: A #HildonPannableArea.
2966 * @x: The x coordinate of the destination point or -1 to ignore this axis.
2967 * @y: The y coordinate of the destination point or -1 to ignore this axis.
2969 * Jumps the position of @area to ensure that (@x, @y) is a visible
2970 * point in the widget. In order to move in only one coordinate, you
2971 * must set the other one to -1. See hildon_pannable_area_scroll_to()
2972 * function for an example of how to calculate the position of
2973 * children in scrollable widgets like #GtkTreeview.
2975 * There is a precondition to this function: the widget must be
2976 * already realized. Check the hildon_pannable_area_jump_to_child() for
2977 * more tips regarding how to call this function during
2983 hildon_pannable_area_jump_to (HildonPannableArea *area,
2984 const gint x, const gint y)
2986 HildonPannableAreaPrivate *priv;
2989 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
2990 g_return_if_fail (GTK_WIDGET_REALIZED (area));
2991 g_return_if_fail (x >= -1 && y >= -1);
2993 if (x == -1 && y == -1) {
2999 width = priv->hadjust->upper - priv->hadjust->lower;
3000 height = priv->vadjust->upper - priv->vadjust->lower;
3002 g_return_if_fail (x < width || y < height);
3005 gdouble jump_to = x - priv->hadjust->page_size/2;
3007 if (jump_to > priv->hadjust->upper - priv->hadjust->page_size) {
3008 jump_to = priv->hadjust->upper - priv->hadjust->page_size;
3011 gtk_adjustment_set_value (priv->hadjust, jump_to);
3015 gdouble jump_to = y - priv->vadjust->page_size/2;
3017 if (jump_to > priv->vadjust->upper - priv->vadjust->page_size) {
3018 jump_to = priv->vadjust->upper - priv->vadjust->page_size;
3021 gtk_adjustment_set_value (priv->vadjust, jump_to);
3024 priv->scroll_indicator_alpha = 1.0;
3026 if (priv->scroll_indicator_timeout) {
3027 g_source_remove (priv->scroll_indicator_timeout);
3028 priv->scroll_indicator_timeout = 0;
3031 if (priv->idle_id) {
3034 priv->overshooting_x = 0;
3035 priv->overshooting_y = 0;
3037 if ((priv->overshot_dist_x>0)||(priv->overshot_dist_y>0)) {
3038 priv->overshot_dist_x = 0;
3039 priv->overshot_dist_y = 0;
3041 gtk_widget_queue_resize (GTK_WIDGET (area));
3044 g_source_remove (priv->idle_id);
3050 * hildon_pannable_area_scroll_to_child:
3051 * @area: A #HildonPannableArea.
3052 * @child: A #GtkWidget, descendant of @area.
3054 * Smoothly scrolls until @child is visible inside @area. @child must
3055 * be a descendant of @area. If you need to scroll inside a scrollable
3056 * widget, e.g., #GtkTreeview, see hildon_pannable_area_scroll_to().
3058 * There is a precondition to this function: the widget must be
3059 * already realized. Check the hildon_pannable_area_jump_to_child() for
3060 * more tips regarding how to call this function during
3066 hildon_pannable_area_scroll_to_child (HildonPannableArea *area, GtkWidget *child)
3068 GtkWidget *bin_child;
3071 g_return_if_fail (GTK_WIDGET_REALIZED (area));
3072 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3073 g_return_if_fail (GTK_IS_WIDGET (child));
3074 g_return_if_fail (gtk_widget_is_ancestor (child, GTK_WIDGET (area)));
3076 if (GTK_BIN (area)->child == NULL)
3079 /* We need to get to check the child of the inside the area */
3080 bin_child = GTK_BIN (area)->child;
3082 /* we check if we added a viewport */
3083 if (GTK_IS_VIEWPORT (bin_child)) {
3084 bin_child = GTK_BIN (bin_child)->child;
3087 if (gtk_widget_translate_coordinates (child, bin_child, 0, 0, &x, &y))
3088 hildon_pannable_area_scroll_to (area, x, y);
3092 * hildon_pannable_area_jump_to_child:
3093 * @area: A #HildonPannableArea.
3094 * @child: A #GtkWidget, descendant of @area.
3096 * Jumps to make sure @child is visible inside @area. @child must
3097 * be a descendant of @area. If you want to move inside a scrollable
3098 * widget, like, #GtkTreeview, see hildon_pannable_area_scroll_to().
3100 * There is a precondition to this function: the widget must be
3101 * already realized. You can control if the widget is ready with the
3102 * GTK_WIDGET_REALIZED macro. If you want to call this function during
3103 * the initialization process of the widget do it inside a callback to
3104 * the ::realize signal, using g_signal_connect_after() function.
3109 hildon_pannable_area_jump_to_child (HildonPannableArea *area, GtkWidget *child)
3111 GtkWidget *bin_child;
3114 g_return_if_fail (GTK_WIDGET_REALIZED (area));
3115 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3116 g_return_if_fail (GTK_IS_WIDGET (child));
3117 g_return_if_fail (gtk_widget_is_ancestor (child, GTK_WIDGET (area)));
3119 if (gtk_bin_get_child (GTK_BIN (area)) == NULL)
3122 /* We need to get to check the child of the inside the area */
3123 bin_child = gtk_bin_get_child (GTK_BIN (area));
3125 /* we check if we added a viewport */
3126 if (GTK_IS_VIEWPORT (bin_child)) {
3127 bin_child = gtk_bin_get_child (GTK_BIN (bin_child));
3130 if (gtk_widget_translate_coordinates (child, bin_child, 0, 0, &x, &y))
3131 hildon_pannable_area_jump_to (area, x, y);
3135 * hildon_pannable_get_child_widget_at:
3136 * @area: A #HildonPannableArea.
3137 * @x: horizontal coordinate of the point
3138 * @y: vertical coordinate of the point
3140 * Get the widget at the point (x, y) inside the pannable area. In
3141 * case no widget found it returns NULL.
3143 * returns: the #GtkWidget if we find a widget, NULL in any other case
3148 hildon_pannable_get_child_widget_at (HildonPannableArea *area,
3149 gdouble x, gdouble y)
3151 GdkWindow *window = NULL;
3152 GtkWidget *child_widget = NULL;
3154 window = hildon_pannable_area_get_topmost
3155 (gtk_bin_get_child (GTK_BIN (area))->window,
3156 x, y, NULL, NULL, GDK_ALL_EVENTS_MASK);
3158 gdk_window_get_user_data (window, (gpointer) &child_widget);
3160 return child_widget;
3165 * hildon_pannable_area_get_hadjustment:
3166 * @area: A #HildonPannableArea.
3168 * Returns the horizontal adjustment. This adjustment is the internal
3169 * widget adjustment used to control the animations. Do not modify it
3170 * directly to change the position of the pannable, to do that use the
3171 * pannable API. If you modify the object directly it could cause
3172 * artifacts in the animations.
3174 * returns: The horizontal #GtkAdjustment
3179 hildon_pannable_area_get_hadjustment (HildonPannableArea *area)
3182 g_return_val_if_fail (HILDON_IS_PANNABLE_AREA (area), NULL);
3184 return area->priv->hadjust;
3188 * hildon_pannable_area_get_vadjustment:
3189 * @area: A #HildonPannableArea.
3191 * Returns the vertical adjustment. This adjustment is the internal
3192 * widget adjustment used to control the animations. Do not modify it
3193 * directly to change the position of the pannable, to do that use the
3194 * pannable API. If you modify the object directly it could cause
3195 * artifacts in the animations.
3197 * returns: The vertical #GtkAdjustment
3202 hildon_pannable_area_get_vadjustment (HildonPannableArea *area)
3204 g_return_val_if_fail (HILDON_IS_PANNABLE_AREA (area), NULL);
3206 return area->priv->vadjust;
3211 * hildon_pannable_area_get_size_request_policy:
3212 * @area: A #HildonPannableArea.
3214 * This function returns the current size request policy of the
3215 * widget. That policy controls the way the size_request is done in
3216 * the pannable area. Check
3217 * hildon_pannable_area_set_size_request_policy() for a more detailed
3220 * returns: the policy is currently being used in the widget
3221 * #HildonSizeRequestPolicy.
3225 HildonSizeRequestPolicy
3226 hildon_pannable_area_get_size_request_policy (HildonPannableArea *area)
3228 HildonPannableAreaPrivate *priv;
3230 g_return_val_if_fail (HILDON_IS_PANNABLE_AREA (area), FALSE);
3234 return priv->size_request_policy;
3238 * hildon_pannable_area_set_size_request_policy:
3239 * @area: A #HildonPannableArea.
3240 * @size_request_policy: One of the allowed #HildonSizeRequestPolicy
3242 * This function sets the pannable area size request policy. That
3243 * policy controls the way the size_request is done in the pannable
3244 * area. Pannable can use the size request of its children
3245 * (#HILDON_SIZE_REQUEST_CHILDREN) or the minimum size required for
3246 * the area itself (#HILDON_SIZE_REQUEST_MINIMUM), the latter is the
3247 * default. Recall this size depends on the scrolling policy you are
3248 * requesting to the pannable area, if you set #GTK_POLICY_NEVER this
3249 * parameter will not have any effect with
3250 * #HILDON_SIZE_REQUEST_MINIMUM set.
3254 * Deprecated: This method and the policy request is deprecated, DO
3255 * NOT use it in future code, the only policy properly supported in
3256 * gtk+ nowadays is the minimum size. Use #gtk_window_set_default_size
3257 * or #gtk_window_set_geometry_hints with the proper size in your case
3258 * to define the height of your dialogs.
3261 hildon_pannable_area_set_size_request_policy (HildonPannableArea *area,
3262 HildonSizeRequestPolicy size_request_policy)
3264 HildonPannableAreaPrivate *priv;
3266 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3270 if (priv->size_request_policy == size_request_policy)
3273 priv->size_request_policy = size_request_policy;
3275 gtk_widget_queue_resize (GTK_WIDGET (area));
3277 g_object_notify (G_OBJECT (area), "size-request-policy");