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 MAX_SPEED_THRESHOLD 250
61 #define PANNABLE_MAX_WIDTH 788
62 #define PANNABLE_MAX_HEIGHT 378
64 G_DEFINE_TYPE (HildonPannableArea, hildon_pannable_area, GTK_TYPE_BIN)
66 #define PANNABLE_AREA_PRIVATE(o) \
67 (G_TYPE_INSTANCE_GET_PRIVATE ((o), HILDON_TYPE_PANNABLE_AREA, \
68 HildonPannableAreaPrivate))
70 struct _HildonPannableAreaPrivate {
71 HildonPannableAreaMode mode;
72 HildonMovementMode mov_mode;
73 GdkWindow *event_window;
74 gdouble x; /* Used to store mouse co-ordinates of the first or */
75 gdouble y; /* previous events in a press-motion pair */
76 gdouble ex; /* Used to store mouse co-ordinates of the last */
77 gdouble ey; /* motion event in acceleration mode */
79 gboolean button_pressed;
80 guint32 last_time; /* Last event time, to stop infinite loops */
86 gdouble vmax_overshooting;
93 guint panning_threshold;
94 guint scrollbar_fade_delay;
97 guint direction_error_margin;
103 gint ix; /* Initial click mouse co-ordinates */
105 gint cx; /* Initial click child window mouse co-ordinates */
112 gint overshot_dist_x;
113 gint overshot_dist_y;
116 gdouble scroll_indicator_alpha;
117 gint motion_event_scroll_timeout;
118 gint scroll_indicator_timeout;
119 gint scroll_indicator_event_interrupt;
120 gint scroll_delay_counter;
123 gboolean initial_hint;
124 gboolean initial_effect;
125 gboolean low_friction_mode;
128 gboolean size_request_policy;
129 gboolean hscroll_visible;
130 gboolean vscroll_visible;
131 GdkRectangle hscroll_rect;
132 GdkRectangle vscroll_rect;
133 guint indicator_width;
135 GtkAdjustment *hadjust;
136 GtkAdjustment *vadjust;
140 GtkPolicyType vscrollbar_policy;
141 GtkPolicyType hscrollbar_policy;
143 GdkGC *scrollbars_gc;
144 GdkColor scroll_color;
146 gboolean center_on_child_focus;
147 gboolean center_on_child_focus_pending;
159 static guint pannable_area_signals [LAST_SIGNAL] = { 0 };
167 PROP_VEL_MAX_OVERSHOOTING,
168 PROP_VELOCITY_FAST_FACTOR,
172 PROP_PANNING_THRESHOLD,
173 PROP_SCROLLBAR_FADE_DELAY,
176 PROP_DIRECTION_ERROR_MARGIN,
177 PROP_VSCROLLBAR_POLICY,
178 PROP_HSCROLLBAR_POLICY,
183 PROP_LOW_FRICTION_MODE,
184 PROP_SIZE_REQUEST_POLICY,
187 PROP_CENTER_ON_CHILD_FOCUS,
191 static void hildon_pannable_area_class_init (HildonPannableAreaClass * klass);
192 static void hildon_pannable_area_init (HildonPannableArea * area);
193 static void hildon_pannable_area_get_property (GObject * object,
197 static void hildon_pannable_area_set_property (GObject * object,
199 const GValue * value,
201 static void hildon_pannable_area_dispose (GObject * object);
202 static void hildon_pannable_area_realize (GtkWidget * widget);
203 static void hildon_pannable_area_unrealize (GtkWidget * widget);
204 static void hildon_pannable_area_size_request (GtkWidget * widget,
205 GtkRequisition * requisition);
206 static void hildon_pannable_area_size_allocate (GtkWidget * widget,
207 GtkAllocation * allocation);
208 static void hildon_pannable_area_child_allocate_calculate (GtkWidget * widget,
209 GtkAllocation * allocation,
210 GtkAllocation * child_allocation);
211 static void hildon_pannable_area_style_set (GtkWidget * widget,
212 GtkStyle * previous_style);
213 static void hildon_pannable_area_map (GtkWidget * widget);
214 static void hildon_pannable_area_unmap (GtkWidget * widget);
215 static void hildon_pannable_area_grab_notify (GtkWidget *widget,
216 gboolean was_grabbed,
218 #if USE_CAIRO_SCROLLBARS == 1
219 static void rgb_from_gdkcolor (GdkColor *color, gdouble *r, gdouble *g, gdouble *b);
220 #else /* USE_CAIRO_SCROLLBARS */
221 static void tranparency_color (GdkColor *color,
224 gdouble transparency);
225 #endif /* USE_CAIRO_SCROLLBARS */
226 static void hildon_pannable_draw_vscroll (GtkWidget * widget,
227 GdkColor *back_color,
228 GdkColor *scroll_color);
229 static void hildon_pannable_draw_hscroll (GtkWidget * widget,
230 GdkColor *back_color,
231 GdkColor *scroll_color);
232 static void hildon_pannable_area_initial_effect (GtkWidget * widget);
233 static void hildon_pannable_area_redraw (HildonPannableArea * area);
234 static void hildon_pannable_area_launch_fade_timeout (HildonPannableArea * area,
236 static void hildon_pannable_area_adjust_value_changed (HildonPannableArea * area,
238 static void hildon_pannable_area_adjust_changed (HildonPannableArea * area,
240 static gboolean hildon_pannable_area_scroll_indicator_fade(HildonPannableArea * area);
241 static gboolean hildon_pannable_area_expose_event (GtkWidget * widget,
242 GdkEventExpose * event);
243 static GdkWindow * hildon_pannable_area_get_topmost (GdkWindow * window,
245 gint * tx, gint * ty,
247 static void synth_crossing (GdkWindow * child,
249 gint x_root, gint y_root,
250 guint32 time, gboolean in);
251 static gboolean hildon_pannable_area_button_press_cb (GtkWidget * widget,
252 GdkEventButton * event);
253 static void hildon_pannable_area_refresh (HildonPannableArea * area);
254 static gboolean hildon_pannable_area_check_scrollbars (HildonPannableArea * area);
255 static void hildon_pannable_axis_scroll (HildonPannableArea *area,
256 GtkAdjustment *adjust,
264 static void hildon_pannable_area_scroll (HildonPannableArea *area,
265 gdouble x, gdouble y);
266 static gboolean hildon_pannable_area_timeout (HildonPannableArea * area);
267 static void hildon_pannable_area_calculate_velocity (gdouble *vel,
271 gdouble drag_inertia,
274 static gboolean hildon_pannable_area_motion_event_scroll_timeout (HildonPannableArea *area);
275 static void hildon_pannable_area_motion_event_scroll (HildonPannableArea *area,
276 gdouble x, gdouble y);
277 static void hildon_pannable_area_check_move (HildonPannableArea *area,
278 GdkEventMotion * event,
281 static void hildon_pannable_area_handle_move (HildonPannableArea *area,
282 GdkEventMotion * event,
285 static gboolean hildon_pannable_area_motion_notify_cb (GtkWidget * widget,
286 GdkEventMotion * event);
287 static gboolean hildon_pannable_leave_notify_event (GtkWidget *widget,
288 GdkEventCrossing *event);
289 static gboolean hildon_pannable_area_button_release_cb (GtkWidget * widget,
290 GdkEventButton * event);
291 static gboolean hildon_pannable_area_scroll_cb (GtkWidget *widget,
292 GdkEventScroll *event);
293 static void hildon_pannable_area_child_mapped (GtkWidget *widget,
296 static void hildon_pannable_area_add (GtkContainer *container, GtkWidget *child);
297 static void hildon_pannable_area_remove (GtkContainer *container, GtkWidget *child);
298 static void hildon_pannable_calculate_vel_factor (HildonPannableArea * self);
299 static void hildon_pannable_area_set_focus_child (GtkContainer *container,
301 static void hildon_pannable_area_center_on_child_focus (HildonPannableArea *area);
305 hildon_pannable_area_class_init (HildonPannableAreaClass * klass)
307 GObjectClass *object_class = G_OBJECT_CLASS (klass);
308 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
309 GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
312 g_type_class_add_private (klass, sizeof (HildonPannableAreaPrivate));
314 object_class->get_property = hildon_pannable_area_get_property;
315 object_class->set_property = hildon_pannable_area_set_property;
316 object_class->dispose = hildon_pannable_area_dispose;
318 widget_class->realize = hildon_pannable_area_realize;
319 widget_class->unrealize = hildon_pannable_area_unrealize;
320 widget_class->map = hildon_pannable_area_map;
321 widget_class->unmap = hildon_pannable_area_unmap;
322 widget_class->size_request = hildon_pannable_area_size_request;
323 widget_class->size_allocate = hildon_pannable_area_size_allocate;
324 widget_class->expose_event = hildon_pannable_area_expose_event;
325 widget_class->style_set = hildon_pannable_area_style_set;
326 widget_class->button_press_event = hildon_pannable_area_button_press_cb;
327 widget_class->button_release_event = hildon_pannable_area_button_release_cb;
328 widget_class->motion_notify_event = hildon_pannable_area_motion_notify_cb;
329 widget_class->leave_notify_event = hildon_pannable_leave_notify_event;
330 widget_class->scroll_event = hildon_pannable_area_scroll_cb;
332 container_class->add = hildon_pannable_area_add;
333 container_class->remove = hildon_pannable_area_remove;
334 container_class->set_focus_child = hildon_pannable_area_set_focus_child;
336 klass->horizontal_movement = NULL;
337 klass->vertical_movement = NULL;
339 g_object_class_install_property (object_class,
341 g_param_spec_boolean ("enabled",
343 "Enable or disable finger-scroll.",
348 g_object_class_install_property (object_class,
349 PROP_VSCROLLBAR_POLICY,
350 g_param_spec_enum ("vscrollbar_policy",
352 "Visual policy of the vertical scrollbar",
353 GTK_TYPE_POLICY_TYPE,
354 GTK_POLICY_AUTOMATIC,
358 g_object_class_install_property (object_class,
359 PROP_HSCROLLBAR_POLICY,
360 g_param_spec_enum ("hscrollbar_policy",
362 "Visual policy of the horizontal scrollbar",
363 GTK_TYPE_POLICY_TYPE,
364 GTK_POLICY_AUTOMATIC,
368 g_object_class_install_property (object_class,
370 g_param_spec_enum ("mode",
372 "Change the finger-scrolling mode.",
373 HILDON_TYPE_PANNABLE_AREA_MODE,
374 HILDON_PANNABLE_AREA_MODE_AUTO,
378 g_object_class_install_property (object_class,
380 g_param_spec_flags ("mov_mode",
381 "Scroll movement mode",
382 "Controls if the widget can scroll vertically, horizontally or both",
383 HILDON_TYPE_MOVEMENT_MODE,
384 HILDON_MOVEMENT_MODE_VERT,
388 g_object_class_install_property (object_class,
390 g_param_spec_double ("velocity_min",
391 "Minimum scroll velocity",
392 "Minimum distance the child widget should scroll "
393 "per 'frame', in pixels per frame.",
398 g_object_class_install_property (object_class,
400 g_param_spec_double ("velocity_max",
401 "Maximum scroll velocity",
402 "Maximum distance the child widget should scroll "
403 "per 'frame', in pixels per frame.",
408 g_object_class_install_property (object_class,
409 PROP_VEL_MAX_OVERSHOOTING,
410 g_param_spec_double ("velocity_overshooting_max",
411 "Maximum scroll velocity when overshooting",
412 "Maximum distance the child widget should scroll "
413 "per 'frame', in pixels per frame when it overshoots after hitting the edge.",
418 g_object_class_install_property (object_class,
419 PROP_VELOCITY_FAST_FACTOR,
420 g_param_spec_double ("velocity_fast_factor",
421 "Fast velocity factor",
422 "Minimum velocity that is considered 'fast': "
423 "children widgets won't receive button presses. "
424 "Expressed as a fraction of the maximum velocity.",
429 g_object_class_install_property (object_class,
431 g_param_spec_double ("deceleration",
432 "Deceleration multiplier",
433 "The multiplier used when decelerating when in "
434 "acceleration scrolling mode.",
439 g_object_class_install_property (object_class,
441 g_param_spec_double ("drag_inertia",
442 "Inertia of the cursor dragging",
443 "Percentage of the calculated speed in each moment we are are going to use"
444 "to calculate the launch speed, the other part would be the speed"
445 "calculated previously",
450 g_object_class_install_property (object_class,
452 g_param_spec_uint ("sps",
453 "Scrolls per second",
454 "Amount of scroll events to generate per second.",
459 g_object_class_install_property (object_class,
460 PROP_PANNING_THRESHOLD,
461 g_param_spec_uint ("panning_threshold",
462 "Threshold to consider a motion event an scroll",
463 "Amount of pixels to consider a motion event an scroll, if it is less"
464 "it is a click detected incorrectly by the touch screen.",
469 g_object_class_install_property (object_class,
470 PROP_SCROLLBAR_FADE_DELAY,
471 g_param_spec_uint ("scrollbar_fade_delay",
472 "Time before starting to fade the scrollbar",
473 "Time the scrollbar is going to be visible if the widget is not in"
474 "action in miliseconds",
479 g_object_class_install_property (object_class,
481 g_param_spec_uint ("bounce_steps",
483 "Number of steps that is going to be used to bounce when hitting the"
484 "edge, the rubberband effect depends on it",
489 g_object_class_install_property (object_class,
491 g_param_spec_uint ("force",
492 "Multiplier of the calculated speed",
493 "Force applied to the movement, multiplies the calculated speed of the"
494 "user movement the cursor in the screen",
499 g_object_class_install_property (object_class,
500 PROP_DIRECTION_ERROR_MARGIN,
501 g_param_spec_uint ("direction_error_margin",
502 "Margin in the direction detection",
503 "After detecting the direction of the movement (horizontal or"
504 "vertical), we can add this margin of error to allow the movement in"
505 "the other direction even apparently it is not",
510 g_object_class_install_property (object_class,
512 g_param_spec_int ("vovershoot_max",
513 "Vertical overshoot distance",
514 "Space we allow the widget to pass over its vertical limits when"
515 "hitting the edges, set 0 in order to deactivate overshooting.",
520 g_object_class_install_property (object_class,
522 g_param_spec_int ("hovershoot_max",
523 "Horizontal overshoot distance",
524 "Space we allow the widget to pass over its horizontal limits when"
525 "hitting the edges, set 0 in order to deactivate overshooting.",
530 g_object_class_install_property (object_class,
532 g_param_spec_double ("scroll_time",
533 "Time to scroll to a position",
534 "The time to scroll to a position when calling the hildon_pannable_scroll_to function",
539 g_object_class_install_property (object_class,
541 g_param_spec_boolean ("initial-hint",
543 "Whether to hint the user about the pannability of the container.",
548 g_object_class_install_property (object_class,
549 PROP_LOW_FRICTION_MODE,
550 g_param_spec_boolean ("low-friction-mode",
551 "Do not decelerate the initial velocity",
552 "Avoid decelerating the panning movement, like no friction, the widget"
553 "will stop in the edges or if the user clicks.",
558 g_object_class_install_property (object_class,
559 PROP_SIZE_REQUEST_POLICY,
560 g_param_spec_enum ("size-request-policy",
561 "Size Requisition policy",
562 "Controls the size request policy of the widget",
563 HILDON_TYPE_SIZE_REQUEST_POLICY,
564 HILDON_SIZE_REQUEST_MINIMUM,
568 g_object_class_install_property (object_class,
570 g_param_spec_object ("hadjustment",
571 "Horizontal Adjustment",
572 "The GtkAdjustment for the horizontal position",
575 g_object_class_install_property (object_class,
577 g_param_spec_object ("vadjustment",
578 "Vertical Adjustment",
579 "The GtkAdjustment for the vertical position",
583 g_object_class_install_property (object_class,
584 PROP_CENTER_ON_CHILD_FOCUS,
585 g_param_spec_boolean ("center-on-child-focus",
586 "Center on the child with the focus",
587 "Whether to center the pannable on the child that receives the focus.",
593 gtk_widget_class_install_style_property (widget_class,
596 "Width of the scroll indicators",
597 "Pixel width used to draw the scroll indicators.",
602 * HildonPannableArea::horizontal-movement:
603 * @hildonpannable: the object which received the signal
604 * @direction: the direction of the movement #HILDON_MOVEMENT_LEFT or #HILDON_MOVEMENT_RIGHT
605 * @initial_x: the x coordinate of the point where the user clicked to start the movement
606 * @initial_y: the y coordinate of the point where the user clicked to start the movement
608 * The horizontal-movement signal is emitted when the pannable area
609 * detects a horizontal movement. The detection does not mean the
610 * widget is going to move (i.e. maybe the children are smaller
611 * horizontally than the screen).
615 pannable_area_signals[HORIZONTAL_MOVEMENT] =
616 g_signal_new ("horizontal_movement",
617 G_TYPE_FROM_CLASS (object_class),
618 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
619 G_STRUCT_OFFSET (HildonPannableAreaClass, horizontal_movement),
621 _hildon_marshal_VOID__INT_DOUBLE_DOUBLE,
628 * HildonPannableArea::vertical-movement:
629 * @hildonpannable: the object which received the signal
630 * @direction: the direction of the movement #HILDON_MOVEMENT_UP or #HILDON_MOVEMENT_DOWN
631 * @initial_x: the x coordinate of the point where the user clicked to start the movement
632 * @initial_y: the y coordinate of the point where the user clicked to start the movement
634 * The vertical-movement signal is emitted when the pannable area
635 * detects a vertical movement. The detection does not mean the
636 * widget is going to move (i.e. maybe the children are smaller
637 * vertically than the screen).
641 pannable_area_signals[VERTICAL_MOVEMENT] =
642 g_signal_new ("vertical_movement",
643 G_TYPE_FROM_CLASS (object_class),
644 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
645 G_STRUCT_OFFSET (HildonPannableAreaClass, vertical_movement),
647 _hildon_marshal_VOID__INT_DOUBLE_DOUBLE,
654 * HildonPannableArea::panning-started:
655 * @hildonpannable: the pannable area object that is going to start
658 * This signal is emitted before the panning starts. Applications
659 * can return %TRUE to avoid the panning. The main difference with
660 * the vertical-movement and horizontal-movement signals is those
661 * gesture signals are launched no matter if the widget is going to
662 * move, this signal means the widget is going to start moving. It
663 * could even happen that the widget moves and there was no gesture
664 * (i.e. click meanwhile the pannable is overshooting).
666 * Returns: %TRUE to stop the panning launch. %FALSE to continue
671 pannable_area_signals[PANNING_STARTED] =
672 g_signal_new ("panning-started",
673 G_TYPE_FROM_CLASS (object_class),
677 _hildon_marshal_BOOLEAN__VOID,
681 * HildonPannableArea::panning-finished:
682 * @hildonpannable: the pannable area object that finished the
685 * This signal is emitted after the kinetic panning has
690 pannable_area_signals[PANNING_FINISHED] =
691 g_signal_new ("panning-finished",
692 G_TYPE_FROM_CLASS (object_class),
696 _hildon_marshal_VOID__VOID,
702 hildon_pannable_area_init (HildonPannableArea * area)
704 HildonPannableAreaPrivate *priv = PANNABLE_AREA_PRIVATE (area);
706 GTK_WIDGET_UNSET_FLAGS (area, GTK_NO_WINDOW);
711 priv->button_pressed = FALSE;
714 priv->vscroll_visible = TRUE;
715 priv->hscroll_visible = TRUE;
716 priv->indicator_width = 6;
717 priv->overshot_dist_x = 0;
718 priv->overshot_dist_y = 0;
719 priv->overshooting_y = 0;
720 priv->overshooting_x = 0;
724 priv->scroll_indicator_alpha = 0.0;
725 priv->scroll_indicator_timeout = 0;
726 priv->motion_event_scroll_timeout = 0;
727 priv->scroll_indicator_event_interrupt = 0;
728 priv->scroll_delay_counter = 0;
729 priv->scrollbar_fade_delay = 0;
730 priv->scroll_to_x = -1;
731 priv->scroll_to_y = -1;
732 priv->first_drag = TRUE;
733 priv->initial_effect = TRUE;
734 priv->child_width = 0;
735 priv->child_height = 0;
736 priv->last_in = TRUE;
739 priv->center_on_child_focus_pending = FALSE;
741 gtk_style_lookup_color (GTK_WIDGET (area)->style,
742 "SecondaryTextColor", &priv->scroll_color);
745 GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
747 GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
749 g_object_ref_sink (G_OBJECT (priv->hadjust));
750 g_object_ref_sink (G_OBJECT (priv->vadjust));
752 g_signal_connect_swapped (priv->hadjust, "value-changed",
753 G_CALLBACK (hildon_pannable_area_adjust_value_changed), area);
754 g_signal_connect_swapped (priv->vadjust, "value-changed",
755 G_CALLBACK (hildon_pannable_area_adjust_value_changed), area);
756 g_signal_connect_swapped (priv->hadjust, "changed",
757 G_CALLBACK (hildon_pannable_area_adjust_changed), area);
758 g_signal_connect_swapped (priv->vadjust, "changed",
759 G_CALLBACK (hildon_pannable_area_adjust_changed), area);
760 g_signal_connect (area, "grab-notify",
761 G_CALLBACK (hildon_pannable_area_grab_notify), NULL);
765 hildon_pannable_area_get_property (GObject * object,
770 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (object)->priv;
772 switch (property_id) {
774 g_value_set_boolean (value, priv->enabled);
777 g_value_set_enum (value, priv->mode);
779 case PROP_MOVEMENT_MODE:
780 g_value_set_flags (value, priv->mov_mode);
782 case PROP_VELOCITY_MIN:
783 g_value_set_double (value, priv->vmin);
785 case PROP_VELOCITY_MAX:
786 g_value_set_double (value, priv->vmax);
788 case PROP_VEL_MAX_OVERSHOOTING:
789 g_value_set_double (value, priv->vmax_overshooting);
791 case PROP_VELOCITY_FAST_FACTOR:
792 g_value_set_double (value, priv->vfast_factor);
794 case PROP_DECELERATION:
795 g_value_set_double (value, priv->decel);
797 case PROP_DRAG_INERTIA:
798 g_value_set_double (value, priv->drag_inertia);
801 g_value_set_uint (value, priv->sps);
803 case PROP_PANNING_THRESHOLD:
804 g_value_set_uint (value, priv->panning_threshold);
806 case PROP_SCROLLBAR_FADE_DELAY:
807 /* convert to miliseconds */
808 g_value_set_uint (value, priv->scrollbar_fade_delay * SCROLL_FADE_TIMEOUT);
810 case PROP_BOUNCE_STEPS:
811 g_value_set_uint (value, priv->bounce_steps);
814 g_value_set_uint (value, priv->force);
816 case PROP_DIRECTION_ERROR_MARGIN:
817 g_value_set_uint (value, priv->direction_error_margin);
819 case PROP_VSCROLLBAR_POLICY:
820 g_value_set_enum (value, priv->vscrollbar_policy);
822 case PROP_HSCROLLBAR_POLICY:
823 g_value_set_enum (value, priv->hscrollbar_policy);
825 case PROP_VOVERSHOOT_MAX:
826 g_value_set_int (value, priv->vovershoot_max);
828 case PROP_HOVERSHOOT_MAX:
829 g_value_set_int (value, priv->hovershoot_max);
831 case PROP_SCROLL_TIME:
832 g_value_set_double (value, priv->scroll_time);
834 case PROP_INITIAL_HINT:
835 g_value_set_boolean (value, priv->initial_hint);
837 case PROP_LOW_FRICTION_MODE:
838 g_value_set_boolean (value, priv->low_friction_mode);
840 case PROP_SIZE_REQUEST_POLICY:
841 g_value_set_enum (value, priv->size_request_policy);
843 case PROP_HADJUSTMENT:
844 g_value_set_object (value,
845 hildon_pannable_area_get_hadjustment
846 (HILDON_PANNABLE_AREA (object)));
848 case PROP_VADJUSTMENT:
849 g_value_set_object (value,
850 hildon_pannable_area_get_vadjustment
851 (HILDON_PANNABLE_AREA (object)));
853 case PROP_CENTER_ON_CHILD_FOCUS:
854 g_value_set_boolean (value, priv->center_on_child_focus);
857 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
862 hildon_pannable_area_set_property (GObject * object,
864 const GValue * value,
867 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (object)->priv;
870 switch (property_id) {
872 enabled = g_value_get_boolean (value);
874 if ((priv->enabled != enabled) && (GTK_WIDGET_REALIZED (object))) {
876 gdk_window_raise (priv->event_window);
878 gdk_window_lower (priv->event_window);
881 priv->enabled = enabled;
884 priv->mode = g_value_get_enum (value);
886 case PROP_MOVEMENT_MODE:
887 priv->mov_mode = g_value_get_flags (value);
889 case PROP_VELOCITY_MIN:
890 priv->vmin = g_value_get_double (value);
892 case PROP_VELOCITY_MAX:
893 priv->vmax = g_value_get_double (value);
895 case PROP_VEL_MAX_OVERSHOOTING:
896 priv->vmax_overshooting = g_value_get_double (value);
898 case PROP_VELOCITY_FAST_FACTOR:
899 priv->vfast_factor = g_value_get_double (value);
901 case PROP_DECELERATION:
902 priv->decel = g_value_get_double (value);
903 hildon_pannable_calculate_vel_factor (HILDON_PANNABLE_AREA (object));
905 case PROP_DRAG_INERTIA:
906 priv->drag_inertia = g_value_get_double (value);
909 priv->sps = g_value_get_uint (value);
911 case PROP_PANNING_THRESHOLD:
913 GtkSettings *settings = gtk_settings_get_default ();
914 GtkSettingsValue svalue = { NULL, { 0, }, };
916 priv->panning_threshold = g_value_get_uint (value);
918 /* insure gtk dnd is the same we are using, not allowed
919 different thresholds in the same application */
920 svalue.origin = "panning_threshold";
921 g_value_init (&svalue.value, G_TYPE_LONG);
922 g_value_set_long (&svalue.value, priv->panning_threshold);
923 gtk_settings_set_property_value (settings, "gtk-dnd-drag-threshold", &svalue);
924 g_value_unset (&svalue.value);
927 case PROP_SCROLLBAR_FADE_DELAY:
928 /* convert to miliseconds */
929 priv->scrollbar_fade_delay = g_value_get_uint (value)/(SCROLL_FADE_TIMEOUT);
931 case PROP_BOUNCE_STEPS:
932 priv->bounce_steps = g_value_get_uint (value);
935 priv->force = g_value_get_uint (value);
937 case PROP_DIRECTION_ERROR_MARGIN:
938 priv->direction_error_margin = g_value_get_uint (value);
940 case PROP_VSCROLLBAR_POLICY:
941 priv->vscrollbar_policy = g_value_get_enum (value);
943 gtk_widget_queue_resize (GTK_WIDGET (object));
945 case PROP_HSCROLLBAR_POLICY:
946 priv->hscrollbar_policy = g_value_get_enum (value);
948 gtk_widget_queue_resize (GTK_WIDGET (object));
950 case PROP_VOVERSHOOT_MAX:
951 priv->vovershoot_max = g_value_get_int (value);
953 case PROP_HOVERSHOOT_MAX:
954 priv->hovershoot_max = g_value_get_int (value);
956 case PROP_SCROLL_TIME:
957 priv->scroll_time = g_value_get_double (value);
959 hildon_pannable_calculate_vel_factor (HILDON_PANNABLE_AREA (object));
961 case PROP_INITIAL_HINT:
962 priv->initial_hint = g_value_get_boolean (value);
964 case PROP_LOW_FRICTION_MODE:
965 priv->low_friction_mode = g_value_get_boolean (value);
967 case PROP_SIZE_REQUEST_POLICY:
968 hildon_pannable_area_set_size_request_policy (HILDON_PANNABLE_AREA (object),
969 g_value_get_enum (value));
971 case PROP_CENTER_ON_CHILD_FOCUS:
972 priv->center_on_child_focus = g_value_get_boolean (value);
976 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
981 hildon_pannable_area_dispose (GObject * object)
983 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (object)->priv;
984 GtkWidget *child = gtk_bin_get_child (GTK_BIN (object));
987 g_signal_emit (object, pannable_area_signals[PANNING_FINISHED], 0);
988 g_source_remove (priv->idle_id);
992 if (priv->scroll_indicator_timeout){
993 g_source_remove (priv->scroll_indicator_timeout);
994 priv->scroll_indicator_timeout = 0;
997 if (priv->motion_event_scroll_timeout){
998 g_source_remove (priv->motion_event_scroll_timeout);
999 priv->motion_event_scroll_timeout = 0;
1003 g_signal_handlers_disconnect_by_func (child,
1004 hildon_pannable_area_child_mapped,
1008 g_signal_handlers_disconnect_by_func (object,
1009 hildon_pannable_area_grab_notify,
1012 if (priv->hadjust) {
1013 g_signal_handlers_disconnect_by_func (priv->hadjust,
1014 hildon_pannable_area_adjust_value_changed,
1016 g_signal_handlers_disconnect_by_func (priv->hadjust,
1017 hildon_pannable_area_adjust_changed,
1019 g_object_unref (priv->hadjust);
1020 priv->hadjust = NULL;
1023 if (priv->vadjust) {
1024 g_signal_handlers_disconnect_by_func (priv->vadjust,
1025 hildon_pannable_area_adjust_value_changed,
1027 g_signal_handlers_disconnect_by_func (priv->vadjust,
1028 hildon_pannable_area_adjust_changed,
1030 g_object_unref (priv->vadjust);
1031 priv->vadjust = NULL;
1034 if (G_OBJECT_CLASS (hildon_pannable_area_parent_class)->dispose)
1035 G_OBJECT_CLASS (hildon_pannable_area_parent_class)->dispose (object);
1039 hildon_pannable_area_realize (GtkWidget * widget)
1041 GdkWindowAttr attributes;
1042 gint attributes_mask;
1044 HildonPannableAreaPrivate *priv;
1046 priv = HILDON_PANNABLE_AREA (widget)->priv;
1048 GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
1050 border_width = GTK_CONTAINER (widget)->border_width;
1052 attributes.x = widget->allocation.x + border_width;
1053 attributes.y = widget->allocation.y + border_width;
1054 attributes.width = MAX (widget->allocation.width - 2 * border_width, 0);
1055 attributes.height = MAX (widget->allocation.height - 2 * border_width, 0);
1056 attributes.window_type = GDK_WINDOW_CHILD;
1058 /* avoid using the hildon_window */
1059 attributes.visual = gtk_widget_get_visual (widget);
1060 attributes.colormap = gtk_widget_get_colormap (widget);
1061 attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK;
1062 attributes.wclass = GDK_INPUT_OUTPUT;
1064 attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1066 widget->window = gdk_window_new (gtk_widget_get_parent_window (widget),
1067 &attributes, attributes_mask);
1068 gdk_window_set_user_data (widget->window, widget);
1070 /* create the events window */
1073 attributes.event_mask = gtk_widget_get_events (widget)
1074 | GDK_BUTTON_MOTION_MASK
1075 | GDK_BUTTON_PRESS_MASK
1076 | GDK_BUTTON_RELEASE_MASK
1078 | GDK_POINTER_MOTION_HINT_MASK
1079 | GDK_EXPOSURE_MASK | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK;
1080 attributes.wclass = GDK_INPUT_ONLY;
1082 attributes_mask = GDK_WA_X | GDK_WA_Y;
1084 priv->event_window = gdk_window_new (widget->window,
1085 &attributes, attributes_mask);
1086 gdk_window_set_user_data (priv->event_window, widget);
1088 widget->style = gtk_style_attach (widget->style, widget->window);
1089 gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL);
1091 priv->scrollbars_gc = gdk_gc_new (GDK_DRAWABLE (widget->window));
1092 gdk_gc_copy (priv->scrollbars_gc, widget->style->fg_gc[GTK_STATE_INSENSITIVE]);
1096 hildon_pannable_area_unrealize (GtkWidget * widget)
1098 HildonPannableAreaPrivate *priv;
1100 priv = HILDON_PANNABLE_AREA (widget)->priv;
1102 if (priv->event_window != NULL) {
1103 gdk_window_set_user_data (priv->event_window, NULL);
1104 gdk_window_destroy (priv->event_window);
1105 priv->event_window = NULL;
1108 gdk_gc_unref (priv->scrollbars_gc);
1110 if (GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->unrealize)
1111 (*GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->unrealize)(widget);
1115 hildon_pannable_area_size_request (GtkWidget * widget,
1116 GtkRequisition * requisition)
1118 GtkRequisition child_requisition = {0};
1119 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1120 GtkWidget *child = gtk_bin_get_child (GTK_BIN (widget));
1122 if (child && GTK_WIDGET_VISIBLE (child))
1124 gtk_widget_size_request (child, &child_requisition);
1127 if (priv->hscrollbar_policy == GTK_POLICY_NEVER) {
1128 requisition->width = child_requisition.width;
1130 switch (priv->size_request_policy) {
1131 case HILDON_SIZE_REQUEST_CHILDREN:
1132 requisition->width = MIN (PANNABLE_MAX_WIDTH,
1133 child_requisition.width);
1135 case HILDON_SIZE_REQUEST_MINIMUM:
1137 requisition->width = priv->indicator_width;
1141 if (priv->vscrollbar_policy == GTK_POLICY_NEVER) {
1142 requisition->height = child_requisition.height;
1144 switch (priv->size_request_policy) {
1145 case HILDON_SIZE_REQUEST_CHILDREN:
1146 requisition->height = MIN (PANNABLE_MAX_HEIGHT,
1147 child_requisition.height);
1149 case HILDON_SIZE_REQUEST_MINIMUM:
1151 requisition->height = priv->indicator_width;
1155 requisition->width += 2 * GTK_CONTAINER (widget)->border_width;
1156 requisition->height += 2 * GTK_CONTAINER (widget)->border_width;
1160 hildon_pannable_area_child_allocate_calculate (GtkWidget * widget,
1161 GtkAllocation * allocation,
1162 GtkAllocation * child_allocation)
1165 HildonPannableAreaPrivate *priv;
1167 border_width = GTK_CONTAINER (widget)->border_width;
1169 priv = HILDON_PANNABLE_AREA (widget)->priv;
1171 child_allocation->x = 0;
1172 child_allocation->y = 0;
1173 child_allocation->width = MAX (allocation->width - 2 * border_width -
1174 (priv->vscroll_visible ? priv->vscroll_rect.width : 0), 0);
1175 child_allocation->height = MAX (allocation->height - 2 * border_width -
1176 (priv->hscroll_visible ? priv->hscroll_rect.height : 0), 0);
1178 if (priv->overshot_dist_y > 0) {
1179 child_allocation->y = MIN (child_allocation->y + priv->overshot_dist_y,
1180 child_allocation->height);
1181 child_allocation->height = MAX (child_allocation->height - priv->overshot_dist_y, 0);
1182 } else if (priv->overshot_dist_y < 0) {
1183 child_allocation->height = MAX (child_allocation->height + priv->overshot_dist_y, 0);
1186 if (priv->overshot_dist_x > 0) {
1187 child_allocation->x = MIN (child_allocation->x + priv->overshot_dist_x,
1188 child_allocation->width);
1189 child_allocation->width = MAX (child_allocation->width - priv->overshot_dist_x, 0);
1190 } else if (priv->overshot_dist_x < 0) {
1191 child_allocation->width = MAX (child_allocation->width + priv->overshot_dist_x, 0);
1196 hildon_pannable_area_size_allocate (GtkWidget * widget,
1197 GtkAllocation * allocation)
1199 GtkAllocation child_allocation;
1200 HildonPannableAreaPrivate *priv;
1201 GtkWidget *child = gtk_bin_get_child (GTK_BIN (widget));
1205 border_width = GTK_CONTAINER (widget)->border_width;
1207 widget->allocation = *allocation;
1209 priv = HILDON_PANNABLE_AREA (widget)->priv;
1211 if (GTK_WIDGET_REALIZED (widget)) {
1212 gdk_window_move_resize (widget->window,
1213 allocation->x + border_width,
1214 allocation->y + border_width,
1215 allocation->width - border_width * 2,
1216 allocation->height - border_width * 2);
1217 gdk_window_move_resize (priv->event_window,
1220 allocation->width - border_width * 2,
1221 allocation->height - border_width * 2);
1224 if (child && GTK_WIDGET_VISIBLE (child)) {
1226 hildon_pannable_area_child_allocate_calculate (widget,
1230 gtk_widget_size_allocate (child, &child_allocation);
1232 if (hildon_pannable_area_check_scrollbars (HILDON_PANNABLE_AREA (widget))) {
1233 hildon_pannable_area_child_allocate_calculate (widget,
1237 gtk_widget_size_allocate (child, &child_allocation);
1240 hv = priv->hadjust->value;
1241 vv = priv->vadjust->value;
1243 /* we have to do this after child size_allocate because page_size is
1244 * changed when we allocate the size of the children */
1245 if (priv->overshot_dist_y < 0) {
1246 priv->vadjust->value = priv->vadjust->upper - priv->vadjust->page_size;
1249 if (priv->overshot_dist_x < 0) {
1250 priv->hadjust->value = priv->hadjust->upper - priv->hadjust->page_size;
1253 if (hv != priv->hadjust->value)
1254 gtk_adjustment_value_changed (priv->hadjust);
1256 if (vv != priv->vadjust->value)
1257 gtk_adjustment_value_changed (priv->vadjust);
1260 hildon_pannable_area_check_scrollbars (HILDON_PANNABLE_AREA (widget));
1265 hildon_pannable_area_style_set (GtkWidget * widget,
1266 GtkStyle * previous_style)
1268 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1270 GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->
1271 style_set (widget, previous_style);
1273 gtk_style_lookup_color (widget->style, "SecondaryTextColor", &priv->scroll_color);
1274 gtk_widget_style_get (widget, "indicator-width", &priv->indicator_width, NULL);
1278 hildon_pannable_area_map (GtkWidget * widget)
1280 HildonPannableAreaPrivate *priv;
1282 priv = HILDON_PANNABLE_AREA (widget)->priv;
1284 gdk_window_show (widget->window);
1286 if (priv->event_window != NULL && !priv->enabled)
1287 gdk_window_show (priv->event_window);
1289 (*GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->map) (widget);
1291 if (priv->event_window != NULL && priv->enabled)
1292 gdk_window_show (priv->event_window);
1296 hildon_pannable_area_unmap (GtkWidget * widget)
1298 HildonPannableAreaPrivate *priv;
1300 priv = HILDON_PANNABLE_AREA (widget)->priv;
1302 if (priv->event_window != NULL)
1303 gdk_window_hide (priv->event_window);
1305 gdk_window_hide (widget->window);
1307 (*GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->unmap) (widget);
1311 hildon_pannable_area_grab_notify (GtkWidget *widget,
1312 gboolean was_grabbed,
1315 /* an internal widget has grabbed the focus and now has returned it,
1316 we have to do some release actions */
1318 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1320 priv->scroll_indicator_event_interrupt = 0;
1322 if ((!priv->scroll_indicator_timeout)&&(priv->scroll_indicator_alpha)>0.1) {
1323 priv->scroll_delay_counter = priv->scrollbar_fade_delay;
1325 hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget),
1326 priv->scroll_indicator_alpha);
1329 priv->last_type = 3;
1330 priv->moved = FALSE;
1334 #if USE_CAIRO_SCROLLBARS == 1
1337 rgb_from_gdkcolor (GdkColor *color, gdouble *r, gdouble *g, gdouble *b)
1339 *r = (color->red >> 8) / 255.0;
1340 *g = (color->green >> 8) / 255.0;
1341 *b = (color->blue >> 8) / 255.0;
1345 hildon_pannable_draw_vscroll (GtkWidget * widget,
1346 GdkColor *back_color,
1347 GdkColor *scroll_color)
1349 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1352 cairo_pattern_t *pattern;
1354 gint radius = (priv->vscroll_rect.width/2) - 1;
1356 cr = gdk_cairo_create(widget->window);
1358 /* Draw the background */
1359 rgb_from_gdkcolor (back_color, &r, &g, &b);
1360 cairo_set_source_rgb (cr, r, g, b);
1361 cairo_rectangle(cr, priv->vscroll_rect.x, priv->vscroll_rect.y,
1362 priv->vscroll_rect.width,
1363 priv->vscroll_rect.height);
1364 cairo_fill_preserve (cr);
1367 /* Calculate the scroll bar height and position */
1368 y = ((priv->vadjust->value - priv->vadjust->lower) / (priv->vadjust->upper - priv->vadjust->lower)) *
1369 (widget->allocation.height -
1370 (priv->hscroll_visible ? priv->indicator_width : 0));
1371 height = ((((priv->vadjust->value - priv->vadjust->lower) +
1372 priv->vadjust->page_size) /
1373 (priv->vadjust->upper - priv->vadjust->lower)) *
1374 (widget->allocation.height -
1375 (priv->hscroll_visible ? priv->indicator_width : 0))) - y;
1377 /* Set a minimum height */
1378 height = MAX (SCROLL_BAR_MIN_SIZE, height);
1380 /* Check the max y position */
1381 y = MIN (y, widget->allocation.height -
1382 (priv->hscroll_visible ? priv->hscroll_rect.height : 0) -
1385 /* Draw the scrollbar */
1386 rgb_from_gdkcolor (scroll_color, &r, &g, &b);
1388 pattern = cairo_pattern_create_linear(radius+1, y, radius+1,y + height);
1389 cairo_pattern_add_color_stop_rgb(pattern, 0, r, g, b);
1390 cairo_pattern_add_color_stop_rgb(pattern, 1, r/2, g/2, b/2);
1391 cairo_set_source(cr, pattern);
1393 cairo_pattern_destroy(pattern);
1395 cairo_arc(cr, priv->vscroll_rect.x + radius + 1, y + radius + 1, radius, G_PI, 0);
1396 cairo_line_to(cr, priv->vscroll_rect.x + (radius * 2) + 1, y + height - radius);
1397 cairo_arc(cr, priv->vscroll_rect.x + radius + 1, y + height - radius, radius, 0, G_PI);
1398 cairo_line_to(cr, priv->vscroll_rect.x + 1, y + height - radius);
1401 cairo_paint_with_alpha(cr, priv->scroll_indicator_alpha);
1407 hildon_pannable_draw_hscroll (GtkWidget * widget,
1408 GdkColor *back_color,
1409 GdkColor *scroll_color)
1411 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1414 cairo_pattern_t *pattern;
1416 gint radius = (priv->hscroll_rect.height/2) - 1;
1418 cr = gdk_cairo_create(widget->window);
1420 /* Draw the background */
1421 rgb_from_gdkcolor (back_color, &r, &g, &b);
1422 cairo_set_source_rgb (cr, r, g, b);
1423 cairo_rectangle(cr, priv->hscroll_rect.x, priv->hscroll_rect.y,
1424 priv->hscroll_rect.width,
1425 priv->hscroll_rect.height);
1426 cairo_fill_preserve (cr);
1429 /* calculate the scrollbar width and position */
1430 x = ((priv->hadjust->value - priv->hadjust->lower) / (priv->hadjust->upper - priv->hadjust->lower)) *
1431 (widget->allocation.width - (priv->vscroll_visible ? priv->indicator_width : 0));
1432 width =((((priv->hadjust->value - priv->hadjust->lower) +
1433 priv->hadjust->page_size) / (priv->hadjust->upper - priv->hadjust->lower)) *
1434 (widget->allocation.width -
1435 (priv->vscroll_visible ? priv->indicator_width : 0))) - x;
1437 /* Set a minimum width */
1438 width = MAX (SCROLL_BAR_MIN_SIZE, width);
1440 /* Check the max x position */
1441 x = MIN (x, widget->allocation.width -
1442 (priv->vscroll_visible ? priv->vscroll_rect.width : 0) -
1445 /* Draw the scrollbar */
1446 rgb_from_gdkcolor (scroll_color, &r, &g, &b);
1448 pattern = cairo_pattern_create_linear(x, radius+1, x+width, radius+1);
1449 cairo_pattern_add_color_stop_rgb(pattern, 0, r, g, b);
1450 cairo_pattern_add_color_stop_rgb(pattern, 1, r/2, g/2, b/2);
1451 cairo_set_source(cr, pattern);
1453 cairo_pattern_destroy(pattern);
1455 cairo_arc_negative(cr, x + radius + 1, priv->hscroll_rect.y + radius + 1, radius, 3*G_PI_2, G_PI_2);
1456 cairo_line_to(cr, x + width - radius, priv->hscroll_rect.y + (radius * 2) + 1);
1457 cairo_arc_negative(cr, x + width - radius, priv->hscroll_rect.y + radius + 1, radius, G_PI_2, 3*G_PI_2);
1458 cairo_line_to(cr, x + width - radius, priv->hscroll_rect.y + 1);
1461 cairo_paint_with_alpha(cr, priv->scroll_indicator_alpha);
1466 #else /* USE_CAIRO_SCROLLBARS */
1469 tranparency_color (GdkColor *color,
1472 gdouble transparency)
1476 diff = colora.red - colorb.red;
1477 color->red = colora.red-diff*transparency;
1479 diff = colora.green - colorb.green;
1480 color->green = colora.green-diff*transparency;
1482 diff = colora.blue - colorb.blue;
1483 color->blue = colora.blue-diff*transparency;
1487 hildon_pannable_draw_vscroll (GtkWidget *widget,
1488 GdkColor *back_color,
1489 GdkColor *scroll_color)
1491 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1493 GdkColor transp_color;
1494 GdkGC *gc = priv->scrollbars_gc;
1496 gdk_draw_rectangle (widget->window,
1497 widget->style->bg_gc[GTK_STATE_NORMAL],
1499 priv->vscroll_rect.x, priv->vscroll_rect.y,
1500 priv->vscroll_rect.width,
1501 priv->vscroll_rect.height);
1503 y = ((priv->vadjust->value - priv->vadjust->lower) / (priv->vadjust->upper - priv->vadjust->lower)) *
1504 (widget->allocation.height - (priv->hscroll_visible ? priv->indicator_width : 0));
1505 height = ((((priv->vadjust->value - priv->vadjust->lower) + priv->vadjust->page_size) /
1506 (priv->vadjust->upper - priv->vadjust->lower)) *
1507 (widget->allocation.height -
1508 (priv->hscroll_visible ? priv->indicator_width : 0))) - y;
1510 /* Set a minimum height */
1511 height = MAX (SCROLL_BAR_MIN_SIZE, height);
1513 /* Check the max y position */
1514 y = MIN (y, widget->allocation.height -
1515 (priv->hscroll_visible ? priv->hscroll_rect.height : 0) -
1518 if (priv->scroll_indicator_alpha == 1.0) {
1519 transp_color = priv->scroll_color;
1520 } else if (priv->scroll_indicator_alpha < 1.0) {
1521 tranparency_color (&transp_color, *back_color, *scroll_color,
1522 priv->scroll_indicator_alpha);
1524 gdk_gc_set_rgb_fg_color (gc, &transp_color);
1526 gdk_draw_rectangle (widget->window, gc,
1527 TRUE, priv->vscroll_rect.x, y,
1528 priv->vscroll_rect.width, height);
1532 hildon_pannable_draw_hscroll (GtkWidget *widget,
1533 GdkColor *back_color,
1534 GdkColor *scroll_color)
1536 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1538 GdkColor transp_color;
1539 GdkGC *gc = priv->scrollbars_gc;
1541 gdk_draw_rectangle (widget->window,
1542 widget->style->bg_gc[GTK_STATE_INSENSITIVE],
1544 priv->hscroll_rect.x, priv->hscroll_rect.y,
1545 priv->hscroll_rect.width,
1546 priv->hscroll_rect.height);
1548 /* calculate the scrollbar width and position */
1549 x = ((priv->hadjust->value - priv->hadjust->lower) / (priv->hadjust->upper - priv->hadjust->lower)) *
1550 (widget->allocation.width - (priv->vscroll_visible ? priv->indicator_width : 0));
1551 width =((((priv->hadjust->value - priv->hadjust->lower) +
1552 priv->hadjust->page_size) / (priv->hadjust->upper - priv->hadjust->lower)) *
1553 (widget->allocation.width -
1554 (priv->vscroll_visible ? priv->indicator_width : 0))) - x;
1556 /* Set a minimum width */
1557 width = MAX (SCROLL_BAR_MIN_SIZE, width);
1559 /* Check the max x position */
1560 x = MIN (x, widget->allocation.width -
1561 (priv->vscroll_visible ? priv->vscroll_rect.width : 0) -
1564 if (priv->scroll_indicator_alpha == 1.0) {
1565 transp_color = priv->scroll_color;
1566 } else if (priv->scroll_indicator_alpha < 1.0) {
1567 tranparency_color (&transp_color, *back_color, *scroll_color,
1568 priv->scroll_indicator_alpha);
1570 gdk_gc_set_rgb_fg_color (gc, &transp_color);
1572 gdk_draw_rectangle (widget->window, gc,
1573 TRUE, x, priv->hscroll_rect.y, width,
1574 priv->hscroll_rect.height);
1577 #endif /* USE_CAIRO_SCROLLBARS */
1580 hildon_pannable_area_initial_effect (GtkWidget * widget)
1582 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1584 if (priv->initial_hint) {
1585 if (priv->vscroll_visible || priv->hscroll_visible) {
1587 priv->scroll_indicator_event_interrupt = 0;
1588 priv->scroll_delay_counter = priv->scrollbar_fade_delay;
1590 hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget), 1.0);
1596 hildon_pannable_area_launch_fade_timeout (HildonPannableArea * area,
1599 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (area)->priv;
1601 priv->scroll_indicator_alpha = alpha;
1603 if (!priv->scroll_indicator_timeout)
1604 priv->scroll_indicator_timeout =
1605 gdk_threads_add_timeout (SCROLL_FADE_TIMEOUT,
1606 (GSourceFunc) hildon_pannable_area_scroll_indicator_fade,
1611 hildon_pannable_area_adjust_changed (HildonPannableArea * area,
1614 if (GTK_WIDGET_REALIZED (area))
1615 hildon_pannable_area_refresh (area);
1619 hildon_pannable_area_adjust_value_changed (HildonPannableArea * area,
1622 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (area)->priv;
1624 gint x = priv->x_offset;
1625 gint y = priv->y_offset;
1627 priv->x_offset = priv->hadjust->value;
1628 xdiff = x - priv->x_offset;
1629 priv->y_offset = priv->vadjust->value;
1630 ydiff = y - priv->y_offset;
1632 if ((xdiff || ydiff) && GTK_WIDGET_DRAWABLE (area)) {
1633 hildon_pannable_area_redraw (area);
1635 if ((priv->vscroll_visible) || (priv->hscroll_visible)) {
1636 priv->scroll_indicator_event_interrupt = 0;
1637 priv->scroll_delay_counter = priv->scrollbar_fade_delay;
1639 hildon_pannable_area_launch_fade_timeout (area, 1.0);
1645 hildon_pannable_area_redraw (HildonPannableArea * area)
1647 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (area)->priv;
1649 /* Redraw scroll indicators */
1650 if (GTK_WIDGET_DRAWABLE (area)) {
1651 if (priv->hscroll_visible) {
1652 gdk_window_invalidate_rect (GTK_WIDGET (area)->window,
1653 &priv->hscroll_rect, FALSE);
1656 if (priv->vscroll_visible) {
1657 gdk_window_invalidate_rect (GTK_WIDGET (area)->window,
1658 &priv->vscroll_rect, FALSE);
1664 hildon_pannable_area_scroll_indicator_fade(HildonPannableArea * area)
1666 HildonPannableAreaPrivate *priv = area->priv;
1668 /* if moving do not fade out */
1669 if (((ABS (priv->vel_y)>priv->vmin)||
1670 (ABS (priv->vel_x)>priv->vmin))&&(!priv->button_pressed)) {
1675 if (priv->scroll_indicator_event_interrupt) {
1676 /* Stop a fade out, and fade back in */
1677 if (priv->scroll_indicator_alpha > 0.9) {
1678 priv->scroll_indicator_alpha = 1.0;
1679 priv->scroll_indicator_timeout = 0;
1683 priv->scroll_indicator_alpha += 0.2;
1684 hildon_pannable_area_redraw (area);
1690 if ((priv->scroll_indicator_alpha > 0.9) &&
1691 (priv->scroll_delay_counter > 0)) {
1692 priv->scroll_delay_counter--;
1697 if (!priv->scroll_indicator_event_interrupt) {
1698 /* Continue fade out */
1699 if (priv->scroll_indicator_alpha < 0.1) {
1700 priv->scroll_indicator_timeout = 0;
1701 priv->scroll_indicator_alpha = 0.0;
1705 priv->scroll_indicator_alpha -= 0.2;
1706 hildon_pannable_area_redraw (area);
1716 hildon_pannable_area_expose_event (GtkWidget * widget,
1717 GdkEventExpose * event)
1720 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
1721 #if USE_CAIRO_SCROLLBARS == 1
1722 GdkColor back_color = widget->style->bg[GTK_STATE_NORMAL];
1723 GdkColor scroll_color = widget->style->base[GTK_STATE_SELECTED];
1724 #else /* USE_CAIRO_SCROLLBARS */
1725 GdkColor back_color = widget->style->bg[GTK_STATE_NORMAL];
1726 GdkColor scroll_color = priv->scroll_color;
1729 if (G_UNLIKELY (priv->initial_effect)) {
1730 hildon_pannable_area_initial_effect (widget);
1732 priv->initial_effect = FALSE;
1735 if (gtk_bin_get_child (GTK_BIN (widget))) {
1737 if (priv->scroll_indicator_alpha > 0.1) {
1738 if (priv->vscroll_visible) {
1739 hildon_pannable_draw_vscroll (widget, &back_color, &scroll_color);
1741 if (priv->hscroll_visible) {
1742 hildon_pannable_draw_hscroll (widget, &back_color, &scroll_color);
1746 /* draw overshooting rectangles */
1747 if (priv->overshot_dist_y > 0) {
1748 gint overshot_height;
1750 overshot_height = MIN (priv->overshot_dist_y, widget->allocation.height -
1751 (priv->hscroll_visible ? priv->hscroll_rect.height : 0));
1753 gdk_draw_rectangle (widget->window,
1754 widget->style->bg_gc[GTK_STATE_NORMAL],
1758 widget->allocation.width -
1759 (priv->vscroll_visible ? priv->vscroll_rect.width : 0),
1761 } else if (priv->overshot_dist_y < 0) {
1762 gint overshot_height;
1766 MAX (priv->overshot_dist_y,
1767 -(widget->allocation.height -
1768 (priv->hscroll_visible ? priv->hscroll_rect.height : 0)));
1770 overshot_y = MAX (widget->allocation.height +
1772 (priv->hscroll_visible ? priv->hscroll_rect.height : 0), 0);
1774 gdk_draw_rectangle (widget->window,
1775 widget->style->bg_gc[GTK_STATE_NORMAL],
1779 widget->allocation.width -
1780 priv->vscroll_rect.width,
1784 if (priv->overshot_dist_x > 0) {
1785 gint overshot_width;
1787 overshot_width = MIN (priv->overshot_dist_x, widget->allocation.width -
1788 (priv->vscroll_visible ? priv->vscroll_rect.width : 0));
1790 gdk_draw_rectangle (widget->window,
1791 widget->style->bg_gc[GTK_STATE_NORMAL],
1796 widget->allocation.height -
1797 (priv->hscroll_visible ? priv->hscroll_rect.height : 0));
1798 } else if (priv->overshot_dist_x < 0) {
1799 gint overshot_width;
1803 MAX (priv->overshot_dist_x,
1804 -(widget->allocation.width -
1805 (priv->vscroll_visible ? priv->vscroll_rect.width : 0)));
1807 overshot_x = MAX (widget->allocation.width +
1809 (priv->vscroll_visible ? priv->vscroll_rect.width : 0), 0);
1811 gdk_draw_rectangle (widget->window,
1812 widget->style->bg_gc[GTK_STATE_NORMAL],
1817 widget->allocation.height -
1818 priv->hscroll_rect.height);
1823 return GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->expose_event (widget, event);
1827 hildon_pannable_area_get_topmost (GdkWindow * window,
1829 gint * tx, gint * ty,
1832 /* Find the GdkWindow at the given point, by recursing from a given
1833 * parent GdkWindow. Optionally return the co-ordinates transformed
1834 * relative to the child window.
1837 GList *c, *children;
1838 GdkWindow *selected_window = NULL;
1840 gdk_drawable_get_size (GDK_DRAWABLE (window), &width, &height);
1841 if ((x < 0) || (x >= width) || (y < 0) || (y >= height))
1844 children = gdk_window_peek_children (window);
1851 selected_window = window;
1854 for (c = children; c; c = c->next) {
1855 GdkWindow *child = (GdkWindow *) c->data;
1858 gdk_drawable_get_size (GDK_DRAWABLE (child), &width, &height);
1859 gdk_window_get_position (child, &wx, &wy);
1861 if ((x >= wx) && (x < (wx + width)) && (y >= wy) && (y < (wy + height)) &&
1862 (gdk_window_is_visible (child))) {
1864 if (gdk_window_peek_children (child)) {
1865 selected_window = hildon_pannable_area_get_topmost (child, x-wx, y-wy,
1867 if (!selected_window) {
1872 selected_window = child;
1875 if ((gdk_window_get_events (child)&mask)) {
1880 selected_window = child;
1886 return selected_window;
1890 synth_crossing (GdkWindow * child,
1892 gint x_root, gint y_root,
1893 guint32 time, gboolean in)
1895 GdkEventCrossing *crossing_event;
1896 GdkEventType type = in ? GDK_ENTER_NOTIFY : GDK_LEAVE_NOTIFY;
1898 /* Send synthetic enter event */
1899 crossing_event = (GdkEventCrossing *) gdk_event_new (type);
1900 ((GdkEventAny *) crossing_event)->type = type;
1901 ((GdkEventAny *) crossing_event)->window = g_object_ref (child);
1902 ((GdkEventAny *) crossing_event)->send_event = FALSE;
1903 crossing_event->subwindow = g_object_ref (child);
1904 crossing_event->time = time;
1905 crossing_event->x = x;
1906 crossing_event->y = y;
1907 crossing_event->x_root = x_root;
1908 crossing_event->y_root = y_root;
1909 crossing_event->mode = GDK_CROSSING_NORMAL;
1910 crossing_event->detail = GDK_NOTIFY_UNKNOWN;
1911 crossing_event->focus = FALSE;
1912 crossing_event->state = 0;
1913 gdk_event_put ((GdkEvent *) crossing_event);
1914 gdk_event_free ((GdkEvent *) crossing_event);
1918 hildon_pannable_area_button_press_cb (GtkWidget * widget,
1919 GdkEventButton * event)
1922 HildonPannableArea *area = HILDON_PANNABLE_AREA (widget);
1923 HildonPannableAreaPrivate *priv = area->priv;
1925 if ((!priv->enabled) || (event->button != 1) ||
1926 ((event->time == priv->last_time) &&
1927 (priv->last_type == 1)) || (gtk_bin_get_child (GTK_BIN (widget)) == NULL))
1930 priv->scroll_indicator_event_interrupt = 1;
1932 hildon_pannable_area_launch_fade_timeout (area,
1933 priv->scroll_indicator_alpha);
1935 priv->last_time = event->time;
1936 priv->last_type = 1;
1938 priv->scroll_to_x = -1;
1939 priv->scroll_to_y = -1;
1941 if (priv->button_pressed && priv->child) {
1942 /* Widget stole focus on last click, send crossing-out event */
1943 synth_crossing (priv->child, 0, 0, event->x_root, event->y_root,
1944 event->time, FALSE);
1952 /* Don't allow a click if we're still moving fast */
1953 if ((ABS (priv->vel_x) <= (priv->vmax * priv->vfast_factor)) &&
1954 (ABS (priv->vel_y) <= (priv->vmax * priv->vfast_factor)))
1956 hildon_pannable_area_get_topmost (gtk_bin_get_child (GTK_BIN (widget))->window,
1957 event->x, event->y, &x, &y, GDK_BUTTON_PRESS_MASK);
1961 priv->button_pressed = TRUE;
1963 /* Stop scrolling on mouse-down (so you can flick, then hold to stop) */
1966 if (priv->idle_id) {
1967 g_source_remove (priv->idle_id);
1969 g_signal_emit (area, pannable_area_signals[PANNING_FINISHED], 0);
1974 gdk_drawable_get_size (priv->child, &priv->child_width,
1975 &priv->child_height);
1976 priv->last_in = TRUE;
1978 g_object_add_weak_pointer ((GObject *) priv->child,
1979 (gpointer) & priv->child);
1981 event = (GdkEventButton *) gdk_event_copy ((GdkEvent *) event);
1982 /* remove the reference we added with the copy */
1983 g_object_unref (priv->event_window);
1989 synth_crossing (priv->child, x, y, event->x_root,
1990 event->y_root, event->time, TRUE);
1992 /* Send synthetic click (button press/release) event */
1993 ((GdkEventAny *) event)->window = g_object_ref (priv->child);
1995 gdk_event_put ((GdkEvent *) event);
1996 gdk_event_free ((GdkEvent *) event);
2004 hildon_pannable_area_check_scrollbars (HildonPannableArea * area)
2006 HildonPannableAreaPrivate *priv = area->priv;
2007 gboolean prev_hscroll_visible, prev_vscroll_visible;
2009 prev_hscroll_visible = priv->hscroll_visible;
2010 prev_vscroll_visible = priv->vscroll_visible;
2012 if (!gtk_bin_get_child (GTK_BIN (area))) {
2013 priv->vscroll_visible = FALSE;
2014 priv->hscroll_visible = FALSE;
2016 switch (priv->hscrollbar_policy) {
2017 case GTK_POLICY_ALWAYS:
2018 priv->hscroll_visible = TRUE;
2020 case GTK_POLICY_NEVER:
2021 priv->hscroll_visible = FALSE;
2024 priv->hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
2025 priv->hadjust->page_size);
2028 switch (priv->vscrollbar_policy) {
2029 case GTK_POLICY_ALWAYS:
2030 priv->vscroll_visible = TRUE;
2032 case GTK_POLICY_NEVER:
2033 priv->vscroll_visible = FALSE;
2036 priv->vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
2037 priv->vadjust->page_size);
2040 /* Store the vscroll/hscroll areas for redrawing */
2041 if (priv->vscroll_visible) {
2042 GtkAllocation *allocation = >K_WIDGET (area)->allocation;
2043 priv->vscroll_rect.x = allocation->width - priv->indicator_width;
2044 priv->vscroll_rect.y = 0;
2045 priv->vscroll_rect.width = priv->indicator_width;
2046 priv->vscroll_rect.height = allocation->height -
2047 (priv->hscroll_visible ? priv->indicator_width : 0);
2049 if (priv->hscroll_visible) {
2050 GtkAllocation *allocation = >K_WIDGET (area)->allocation;
2051 priv->hscroll_rect.y = allocation->height - priv->indicator_width;
2052 priv->hscroll_rect.x = 0;
2053 priv->hscroll_rect.height = priv->indicator_width;
2054 priv->hscroll_rect.width = allocation->width -
2055 (priv->vscroll_visible ? priv->indicator_width : 0);
2059 return ((priv->hscroll_visible != prev_hscroll_visible) ||
2060 (priv->vscroll_visible != prev_vscroll_visible));
2064 hildon_pannable_area_refresh (HildonPannableArea * area)
2066 if (GTK_WIDGET_DRAWABLE (area) &&
2067 hildon_pannable_area_check_scrollbars (area)) {
2068 HildonPannableAreaPrivate *priv = area->priv;
2070 gtk_widget_queue_resize (GTK_WIDGET (area));
2072 if ((priv->vscroll_visible) || (priv->hscroll_visible)) {
2073 priv->scroll_indicator_event_interrupt = 0;
2074 priv->scroll_delay_counter = area->priv->scrollbar_fade_delay;
2076 hildon_pannable_area_launch_fade_timeout (area, 1.0);
2079 hildon_pannable_area_redraw (area);
2083 /* Scroll by a particular amount (in pixels). Optionally, return if
2084 * the scroll on a particular axis was successful.
2087 hildon_pannable_axis_scroll (HildonPannableArea *area,
2088 GtkAdjustment *adjust,
2092 gint *overshot_dist,
2098 HildonPannableAreaPrivate *priv = area->priv;
2100 dist = gtk_adjustment_get_value (adjust) - inc;
2103 * We use overshot_dist to define the distance of the current overshoot,
2104 * and overshooting to define the direction/whether or not we are overshot
2106 if (!(*overshooting)) {
2108 /* Initiation of the overshoot happens when the finger is released
2109 * and the current position of the pannable contents are out of range
2111 if (dist < adjust->lower) {
2114 dist = adjust->lower;
2116 if (overshoot_max!=0) {
2119 *overshot_dist = CLAMP (*overshot_dist + *vel, 0, overshoot_max);
2120 *vel = MIN (priv->vmax_overshooting, *vel);
2121 gtk_widget_queue_resize (GTK_WIDGET (area));
2125 } else if (dist > adjust->upper - adjust->page_size) {
2128 dist = adjust->upper - adjust->page_size;
2130 if (overshoot_max!=0) {
2133 *overshot_dist = CLAMP (*overshot_dist + *vel, -overshoot_max, 0);
2134 *vel = MAX (-priv->vmax_overshooting, *vel);
2135 gtk_widget_queue_resize (GTK_WIDGET (area));
2140 if ((*scroll_to) != -1) {
2141 if (((inc < 0)&&(*scroll_to <= dist))||
2142 ((inc > 0)&&(*scroll_to >= dist))) {
2150 adjust->value = dist;
2152 if (!priv->button_pressed) {
2154 /* When the overshoot has started we continue for
2155 * PROP_BOUNCE_STEPS more steps into the overshoot before we
2156 * reverse direction. The deceleration factor is calculated
2157 * based on the percentage distance from the first item with
2158 * each iteration, therefore always returning us to the
2159 * top/bottom most element
2161 if (*overshot_dist > 0) {
2163 if ((*overshooting < priv->bounce_steps) && (*vel > 0)) {
2165 *vel = (((gdouble)*overshot_dist)/overshoot_max) * (*vel);
2166 } else if ((*overshooting >= priv->bounce_steps) && (*vel > 0)) {
2168 } else if ((*overshooting > 1) && (*vel < 0)) {
2169 /* we add the MIN in order to avoid very small speeds */
2170 *vel = MIN (((((gdouble)*overshot_dist)*0.8) * -1), -10.0);
2173 *overshot_dist = CLAMP (*overshot_dist + *vel, 0, overshoot_max);
2175 gtk_widget_queue_resize (GTK_WIDGET (area));
2177 } else if (*overshot_dist < 0) {
2179 if ((*overshooting < priv->bounce_steps) && (*vel < 0)) {
2181 *vel = (((gdouble)*overshot_dist)/overshoot_max) * (*vel) * -1;
2182 } else if ((*overshooting >= priv->bounce_steps) && (*vel < 0)) {
2184 } else if ((*overshooting > 1) && (*vel > 0)) {
2185 /* we add the MAX in order to avoid very small speeds */
2186 *vel = MAX (((((gdouble)*overshot_dist)*0.8) * -1), 10.0);
2189 *overshot_dist = CLAMP (*overshot_dist + (*vel), -overshoot_max, 0);
2191 gtk_widget_queue_resize (GTK_WIDGET (area));
2196 gtk_widget_queue_resize (GTK_WIDGET (area));
2200 gint overshot_dist_old = *overshot_dist;
2202 if (*overshot_dist > 0) {
2203 *overshot_dist = CLAMP ((*overshot_dist) + inc, 0, overshoot_max);
2204 } else if (*overshot_dist < 0) {
2205 *overshot_dist = CLAMP ((*overshot_dist) + inc, -1 * overshoot_max, 0);
2208 adjust->value = CLAMP (dist,
2214 if (*overshot_dist != overshot_dist_old)
2215 gtk_widget_queue_resize (GTK_WIDGET (area));
2221 hildon_pannable_area_scroll (HildonPannableArea *area,
2222 gdouble x, gdouble y)
2225 HildonPannableAreaPrivate *priv = area->priv;
2226 gboolean hscroll_visible, vscroll_visible;
2229 if (gtk_bin_get_child (GTK_BIN (area)) == NULL)
2232 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
2233 priv->vadjust->page_size);
2234 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
2235 priv->hadjust->page_size);
2240 hv = priv->hadjust->value;
2241 vv = priv->vadjust->value;
2243 if (vscroll_visible) {
2244 hildon_pannable_axis_scroll (area, priv->vadjust, &priv->vel_y, y,
2245 &priv->overshooting_y, &priv->overshot_dist_y,
2246 &priv->scroll_to_y, priv->vovershoot_max, &sy);
2251 if (hscroll_visible) {
2252 hildon_pannable_axis_scroll (area, priv->hadjust, &priv->vel_x, x,
2253 &priv->overshooting_x, &priv->overshot_dist_x,
2254 &priv->scroll_to_x, priv->hovershoot_max, &sx);
2259 if (hv != priv->hadjust->value)
2260 gtk_adjustment_value_changed (priv->hadjust);
2262 if (vv != priv->vadjust->value)
2263 gtk_adjustment_value_changed (priv->vadjust);
2265 /* If the scroll on a particular axis wasn't succesful, reset the
2266 * initial scroll position to the new mouse co-ordinate. This means
2267 * when you get to the top of the page, dragging down works immediately.
2269 if (priv->mode == HILDON_PANNABLE_AREA_MODE_ACCEL) {
2281 hildon_pannable_area_timeout (HildonPannableArea * area)
2283 HildonPannableAreaPrivate *priv = area->priv;
2285 if ((!priv->enabled) || (priv->mode == HILDON_PANNABLE_AREA_MODE_PUSH)) {
2287 g_signal_emit (area, pannable_area_signals[PANNING_FINISHED], 0);
2292 if (!priv->button_pressed) {
2293 /* Decelerate gradually when pointer is raised */
2294 if ((!priv->overshot_dist_y) &&
2295 (!priv->overshot_dist_x)) {
2297 /* in case we move to a specific point do not decelerate when arriving */
2298 if ((priv->scroll_to_x != -1)||(priv->scroll_to_y != -1)) {
2300 if (ABS (priv->vel_x) >= 1.5) {
2301 priv->vel_x *= priv->decel;
2304 if (ABS (priv->vel_y) >= 1.5) {
2305 priv->vel_y *= priv->decel;
2309 if ((!priv->low_friction_mode) ||
2310 ((priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ) &&
2311 (ABS (priv->vel_x) < 0.8*priv->vmax)))
2312 priv->vel_x *= priv->decel;
2314 if ((!priv->low_friction_mode) ||
2315 ((priv->mov_mode&HILDON_MOVEMENT_MODE_VERT) &&
2316 (ABS (priv->vel_y) < 0.8*priv->vmax)))
2317 priv->vel_y *= priv->decel;
2319 if ((ABS (priv->vel_x) < 1.0) && (ABS (priv->vel_y) < 1.0)) {
2324 g_signal_emit (area, pannable_area_signals[PANNING_FINISHED], 0);
2330 } else if (priv->mode == HILDON_PANNABLE_AREA_MODE_AUTO) {
2336 hildon_pannable_area_scroll (area, priv->vel_x, priv->vel_y);
2338 gdk_window_process_updates (GTK_WIDGET (area)->window, FALSE);
2344 hildon_pannable_area_calculate_velocity (gdouble *vel,
2348 gdouble drag_inertia,
2354 if (ABS (dist) >= RATIO_TOLERANCE) {
2355 rawvel = (dist / ABS (delta)) * force;
2356 *vel = *vel * (1 - drag_inertia) +
2357 rawvel * drag_inertia;
2358 *vel = *vel > 0 ? MIN (*vel, vmax)
2359 : MAX (*vel, -1 * vmax);
2364 hildon_pannable_area_motion_event_scroll_timeout (HildonPannableArea *area)
2366 HildonPannableAreaPrivate *priv = area->priv;
2368 if ((priv->motion_x != 0)||(priv->motion_y != 0))
2369 hildon_pannable_area_scroll (area, priv->motion_x, priv->motion_y);
2371 priv->motion_event_scroll_timeout = 0;
2377 hildon_pannable_area_motion_event_scroll (HildonPannableArea *area,
2378 gdouble x, gdouble y)
2380 HildonPannableAreaPrivate *priv = area->priv;
2382 if (priv->motion_event_scroll_timeout) {
2384 priv->motion_x += x;
2385 priv->motion_y += y;
2389 /* we do not delay the first event but the next ones */
2390 hildon_pannable_area_scroll (area, x, y);
2395 priv->motion_event_scroll_timeout = gdk_threads_add_timeout
2396 ((gint) (1000.0 / (gdouble) MOTION_EVENTS_PER_SECOND),
2397 (GSourceFunc) hildon_pannable_area_motion_event_scroll_timeout, area);
2402 hildon_pannable_area_check_move (HildonPannableArea *area,
2403 GdkEventMotion * event,
2407 HildonPannableAreaPrivate *priv = area->priv;
2409 if (priv->first_drag && (!priv->moved) &&
2410 ((ABS (*x) > (priv->panning_threshold))
2411 || (ABS (*y) > (priv->panning_threshold)))) {
2416 if (priv->first_drag) {
2417 gboolean vscroll_visible;
2418 gboolean hscroll_visible;
2420 if (ABS (priv->iy - event->y) >=
2421 ABS (priv->ix - event->x)) {
2423 g_signal_emit (area,
2424 pannable_area_signals[VERTICAL_MOVEMENT],
2425 0, (priv->iy > event->y) ?
2426 HILDON_MOVEMENT_UP :
2427 HILDON_MOVEMENT_DOWN,
2428 (gdouble)priv->ix, (gdouble)priv->iy);
2430 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
2431 priv->vadjust->page_size);
2433 if (!((vscroll_visible)&&
2434 (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT))) {
2436 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
2437 priv->hadjust->page_size);
2439 /* even in case we do not have to move we check if this
2440 could be a fake horizontal movement */
2441 if (!((hscroll_visible)&&
2442 (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ)) ||
2443 (ABS (priv->iy - event->y) -
2444 ABS (priv->ix - event->x) >= priv->direction_error_margin))
2445 priv->moved = FALSE;
2449 g_signal_emit (area,
2450 pannable_area_signals[HORIZONTAL_MOVEMENT],
2451 0, (priv->ix > event->x) ?
2452 HILDON_MOVEMENT_LEFT :
2453 HILDON_MOVEMENT_RIGHT,
2454 (gdouble)priv->ix, (gdouble)priv->iy);
2456 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
2457 priv->hadjust->page_size);
2459 if (!((hscroll_visible)&&
2460 (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ))) {
2462 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
2463 priv->vadjust->page_size);
2465 /* even in case we do not have to move we check if this
2466 could be a fake vertical movement */
2467 if (!((vscroll_visible) &&
2468 (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT)) ||
2469 (ABS (priv->ix - event->x) -
2470 ABS (priv->iy - event->y) >= priv->direction_error_margin))
2471 priv->moved = FALSE;
2475 if ((priv->moved)&&(priv->child)) {
2478 pos_x = priv->cx + (event->x - priv->ix);
2479 pos_y = priv->cy + (event->y - priv->iy);
2481 synth_crossing (priv->child, pos_x, pos_y, event->x_root,
2482 event->y_root, event->time, FALSE);
2486 gboolean result_val;
2488 g_signal_emit (area,
2489 pannable_area_signals[PANNING_STARTED],
2492 priv->moved = !result_val;
2496 priv->first_drag = FALSE;
2498 if ((priv->mode != HILDON_PANNABLE_AREA_MODE_PUSH) &&
2499 (priv->mode != HILDON_PANNABLE_AREA_MODE_AUTO)) {
2502 priv->idle_id = gdk_threads_add_timeout ((gint)
2503 (1000.0 / (gdouble) priv->sps),
2505 hildon_pannable_area_timeout, area);
2511 hildon_pannable_area_handle_move (HildonPannableArea *area,
2512 GdkEventMotion * event,
2516 HildonPannableAreaPrivate *priv = area->priv;
2519 switch (priv->mode) {
2520 case HILDON_PANNABLE_AREA_MODE_PUSH:
2521 /* Scroll by the amount of pixels the cursor has moved
2522 * since the last motion event.
2524 hildon_pannable_area_motion_event_scroll (area, *x, *y);
2528 case HILDON_PANNABLE_AREA_MODE_ACCEL:
2529 /* Set acceleration relative to the initial click */
2530 priv->ex = event->x;
2531 priv->ey = event->y;
2532 priv->vel_x = ((*x > 0) ? 1 : -1) *
2534 (gdouble) GTK_WIDGET (area)->allocation.width) *
2535 (priv->vmax - priv->vmin)) + priv->vmin);
2536 priv->vel_y = ((*y > 0) ? 1 : -1) *
2538 (gdouble) GTK_WIDGET (area)->allocation.height) *
2539 (priv->vmax - priv->vmin)) + priv->vmin);
2541 case HILDON_PANNABLE_AREA_MODE_AUTO:
2543 delta = event->time - priv->last_time;
2545 if (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT) {
2546 gdouble dist = event->y - priv->y;
2548 hildon_pannable_area_calculate_velocity (&priv->vel_y,
2560 if (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ) {
2561 gdouble dist = event->x - priv->x;
2563 hildon_pannable_area_calculate_velocity (&priv->vel_x,
2575 hildon_pannable_area_motion_event_scroll (area, *x, *y);
2577 if (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ)
2579 if (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT)
2589 hildon_pannable_area_motion_notify_cb (GtkWidget * widget,
2590 GdkEventMotion * event)
2592 HildonPannableArea *area = HILDON_PANNABLE_AREA (widget);
2593 HildonPannableAreaPrivate *priv = area->priv;
2596 if (gtk_bin_get_child (GTK_BIN (widget)) == NULL)
2599 if ((!priv->enabled) || (!priv->button_pressed) ||
2600 ((event->time == priv->last_time) && (priv->last_type == 2))) {
2601 gdk_window_get_pointer (widget->window, NULL, NULL, 0);
2605 if (priv->last_type == 1) {
2606 priv->first_drag = TRUE;
2609 x = event->x - priv->x;
2610 y = event->y - priv->y;
2613 hildon_pannable_area_check_move (area, event, &x, &y);
2617 hildon_pannable_area_handle_move (area, event, &x, &y);
2618 } else if (priv->child) {
2622 pos_x = priv->cx + (event->x - priv->ix);
2623 pos_y = priv->cy + (event->y - priv->iy);
2625 in = (((0 <= pos_x)&&(priv->child_width >= pos_x)) &&
2626 ((0 <= pos_y)&&(priv->child_height >= pos_y)));
2628 if (((!priv->last_in)&&in)||((priv->last_in)&&(!in))) {
2630 synth_crossing (priv->child, pos_x, pos_y, event->x_root,
2631 event->y_root, event->time, in);
2637 priv->last_time = event->time;
2638 priv->last_type = 2;
2641 /* Send motion notify to child */
2642 event = (GdkEventMotion *) gdk_event_copy ((GdkEvent *) event);
2643 /* remove the reference we added with the copy */
2644 g_object_unref (priv->event_window);
2645 event->x = priv->cx + (event->x - priv->ix);
2646 event->y = priv->cy + (event->y - priv->iy);
2647 event->window = g_object_ref (priv->child);
2648 gdk_event_put ((GdkEvent *) event);
2649 gdk_event_free ((GdkEvent *) event);
2652 gdk_window_get_pointer (widget->window, NULL, NULL, 0);
2658 hildon_pannable_leave_notify_event (GtkWidget *widget,
2659 GdkEventCrossing *event)
2661 HildonPannableArea *area = HILDON_PANNABLE_AREA (widget);
2662 HildonPannableAreaPrivate *priv = area->priv;
2664 if ((priv->child)&&(priv->last_in)) {
2665 priv->last_in = FALSE;
2667 synth_crossing (priv->child, 0, 0, event->x_root,
2668 event->y_root, event->time, FALSE);
2675 hildon_pannable_area_button_release_cb (GtkWidget * widget,
2676 GdkEventButton * event)
2678 HildonPannableArea *area = HILDON_PANNABLE_AREA (widget);
2679 HildonPannableAreaPrivate *priv = area->priv;
2684 if (((event->time == priv->last_time) && (priv->last_type == 3))
2685 || (gtk_bin_get_child (GTK_BIN (widget)) == NULL)
2686 || (!priv->button_pressed) || (!priv->enabled) || (event->button != 1))
2689 /* if last event was a motion-notify we have to check the movement
2690 and launch the animation */
2691 if (priv->last_type == 2) {
2693 dx = event->x - priv->x;
2694 dy = event->y - priv->y;
2696 hildon_pannable_area_check_move (area, (GdkEventMotion *) event, &dx, &dy);
2699 gdouble delta = event->time - priv->last_time;
2701 hildon_pannable_area_handle_move (area, (GdkEventMotion *) event, &dx, &dy);
2703 /* move all the way to the last position now */
2704 if (priv->motion_event_scroll_timeout) {
2705 g_source_remove (priv->motion_event_scroll_timeout);
2706 hildon_pannable_area_motion_event_scroll_timeout (HILDON_PANNABLE_AREA (widget));
2711 if ((ABS (dx) < 4.0) && (delta >= CURSOR_STOPPED_TIMEOUT))
2714 if ((ABS (dy) < 4.0) && (delta >= CURSOR_STOPPED_TIMEOUT))
2719 /* If overshoot has been initiated with a finger down, on release set max speed */
2720 if (priv->overshot_dist_y != 0) {
2721 priv->overshooting_y = priv->bounce_steps; /* Hack to stop a bounce in the finger down case */
2722 priv->vel_y = priv->overshot_dist_y * 0.9;
2725 if (priv->overshot_dist_x != 0) {
2726 priv->overshooting_x = priv->bounce_steps; /* Hack to stop a bounce in the finger down case */
2727 priv->vel_x = priv->overshot_dist_x * 0.9;
2730 priv->button_pressed = FALSE;
2732 if ((ABS (priv->vel_y) >= priv->vmin) ||
2733 (ABS (priv->vel_x) >= priv->vmin)) {
2735 /* we have to move because we are in overshooting position*/
2737 gboolean result_val;
2739 g_signal_emit (area,
2740 pannable_area_signals[PANNING_STARTED],
2744 priv->scroll_indicator_alpha = 1.0;
2746 if (ABS (priv->vel_x) > MAX_SPEED_THRESHOLD)
2747 priv->vel_x = (priv->vel_x > 0) ? priv->vmax : -priv->vmax;
2749 if (ABS (priv->vel_y) > MAX_SPEED_THRESHOLD)
2750 priv->vel_y = (priv->vel_y > 0) ? priv->vmax : -priv->vmax;
2753 priv->idle_id = gdk_threads_add_timeout ((gint) (1000.0 / (gdouble) priv->sps),
2755 hildon_pannable_area_timeout, widget);
2757 if (priv->center_on_child_focus_pending) {
2758 hildon_pannable_area_center_on_child_focus (area);
2762 g_signal_emit (widget, pannable_area_signals[PANNING_FINISHED], 0);
2765 area->priv->center_on_child_focus_pending = FALSE;
2767 priv->scroll_indicator_event_interrupt = 0;
2768 priv->scroll_delay_counter = priv->scrollbar_fade_delay;
2770 hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget),
2771 priv->scroll_indicator_alpha);
2773 priv->last_time = event->time;
2774 priv->last_type = 3;
2777 priv->moved = FALSE;
2782 hildon_pannable_area_get_topmost (gtk_bin_get_child (GTK_BIN (widget))->window,
2783 event->x, event->y, &x, &y, GDK_BUTTON_RELEASE_MASK);
2785 event = (GdkEventButton *) gdk_event_copy ((GdkEvent *) event);
2786 /* remove the reference we added with the copy */
2787 g_object_unref (priv->event_window);
2791 /* Leave the widget if we've moved - This doesn't break selection,
2792 * but stops buttons from being clicked.
2794 if ((child != priv->child) || (priv->moved)) {
2795 /* Send synthetic leave event */
2796 synth_crossing (priv->child, x, y, event->x_root,
2797 event->y_root, event->time, FALSE);
2798 /* insure no click will happen for widgets that do not handle
2802 /* Send synthetic button release event */
2803 ((GdkEventAny *) event)->window = g_object_ref (priv->child);
2804 gdk_event_put ((GdkEvent *) event);
2806 /* Send synthetic button release event */
2807 ((GdkEventAny *) event)->window = g_object_ref (child);
2808 gdk_event_put ((GdkEvent *) event);
2809 /* Send synthetic leave event */
2810 synth_crossing (priv->child, x, y, event->x_root,
2811 event->y_root, event->time, FALSE);
2813 g_object_remove_weak_pointer ((GObject *) priv->child,
2814 (gpointer) & priv->child);
2816 priv->moved = FALSE;
2817 gdk_event_free ((GdkEvent *) event);
2822 /* utility event handler */
2824 hildon_pannable_area_scroll_cb (GtkWidget *widget,
2825 GdkEventScroll *event)
2827 GtkAdjustment *adj = NULL;
2828 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
2830 if ((!priv->enabled) ||
2831 (gtk_bin_get_child (GTK_BIN (widget)) == NULL))
2834 priv->scroll_indicator_event_interrupt = 0;
2835 priv->scroll_delay_counter = priv->scrollbar_fade_delay + 20;
2837 hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget), 1.0);
2839 /* Stop inertial scrolling */
2840 if (priv->idle_id) {
2843 priv->overshooting_x = 0;
2844 priv->overshooting_y = 0;
2846 if ((priv->overshot_dist_x>0)||(priv->overshot_dist_y>0)) {
2847 priv->overshot_dist_x = 0;
2848 priv->overshot_dist_y = 0;
2850 gtk_widget_queue_resize (GTK_WIDGET (widget));
2853 g_signal_emit (widget, pannable_area_signals[PANNING_FINISHED], 0);
2855 g_source_remove (priv->idle_id);
2859 if (event->direction == GDK_SCROLL_UP || event->direction == GDK_SCROLL_DOWN)
2860 adj = priv->vadjust;
2862 adj = priv->hadjust;
2866 gdouble delta, new_value;
2868 /* from gtkrange.c calculate delta*/
2869 delta = pow (adj->page_size, 2.0 / 3.0);
2871 if (event->direction == GDK_SCROLL_UP ||
2872 event->direction == GDK_SCROLL_LEFT)
2875 new_value = CLAMP (adj->value + delta, adj->lower, adj->upper - adj->page_size);
2877 gtk_adjustment_set_value (adj, new_value);
2884 hildon_pannable_area_child_mapped (GtkWidget *widget,
2888 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (user_data)->priv;
2890 if (priv->event_window != NULL && priv->enabled)
2891 gdk_window_raise (priv->event_window);
2895 hildon_pannable_area_add (GtkContainer *container, GtkWidget *child)
2897 HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (container)->priv;
2899 g_return_if_fail (gtk_bin_get_child (GTK_BIN (container)) == NULL);
2901 gtk_widget_set_parent (child, GTK_WIDGET (container));
2902 GTK_BIN (container)->child = child;
2904 g_signal_connect_after (child, "map-event",
2905 G_CALLBACK (hildon_pannable_area_child_mapped),
2908 if (!gtk_widget_set_scroll_adjustments (child, priv->hadjust, priv->vadjust)) {
2909 g_warning ("%s: cannot add non scrollable widget, "
2910 "wrap it in a viewport", __FUNCTION__);
2914 /* call this function if you are not panning */
2916 hildon_pannable_area_center_on_child_focus (HildonPannableArea *area)
2918 GtkWidget *focused_child = NULL;
2919 GtkWidget *window = NULL;
2921 window = gtk_widget_get_toplevel (GTK_WIDGET (area));
2923 if (GTK_WIDGET_TOPLEVEL (window)) {
2924 focused_child = gtk_window_get_focus (GTK_WINDOW (window));
2927 if (focused_child) {
2928 hildon_pannable_area_scroll_to_child (area, focused_child);
2933 hildon_pannable_area_set_focus_child (GtkContainer *container,
2936 HildonPannableArea *area = HILDON_PANNABLE_AREA (container);
2938 if (!area->priv->center_on_child_focus) {
2942 if (GTK_IS_WIDGET (child)) {
2943 area->priv->center_on_child_focus_pending = TRUE;
2948 hildon_pannable_area_remove (GtkContainer *container, GtkWidget *child)
2950 g_return_if_fail (HILDON_IS_PANNABLE_AREA (container));
2951 g_return_if_fail (child != NULL);
2952 g_return_if_fail (gtk_bin_get_child (GTK_BIN (container)) == child);
2954 gtk_widget_set_scroll_adjustments (child, NULL, NULL);
2956 g_signal_handlers_disconnect_by_func (child,
2957 hildon_pannable_area_child_mapped,
2960 /* chain parent class handler to remove child */
2961 GTK_CONTAINER_CLASS (hildon_pannable_area_parent_class)->remove (container, child);
2965 * This method calculates a factor necessary to determine the initial distance
2966 * to jump in hildon_pannable_area_scroll_to(). For fixed time and frames per
2967 * second, we know in how many frames 'n' we need to reach the destination
2968 * point. We know that, for a distance d,
2970 * d = d_0 + d_1 + ... + d_n
2972 * where d_i is the distance travelled in the i-th frame and decel_factor is
2973 * the deceleration factor. This can be rewritten as
2975 * d = d_0 + (d_0 * decel_factor) + ... + (d_n-1 * decel_factor),
2977 * since the distance travelled on each frame is the distance travelled in the
2978 * previous frame reduced by the deceleration factor. Reducing this and
2979 * factoring d_0 out, we get
2981 * d = d_0 (1 + decel_factor + ... + decel_factor^(n-1)).
2983 * Since the sum is independent of the distance to be travelled, we can define
2986 * vel_factor = 1 + decel_factor + ... + decel_factor^(n-1).
2988 * That's the gem we calculate in this method.
2991 hildon_pannable_calculate_vel_factor (HildonPannableArea * self)
2993 HildonPannableAreaPrivate *priv = self->priv;
2998 n = ceil (priv->sps * priv->scroll_time);
3000 for (i = 1; i < n && fct_i >= RATIO_TOLERANCE; i++) {
3001 fct_i *= priv->decel;
3005 priv->vel_factor = fct;
3009 * hildon_pannable_area_new:
3011 * Create a new pannable area widget
3013 * Returns: the newly created #HildonPannableArea
3019 hildon_pannable_area_new (void)
3021 return g_object_new (HILDON_TYPE_PANNABLE_AREA, NULL);
3025 * hildon_pannable_area_new_full:
3026 * @mode: #HildonPannableAreaMode
3027 * @enabled: Value for the enabled property
3028 * @vel_min: Value for the velocity-min property
3029 * @vel_max: Value for the velocity-max property
3030 * @decel: Value for the deceleration property
3031 * @sps: Value for the sps property
3033 * Create a new #HildonPannableArea widget and set various properties
3035 * returns: the newly create #HildonPannableArea
3041 hildon_pannable_area_new_full (gint mode, gboolean enabled,
3042 gdouble vel_min, gdouble vel_max,
3043 gdouble decel, guint sps)
3045 return g_object_new (HILDON_TYPE_PANNABLE_AREA,
3048 "velocity_min", vel_min,
3049 "velocity_max", vel_max,
3050 "deceleration", decel, "sps", sps, NULL);
3054 * hildon_pannable_area_add_with_viewport:
3055 * @area: A #HildonPannableArea
3056 * @child: Child widget to add to the viewport
3058 * Convenience function used to add a child to a #GtkViewport, and add the
3059 * viewport to the scrolled window.
3065 hildon_pannable_area_add_with_viewport (HildonPannableArea * area,
3069 GtkWidget *viewport;
3071 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3072 g_return_if_fail (GTK_IS_WIDGET (child));
3073 g_return_if_fail (child->parent == NULL);
3075 bin = GTK_BIN (area);
3077 if (bin->child != NULL)
3079 g_return_if_fail (GTK_IS_VIEWPORT (bin->child));
3080 g_return_if_fail (GTK_BIN (bin->child)->child == NULL);
3082 viewport = bin->child;
3086 HildonPannableAreaPrivate *priv = area->priv;
3088 viewport = gtk_viewport_new (priv->hadjust,
3090 gtk_viewport_set_shadow_type (GTK_VIEWPORT (viewport), GTK_SHADOW_NONE);
3091 gtk_container_add (GTK_CONTAINER (area), viewport);
3094 gtk_widget_show (viewport);
3095 gtk_container_add (GTK_CONTAINER (viewport), child);
3099 * hildon_pannable_area_scroll_to:
3100 * @area: A #HildonPannableArea.
3101 * @x: The x coordinate of the destination point or -1 to ignore this axis.
3102 * @y: The y coordinate of the destination point or -1 to ignore this axis.
3104 * Smoothly scrolls @area to ensure that (@x, @y) is a visible point
3105 * on the widget. To move in only one coordinate, you must set the other one
3106 * to -1. Notice that, in %HILDON_PANNABLE_AREA_MODE_PUSH mode, this function
3107 * works just like hildon_pannable_area_jump_to().
3109 * This function is useful if you need to present the user with a particular
3110 * element inside a scrollable widget, like #GtkTreeView. For instance,
3111 * the following example shows how to scroll inside a #GtkTreeView to
3112 * make visible an item, indicated by the #GtkTreeIter @iter.
3116 * GtkTreePath *path;
3117 * GdkRectangle *rect;
3119 * path = gtk_tree_model_get_path (model, &iter);
3120 * gtk_tree_view_get_background_area (GTK_TREE_VIEW (treeview),
3121 * path, NULL, &rect);
3122 * gtk_tree_view_convert_bin_window_to_tree_coords (GTK_TREE_VIEW (treeview),
3123 * 0, rect.y, NULL, &y);
3124 * hildon_pannable_area_scroll_to (panarea, -1, y);
3125 * gtk_tree_path_free (path);
3129 * If you want to present a child widget in simpler scenarios,
3130 * use hildon_pannable_area_scroll_to_child() instead.
3132 * There is a precondition to this function: the widget must be
3133 * already realized. Check the hildon_pannable_area_jump_to_child() for
3134 * more tips regarding how to call this function during
3140 hildon_pannable_area_scroll_to (HildonPannableArea *area,
3141 const gint x, const gint y)
3143 HildonPannableAreaPrivate *priv;
3145 gint dist_x, dist_y;
3146 gboolean hscroll_visible, vscroll_visible;
3148 g_return_if_fail (GTK_WIDGET_REALIZED (area));
3149 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3153 vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
3154 priv->vadjust->page_size);
3155 hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
3156 priv->hadjust->page_size);
3158 if (((!vscroll_visible)&&(!hscroll_visible)) ||
3159 (x == -1 && y == -1)) {
3163 if (priv->mode == HILDON_PANNABLE_AREA_MODE_PUSH)
3164 hildon_pannable_area_jump_to (area, x, y);
3166 width = priv->hadjust->upper - priv->hadjust->lower;
3167 height = priv->vadjust->upper - priv->vadjust->lower;
3169 g_return_if_fail (x < width || y < height);
3171 if ((x > -1)&&(hscroll_visible)) {
3172 priv->scroll_to_x = x - priv->hadjust->page_size/2;
3173 dist_x = priv->scroll_to_x - priv->hadjust->value;
3175 priv->scroll_to_x = -1;
3177 priv->vel_x = - dist_x/priv->vel_factor;
3180 priv->scroll_to_x = -1;
3183 if ((y > -1)&&(vscroll_visible)) {
3184 priv->scroll_to_y = y - priv->vadjust->page_size/2;
3185 dist_y = priv->scroll_to_y - priv->vadjust->value;
3187 priv->scroll_to_y = -1;
3189 priv->vel_y = - dist_y/priv->vel_factor;
3192 priv->scroll_to_y = y;
3195 if ((priv->scroll_to_y == -1) && (priv->scroll_to_y == -1)) {
3199 hildon_pannable_area_launch_fade_timeout (area, 1.0);
3202 priv->idle_id = gdk_threads_add_timeout ((gint) (1000.0 / (gdouble) priv->sps),
3204 hildon_pannable_area_timeout, area);
3208 * hildon_pannable_area_jump_to:
3209 * @area: A #HildonPannableArea.
3210 * @x: The x coordinate of the destination point or -1 to ignore this axis.
3211 * @y: The y coordinate of the destination point or -1 to ignore this axis.
3213 * Jumps the position of @area to ensure that (@x, @y) is a visible
3214 * point in the widget. In order to move in only one coordinate, you
3215 * must set the other one to -1. See hildon_pannable_area_scroll_to()
3216 * function for an example of how to calculate the position of
3217 * children in scrollable widgets like #GtkTreeview.
3219 * There is a precondition to this function: the widget must be
3220 * already realized. Check the hildon_pannable_area_jump_to_child() for
3221 * more tips regarding how to call this function during
3227 hildon_pannable_area_jump_to (HildonPannableArea *area,
3228 const gint x, const gint y)
3230 HildonPannableAreaPrivate *priv;
3234 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3235 g_return_if_fail (GTK_WIDGET_REALIZED (area));
3236 g_return_if_fail (x >= -1 && y >= -1);
3238 if (x == -1 && y == -1) {
3244 width = priv->hadjust->upper - priv->hadjust->lower;
3245 height = priv->vadjust->upper - priv->vadjust->lower;
3247 g_return_if_fail (x < width || y < height);
3249 hv = priv->hadjust->value;
3250 vv = priv->vadjust->value;
3253 gdouble jump_to = x - priv->hadjust->page_size/2;
3255 priv->hadjust->value = CLAMP (jump_to,
3256 priv->hadjust->lower,
3257 priv->hadjust->upper -
3258 priv->hadjust->page_size);
3262 gdouble jump_to = y - priv->vadjust->page_size/2;
3264 priv->vadjust->value = CLAMP (jump_to,
3265 priv->vadjust->lower,
3266 priv->vadjust->upper -
3267 priv->vadjust->page_size);
3270 if (hv != priv->hadjust->value)
3271 gtk_adjustment_value_changed (priv->hadjust);
3273 if (vv != priv->vadjust->value)
3274 gtk_adjustment_value_changed (priv->vadjust);
3276 priv->scroll_indicator_alpha = 1.0;
3278 if (priv->scroll_indicator_timeout) {
3279 g_source_remove (priv->scroll_indicator_timeout);
3280 priv->scroll_indicator_timeout = 0;
3283 if (priv->idle_id) {
3286 priv->overshooting_x = 0;
3287 priv->overshooting_y = 0;
3289 if ((priv->overshot_dist_x>0)||(priv->overshot_dist_y>0)) {
3290 priv->overshot_dist_x = 0;
3291 priv->overshot_dist_y = 0;
3293 gtk_widget_queue_resize (GTK_WIDGET (area));
3296 g_signal_emit (area, pannable_area_signals[PANNING_FINISHED], 0);
3297 g_source_remove (priv->idle_id);
3303 * hildon_pannable_area_scroll_to_child:
3304 * @area: A #HildonPannableArea.
3305 * @child: A #GtkWidget, descendant of @area.
3307 * Smoothly scrolls until @child is visible inside @area. @child must
3308 * be a descendant of @area. If you need to scroll inside a scrollable
3309 * widget, e.g., #GtkTreeview, see hildon_pannable_area_scroll_to().
3311 * There is a precondition to this function: the widget must be
3312 * already realized. Check the hildon_pannable_area_jump_to_child() for
3313 * more tips regarding how to call this function during
3319 hildon_pannable_area_scroll_to_child (HildonPannableArea *area, GtkWidget *child)
3321 GtkWidget *bin_child;
3324 g_return_if_fail (GTK_WIDGET_REALIZED (area));
3325 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3326 g_return_if_fail (GTK_IS_WIDGET (child));
3327 g_return_if_fail (gtk_widget_is_ancestor (child, GTK_WIDGET (area)));
3329 if (GTK_BIN (area)->child == NULL)
3332 /* We need to get to check the child of the inside the area */
3333 bin_child = GTK_BIN (area)->child;
3335 /* we check if we added a viewport */
3336 if (GTK_IS_VIEWPORT (bin_child)) {
3337 bin_child = GTK_BIN (bin_child)->child;
3340 if (gtk_widget_translate_coordinates (child, bin_child, 0, 0, &x, &y))
3341 hildon_pannable_area_scroll_to (area, x, y);
3345 * hildon_pannable_area_jump_to_child:
3346 * @area: A #HildonPannableArea.
3347 * @child: A #GtkWidget, descendant of @area.
3349 * Jumps to make sure @child is visible inside @area. @child must
3350 * be a descendant of @area. If you want to move inside a scrollable
3351 * widget, like, #GtkTreeview, see hildon_pannable_area_scroll_to().
3353 * There is a precondition to this function: the widget must be
3354 * already realized. You can control if the widget is ready with the
3355 * GTK_WIDGET_REALIZED macro. If you want to call this function during
3356 * the initialization process of the widget do it inside a callback to
3357 * the ::realize signal, using g_signal_connect_after() function.
3362 hildon_pannable_area_jump_to_child (HildonPannableArea *area, GtkWidget *child)
3364 GtkWidget *bin_child;
3367 g_return_if_fail (GTK_WIDGET_REALIZED (area));
3368 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3369 g_return_if_fail (GTK_IS_WIDGET (child));
3370 g_return_if_fail (gtk_widget_is_ancestor (child, GTK_WIDGET (area)));
3372 if (gtk_bin_get_child (GTK_BIN (area)) == NULL)
3375 /* We need to get to check the child of the inside the area */
3376 bin_child = gtk_bin_get_child (GTK_BIN (area));
3378 /* we check if we added a viewport */
3379 if (GTK_IS_VIEWPORT (bin_child)) {
3380 bin_child = gtk_bin_get_child (GTK_BIN (bin_child));
3383 if (gtk_widget_translate_coordinates (child, bin_child, 0, 0, &x, &y))
3384 hildon_pannable_area_jump_to (area, x, y);
3388 * hildon_pannable_get_child_widget_at:
3389 * @area: A #HildonPannableArea.
3390 * @x: horizontal coordinate of the point
3391 * @y: vertical coordinate of the point
3393 * Get the widget at the point (x, y) inside the pannable area. In
3394 * case no widget found it returns NULL.
3396 * returns: the #GtkWidget if we find a widget, NULL in any other case
3401 hildon_pannable_get_child_widget_at (HildonPannableArea *area,
3402 gdouble x, gdouble y)
3404 GdkWindow *window = NULL;
3405 GtkWidget *child_widget = NULL;
3407 window = hildon_pannable_area_get_topmost
3408 (gtk_bin_get_child (GTK_BIN (area))->window,
3409 x, y, NULL, NULL, GDK_ALL_EVENTS_MASK);
3411 gdk_window_get_user_data (window, (gpointer) &child_widget);
3413 return child_widget;
3418 * hildon_pannable_area_get_hadjustment:
3419 * @area: A #HildonPannableArea.
3421 * Returns the horizontal adjustment. This adjustment is the internal
3422 * widget adjustment used to control the animations. Do not modify it
3423 * directly to change the position of the pannable, to do that use the
3424 * pannable API. If you modify the object directly it could cause
3425 * artifacts in the animations.
3427 * returns: The horizontal #GtkAdjustment
3432 hildon_pannable_area_get_hadjustment (HildonPannableArea *area)
3435 g_return_val_if_fail (HILDON_IS_PANNABLE_AREA (area), NULL);
3437 return area->priv->hadjust;
3441 * hildon_pannable_area_get_vadjustment:
3442 * @area: A #HildonPannableArea.
3444 * Returns the vertical adjustment. This adjustment is the internal
3445 * widget adjustment used to control the animations. Do not modify it
3446 * directly to change the position of the pannable, to do that use the
3447 * pannable API. If you modify the object directly it could cause
3448 * artifacts in the animations.
3450 * returns: The vertical #GtkAdjustment
3455 hildon_pannable_area_get_vadjustment (HildonPannableArea *area)
3457 g_return_val_if_fail (HILDON_IS_PANNABLE_AREA (area), NULL);
3459 return area->priv->vadjust;
3464 * hildon_pannable_area_get_size_request_policy:
3465 * @area: A #HildonPannableArea.
3467 * This function returns the current size request policy of the
3468 * widget. That policy controls the way the size_request is done in
3469 * the pannable area. Check
3470 * hildon_pannable_area_set_size_request_policy() for a more detailed
3473 * returns: the policy is currently being used in the widget
3474 * #HildonSizeRequestPolicy.
3478 HildonSizeRequestPolicy
3479 hildon_pannable_area_get_size_request_policy (HildonPannableArea *area)
3481 HildonPannableAreaPrivate *priv;
3483 g_return_val_if_fail (HILDON_IS_PANNABLE_AREA (area), FALSE);
3487 return priv->size_request_policy;
3491 * hildon_pannable_area_set_size_request_policy:
3492 * @area: A #HildonPannableArea.
3493 * @size_request_policy: One of the allowed #HildonSizeRequestPolicy
3495 * This function sets the pannable area size request policy. That
3496 * policy controls the way the size_request is done in the pannable
3497 * area. Pannable can use the size request of its children
3498 * (#HILDON_SIZE_REQUEST_CHILDREN) or the minimum size required for
3499 * the area itself (#HILDON_SIZE_REQUEST_MINIMUM), the latter is the
3500 * default. Recall this size depends on the scrolling policy you are
3501 * requesting to the pannable area, if you set #GTK_POLICY_NEVER this
3502 * parameter will not have any effect with
3503 * #HILDON_SIZE_REQUEST_MINIMUM set.
3507 * Deprecated: This method and the policy request is deprecated, DO
3508 * NOT use it in future code, the only policy properly supported in
3509 * gtk+ nowadays is the minimum size. Use #gtk_window_set_default_size
3510 * or #gtk_window_set_geometry_hints with the proper size in your case
3511 * to define the height of your dialogs.
3514 hildon_pannable_area_set_size_request_policy (HildonPannableArea *area,
3515 HildonSizeRequestPolicy size_request_policy)
3517 HildonPannableAreaPrivate *priv;
3519 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3523 if (priv->size_request_policy == size_request_policy)
3526 priv->size_request_policy = size_request_policy;
3528 gtk_widget_queue_resize (GTK_WIDGET (area));
3530 g_object_notify (G_OBJECT (area), "size-request-policy");
3534 * hildon_pannable_area_get_center_on_child_focus
3535 * @area: A #HildonPannableArea
3537 * Gets the @area #HildonPannableArea:center-on-child-focus property
3540 * See #HildonPannableArea:center-on-child-focus for more information.
3542 * Returns: the @area #HildonPannableArea:center-on-child-focus value
3547 hildon_pannable_area_get_center_on_child_focus (HildonPannableArea *area)
3549 g_return_val_if_fail (HILDON_IS_PANNABLE_AREA (area), FALSE);
3551 return area->priv->center_on_child_focus;
3555 * hildon_pannable_area_set_center_on_child_focus
3556 * @area: A #HildonPannableArea
3557 * @value: the new value
3559 * Sets the @area #HildonPannableArea:center-on-child-focus property
3562 * See #HildonPannableArea:center-on-child-focus for more information.
3567 hildon_pannable_area_set_center_on_child_focus (HildonPannableArea *area,
3570 g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
3572 area->priv->center_on_child_focus = value;