More tuning for the pannable large lists kinetics.
[hildon] / hildon / hildon-pannable-area.c
index 18c4a11..219810a 100644 (file)
@@ -56,7 +56,8 @@
 #define RATIO_TOLERANCE 0.000001
 #define SCROLL_FADE_TIMEOUT 100
 #define MOTION_EVENTS_PER_SECOND 25
 #define RATIO_TOLERANCE 0.000001
 #define SCROLL_FADE_TIMEOUT 100
 #define MOTION_EVENTS_PER_SECOND 25
-#define CURSOR_STOPPED_TIMEOUT 80
+#define CURSOR_STOPPED_TIMEOUT 200
+#define MAX_SPEED_THRESHOLD 290
 #define PANNABLE_MAX_WIDTH 788
 #define PANNABLE_MAX_HEIGHT 378
 
 #define PANNABLE_MAX_WIDTH 788
 #define PANNABLE_MAX_HEIGHT 378
 
@@ -75,8 +76,9 @@ struct _HildonPannableAreaPrivate {
   gdouble ex;          /* Used to store mouse co-ordinates of the last */
   gdouble ey;          /* motion event in acceleration mode */
   gboolean enabled;
   gdouble ex;          /* Used to store mouse co-ordinates of the last */
   gdouble ey;          /* motion event in acceleration mode */
   gboolean enabled;
-  gboolean clicked;
+  gboolean button_pressed;
   guint32 last_time;   /* Last event time, to stop infinite loops */
   guint32 last_time;   /* Last event time, to stop infinite loops */
+  guint32 last_press_time;
   gint last_type;
   gboolean last_in;
   gboolean moved;
   gint last_type;
   gboolean last_in;
   gboolean moved;
@@ -96,6 +98,8 @@ struct _HildonPannableAreaPrivate {
   guint direction_error_margin;
   gdouble vel_x;
   gdouble vel_y;
   guint direction_error_margin;
   gdouble vel_x;
   gdouble vel_y;
+  gdouble old_vel_x;
+  gdouble old_vel_y;
   GdkWindow *child;
   gint child_width;
   gint child_height;
   GdkWindow *child;
   gint child_width;
   gint child_height;
@@ -133,17 +137,25 @@ struct _HildonPannableAreaPrivate {
 
   GtkAdjustment *hadjust;
   GtkAdjustment *vadjust;
 
   GtkAdjustment *hadjust;
   GtkAdjustment *vadjust;
+  gint x_offset;
+  gint y_offset;
 
   GtkPolicyType vscrollbar_policy;
   GtkPolicyType hscrollbar_policy;
 
   GdkGC *scrollbars_gc;
 
   GtkPolicyType vscrollbar_policy;
   GtkPolicyType hscrollbar_policy;
 
   GdkGC *scrollbars_gc;
+  GdkColor scroll_color;
+
+  gboolean center_on_child_focus;
+  gboolean center_on_child_focus_pending;
 };
 
 /*signals*/
 enum {
   HORIZONTAL_MOVEMENT,
   VERTICAL_MOVEMENT,
 };
 
 /*signals*/
 enum {
   HORIZONTAL_MOVEMENT,
   VERTICAL_MOVEMENT,
+  PANNING_STARTED,
+  PANNING_FINISHED,
   LAST_SIGNAL
 };
 
   LAST_SIGNAL
 };
 
@@ -175,6 +187,7 @@ enum {
   PROP_SIZE_REQUEST_POLICY,
   PROP_HADJUSTMENT,
   PROP_VADJUSTMENT,
   PROP_SIZE_REQUEST_POLICY,
   PROP_HADJUSTMENT,
   PROP_VADJUSTMENT,
+  PROP_CENTER_ON_CHILD_FOCUS,
   PROP_LAST
 };
 
   PROP_LAST
 };
 
@@ -188,6 +201,7 @@ static void hildon_pannable_area_set_property (GObject * object,
                                                guint property_id,
                                                const GValue * value,
                                                GParamSpec * pspec);
                                                guint property_id,
                                                const GValue * value,
                                                GParamSpec * pspec);
+static void hildon_pannable_area_remove_timeouts (GtkWidget * widget);
 static void hildon_pannable_area_dispose (GObject * object);
 static void hildon_pannable_area_realize (GtkWidget * widget);
 static void hildon_pannable_area_unrealize (GtkWidget * widget);
 static void hildon_pannable_area_dispose (GObject * object);
 static void hildon_pannable_area_realize (GtkWidget * widget);
 static void hildon_pannable_area_unrealize (GtkWidget * widget);
@@ -264,6 +278,14 @@ static void hildon_pannable_area_calculate_velocity (gdouble *vel,
 static gboolean hildon_pannable_area_motion_event_scroll_timeout (HildonPannableArea *area);
 static void hildon_pannable_area_motion_event_scroll (HildonPannableArea *area,
                                                       gdouble x, gdouble y);
 static gboolean hildon_pannable_area_motion_event_scroll_timeout (HildonPannableArea *area);
 static void hildon_pannable_area_motion_event_scroll (HildonPannableArea *area,
                                                       gdouble x, gdouble y);
+static void hildon_pannable_area_check_move (HildonPannableArea *area,
+                                             GdkEventMotion * event,
+                                             gdouble *x,
+                                             gdouble *y);
+static void hildon_pannable_area_handle_move (HildonPannableArea *area,
+                                              GdkEventMotion * event,
+                                              gdouble *x,
+                                              gdouble *y);
 static gboolean hildon_pannable_area_motion_notify_cb (GtkWidget * widget,
                                                        GdkEventMotion * event);
 static gboolean hildon_pannable_leave_notify_event (GtkWidget *widget,
 static gboolean hildon_pannable_area_motion_notify_cb (GtkWidget * widget,
                                                        GdkEventMotion * event);
 static gboolean hildon_pannable_leave_notify_event (GtkWidget *widget,
@@ -278,6 +300,9 @@ static void hildon_pannable_area_child_mapped (GtkWidget *widget,
 static void hildon_pannable_area_add (GtkContainer *container, GtkWidget *child);
 static void hildon_pannable_area_remove (GtkContainer *container, GtkWidget *child);
 static void hildon_pannable_calculate_vel_factor (HildonPannableArea * self);
 static void hildon_pannable_area_add (GtkContainer *container, GtkWidget *child);
 static void hildon_pannable_area_remove (GtkContainer *container, GtkWidget *child);
 static void hildon_pannable_calculate_vel_factor (HildonPannableArea * self);
+static void hildon_pannable_area_set_focus_child (GtkContainer *container,
+                                                 GtkWidget *child);
+static void hildon_pannable_area_center_on_child_focus (HildonPannableArea *area);
 
 
 static void
 
 
 static void
@@ -310,6 +335,7 @@ hildon_pannable_area_class_init (HildonPannableAreaClass * klass)
 
   container_class->add = hildon_pannable_area_add;
   container_class->remove = hildon_pannable_area_remove;
 
   container_class->add = hildon_pannable_area_add;
   container_class->remove = hildon_pannable_area_remove;
+  container_class->set_focus_child = hildon_pannable_area_set_focus_child;
 
   klass->horizontal_movement = NULL;
   klass->vertical_movement = NULL;
 
   klass->horizontal_movement = NULL;
   klass->vertical_movement = NULL;
@@ -369,7 +395,7 @@ hildon_pannable_area_class_init (HildonPannableAreaClass * klass)
                                                        "Minimum scroll velocity",
                                                        "Minimum distance the child widget should scroll "
                                                        "per 'frame', in pixels per frame.",
                                                        "Minimum scroll velocity",
                                                        "Minimum distance the child widget should scroll "
                                                        "per 'frame', in pixels per frame.",
-                                                       0, G_MAXDOUBLE, 20,
+                                                       0, G_MAXDOUBLE, 10,
                                                        G_PARAM_READWRITE |
                                                        G_PARAM_CONSTRUCT));
 
                                                        G_PARAM_READWRITE |
                                                        G_PARAM_CONSTRUCT));
 
@@ -389,7 +415,7 @@ hildon_pannable_area_class_init (HildonPannableAreaClass * klass)
                                                        "Maximum scroll velocity when overshooting",
                                                        "Maximum distance the child widget should scroll "
                                                        "per 'frame', in pixels per frame when it overshoots after hitting the edge.",
                                                        "Maximum scroll velocity when overshooting",
                                                        "Maximum distance the child widget should scroll "
                                                        "per 'frame', in pixels per frame when it overshoots after hitting the edge.",
-                                                       0, G_MAXDOUBLE, 20,
+                                                       0, G_MAXDOUBLE, 130,
                                                        G_PARAM_READWRITE |
                                                        G_PARAM_CONSTRUCT));
 
                                                        G_PARAM_READWRITE |
                                                        G_PARAM_CONSTRUCT));
 
@@ -410,7 +436,7 @@ hildon_pannable_area_class_init (HildonPannableAreaClass * klass)
                                                        "Deceleration multiplier",
                                                        "The multiplier used when decelerating when in "
                                                        "acceleration scrolling mode.",
                                                        "Deceleration multiplier",
                                                        "The multiplier used when decelerating when in "
                                                        "acceleration scrolling mode.",
-                                                       0, 1.0, 0.93,
+                                                       0, 1.0, 0.85,
                                                        G_PARAM_READWRITE |
                                                        G_PARAM_CONSTRUCT));
 
                                                        G_PARAM_READWRITE |
                                                        G_PARAM_CONSTRUCT));
 
@@ -440,7 +466,7 @@ hildon_pannable_area_class_init (HildonPannableAreaClass * klass)
                                                      "Threshold to consider a motion event an scroll",
                                                      "Amount of pixels to consider a motion event an scroll, if it is less"
                                                       "it is a click detected incorrectly by the touch screen.",
                                                      "Threshold to consider a motion event an scroll",
                                                      "Amount of pixels to consider a motion event an scroll, if it is less"
                                                       "it is a click detected incorrectly by the touch screen.",
-                                                     0, G_MAXUINT, 6,
+                                                     0, G_MAXUINT, 25,
                                                      G_PARAM_READWRITE |
                                                      G_PARAM_CONSTRUCT));
 
                                                      G_PARAM_READWRITE |
                                                      G_PARAM_CONSTRUCT));
 
@@ -450,7 +476,7 @@ hildon_pannable_area_class_init (HildonPannableAreaClass * klass)
                                                      "Time before starting to fade the scrollbar",
                                                      "Time the scrollbar is going to be visible if the widget is not in"
                                                       "action in miliseconds",
                                                      "Time before starting to fade the scrollbar",
                                                      "Time the scrollbar is going to be visible if the widget is not in"
                                                       "action in miliseconds",
-                                                     0, G_MAXUINT, 3000,
+                                                     0, G_MAXUINT, 1000,
                                                      G_PARAM_READWRITE |
                                                      G_PARAM_CONSTRUCT));
 
                                                      G_PARAM_READWRITE |
                                                      G_PARAM_CONSTRUCT));
 
@@ -470,7 +496,7 @@ hildon_pannable_area_class_init (HildonPannableAreaClass * klass)
                                                      "Multiplier of the calculated speed",
                                                      "Force applied to the movement, multiplies the calculated speed of the"
                                                       "user movement the cursor in the screen",
                                                      "Multiplier of the calculated speed",
                                                      "Force applied to the movement, multiplies the calculated speed of the"
                                                       "user movement the cursor in the screen",
-                                                     0, G_MAXUINT, 120,
+                                                     0, G_MAXUINT, 50,
                                                      G_PARAM_READWRITE |
                                                      G_PARAM_CONSTRUCT));
 
                                                      G_PARAM_READWRITE |
                                                      G_PARAM_CONSTRUCT));
 
@@ -558,6 +584,16 @@ hildon_pannable_area_class_init (HildonPannableAreaClass * klass)
                                                        GTK_TYPE_ADJUSTMENT,
                                                        G_PARAM_READABLE));
 
                                                        GTK_TYPE_ADJUSTMENT,
                                                        G_PARAM_READABLE));
 
+  g_object_class_install_property (object_class,
+                                   PROP_CENTER_ON_CHILD_FOCUS,
+                                   g_param_spec_boolean ("center-on-child-focus",
+                                                         "Center on the child with the focus",
+                                                         "Whether to center the pannable on the child that receives the focus.",
+                                                         FALSE,
+                                                         G_PARAM_READWRITE |
+                                                         G_PARAM_CONSTRUCT));
+
+
   gtk_widget_class_install_style_property (widget_class,
                                           g_param_spec_uint
                                           ("indicator-width",
   gtk_widget_class_install_style_property (widget_class,
                                           g_param_spec_uint
                                           ("indicator-width",
@@ -618,6 +654,52 @@ hildon_pannable_area_class_init (HildonPannableAreaClass * klass)
                  G_TYPE_DOUBLE,
                  G_TYPE_DOUBLE);
 
                  G_TYPE_DOUBLE,
                  G_TYPE_DOUBLE);
 
+ /**
+   * HildonPannableArea::panning-started:
+   * @hildonpannable: the pannable area object that is going to start
+   * the panning
+   *
+   * This signal is emitted before the panning starts. Applications
+   * can return %TRUE to avoid the panning. The main difference with
+   * the vertical-movement and horizontal-movement signals is those
+   * gesture signals are launched no matter if the widget is going to
+   * move, this signal means the widget is going to start moving. It
+   * could even happen that the widget moves and there was no gesture
+   * (i.e. click meanwhile the pannable is overshooting).
+   *
+   * Returns: %TRUE to stop the panning launch. %FALSE to continue
+   * with it.
+   *
+   * Since: 2.2
+   */
+  pannable_area_signals[PANNING_STARTED] =
+    g_signal_new ("panning-started",
+                  G_TYPE_FROM_CLASS (object_class),
+                  0,
+                  0,
+                  NULL, NULL,
+                  _hildon_marshal_BOOLEAN__VOID,
+                  G_TYPE_BOOLEAN, 0);
+
+ /**
+   * HildonPannableArea::panning-finished:
+   * @hildonpannable: the pannable area object that finished the
+   * panning
+   *
+   * This signal is emitted after the kinetic panning has
+   * finished.
+   *
+   * Since: 2.2
+   */
+  pannable_area_signals[PANNING_FINISHED] =
+    g_signal_new ("panning-finished",
+                  G_TYPE_FROM_CLASS (object_class),
+                  0,
+                  0,
+                  NULL, NULL,
+                  _hildon_marshal_VOID__VOID,
+                  G_TYPE_NONE, 0);
+
 }
 
 static void
 }
 
 static void
@@ -630,8 +712,9 @@ hildon_pannable_area_init (HildonPannableArea * area)
   area->priv = priv;
 
   priv->moved = FALSE;
   area->priv = priv;
 
   priv->moved = FALSE;
-  priv->clicked = FALSE;
+  priv->button_pressed = FALSE;
   priv->last_time = 0;
   priv->last_time = 0;
+  priv->last_press_time = 0;
   priv->last_type = 0;
   priv->vscroll_visible = TRUE;
   priv->hscroll_visible = TRUE;
   priv->last_type = 0;
   priv->vscroll_visible = TRUE;
   priv->hscroll_visible = TRUE;
@@ -643,11 +726,14 @@ hildon_pannable_area_init (HildonPannableArea * area)
   priv->idle_id = 0;
   priv->vel_x = 0;
   priv->vel_y = 0;
   priv->idle_id = 0;
   priv->vel_x = 0;
   priv->vel_y = 0;
+  priv->old_vel_x = 0;
+  priv->old_vel_y = 0;
   priv->scroll_indicator_alpha = 0.0;
   priv->scroll_indicator_timeout = 0;
   priv->motion_event_scroll_timeout = 0;
   priv->scroll_indicator_event_interrupt = 0;
   priv->scroll_indicator_alpha = 0.0;
   priv->scroll_indicator_timeout = 0;
   priv->motion_event_scroll_timeout = 0;
   priv->scroll_indicator_event_interrupt = 0;
-  priv->scroll_delay_counter = priv->scrollbar_fade_delay;
+  priv->scroll_delay_counter = 0;
+  priv->scrollbar_fade_delay = 0;
   priv->scroll_to_x = -1;
   priv->scroll_to_y = -1;
   priv->first_drag = TRUE;
   priv->scroll_to_x = -1;
   priv->scroll_to_y = -1;
   priv->first_drag = TRUE;
@@ -655,8 +741,12 @@ hildon_pannable_area_init (HildonPannableArea * area)
   priv->child_width = 0;
   priv->child_height = 0;
   priv->last_in = TRUE;
   priv->child_width = 0;
   priv->child_height = 0;
   priv->last_in = TRUE;
+  priv->x_offset = 0;
+  priv->y_offset = 0;
+  priv->center_on_child_focus_pending = FALSE;
 
 
-  gtk_widget_add_events (GTK_WIDGET (area), GDK_POINTER_MOTION_HINT_MASK);
+  gtk_style_lookup_color (GTK_WIDGET (area)->style,
+                         "SecondaryTextColor", &priv->scroll_color);
 
   priv->hadjust =
     GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
 
   priv->hadjust =
     GTK_ADJUSTMENT (gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0));
@@ -767,6 +857,9 @@ hildon_pannable_area_get_property (GObject * object,
                         hildon_pannable_area_get_vadjustment
                         (HILDON_PANNABLE_AREA (object)));
     break;
                         hildon_pannable_area_get_vadjustment
                         (HILDON_PANNABLE_AREA (object)));
     break;
+  case PROP_CENTER_ON_CHILD_FOCUS:
+    g_value_set_boolean (value, priv->center_on_child_focus);
+    break;
   default:
     G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
   }
   default:
     G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
   }
@@ -823,7 +916,20 @@ hildon_pannable_area_set_property (GObject * object,
     priv->sps = g_value_get_uint (value);
     break;
   case PROP_PANNING_THRESHOLD:
     priv->sps = g_value_get_uint (value);
     break;
   case PROP_PANNING_THRESHOLD:
-    priv->panning_threshold = g_value_get_uint (value);
+    {
+      GtkSettings *settings = gtk_settings_get_default ();
+      GtkSettingsValue svalue = { NULL, { 0, }, };
+
+      priv->panning_threshold = g_value_get_uint (value);
+
+      /* insure gtk dnd is the same we are using, not allowed
+         different thresholds in the same application */
+      svalue.origin = "panning_threshold";
+      g_value_init (&svalue.value, G_TYPE_LONG);
+      g_value_set_long (&svalue.value, priv->panning_threshold);
+      gtk_settings_set_property_value (settings, "gtk-dnd-drag-threshold", &svalue);
+      g_value_unset (&svalue.value);
+    }
     break;
   case PROP_SCROLLBAR_FADE_DELAY:
     /* convert to miliseconds */
     break;
   case PROP_SCROLLBAR_FADE_DELAY:
     /* convert to miliseconds */
@@ -869,6 +975,9 @@ hildon_pannable_area_set_property (GObject * object,
     hildon_pannable_area_set_size_request_policy (HILDON_PANNABLE_AREA (object),
                                                   g_value_get_enum (value));
     break;
     hildon_pannable_area_set_size_request_policy (HILDON_PANNABLE_AREA (object),
                                                   g_value_get_enum (value));
     break;
+  case PROP_CENTER_ON_CHILD_FOCUS:
+    priv->center_on_child_focus = g_value_get_boolean (value);
+    break;
 
   default:
     G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
 
   default:
     G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -881,20 +990,7 @@ hildon_pannable_area_dispose (GObject * object)
   HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (object)->priv;
   GtkWidget *child = gtk_bin_get_child (GTK_BIN (object));
 
   HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (object)->priv;
   GtkWidget *child = gtk_bin_get_child (GTK_BIN (object));
 
-  if (priv->idle_id) {
-    g_source_remove (priv->idle_id);
-    priv->idle_id = 0;
-  }
-
-  if (priv->scroll_indicator_timeout){
-    g_source_remove (priv->scroll_indicator_timeout);
-    priv->scroll_indicator_timeout = 0;
-  }
-
-  if (priv->motion_event_scroll_timeout){
-    g_source_remove (priv->motion_event_scroll_timeout);
-    priv->motion_event_scroll_timeout = 0;
-  }
+  hildon_pannable_area_remove_timeouts (GTK_WIDGET (object));
 
   if (child) {
     g_signal_handlers_disconnect_by_func (child,
 
   if (child) {
     g_signal_handlers_disconnect_by_func (child,
@@ -972,6 +1068,7 @@ hildon_pannable_area_realize (GtkWidget * widget)
     | GDK_BUTTON_PRESS_MASK
     | GDK_BUTTON_RELEASE_MASK
     | GDK_SCROLL_MASK
     | GDK_BUTTON_PRESS_MASK
     | GDK_BUTTON_RELEASE_MASK
     | GDK_SCROLL_MASK
+    | GDK_POINTER_MOTION_HINT_MASK
     | GDK_EXPOSURE_MASK | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK;
   attributes.wclass = GDK_INPUT_ONLY;
 
     | GDK_EXPOSURE_MASK | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK;
   attributes.wclass = GDK_INPUT_ONLY;
 
@@ -988,6 +1085,29 @@ hildon_pannable_area_realize (GtkWidget * widget)
   gdk_gc_copy (priv->scrollbars_gc, widget->style->fg_gc[GTK_STATE_INSENSITIVE]);
 }
 
   gdk_gc_copy (priv->scrollbars_gc, widget->style->fg_gc[GTK_STATE_INSENSITIVE]);
 }
 
+
+static void
+hildon_pannable_area_remove_timeouts (GtkWidget * widget)
+{
+  HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
+
+  if (priv->idle_id) {
+    g_signal_emit (widget, pannable_area_signals[PANNING_FINISHED], 0);
+    g_source_remove (priv->idle_id);
+    priv->idle_id = 0;
+  }
+
+  if (priv->scroll_indicator_timeout){
+    g_source_remove (priv->scroll_indicator_timeout);
+    priv->scroll_indicator_timeout = 0;
+  }
+
+  if (priv->motion_event_scroll_timeout){
+    g_source_remove (priv->motion_event_scroll_timeout);
+    priv->motion_event_scroll_timeout = 0;
+  }
+}
+
 static void
 hildon_pannable_area_unrealize (GtkWidget * widget)
 {
 static void
 hildon_pannable_area_unrealize (GtkWidget * widget)
 {
@@ -995,6 +1115,8 @@ hildon_pannable_area_unrealize (GtkWidget * widget)
 
   priv = HILDON_PANNABLE_AREA (widget)->priv;
 
 
   priv = HILDON_PANNABLE_AREA (widget)->priv;
 
+  hildon_pannable_area_remove_timeouts (widget);
+
   if (priv->event_window != NULL) {
     gdk_window_set_user_data (priv->event_window, NULL);
     gdk_window_destroy (priv->event_window);
   if (priv->event_window != NULL) {
     gdk_window_set_user_data (priv->event_window, NULL);
     gdk_window_destroy (priv->event_window);
@@ -1096,6 +1218,7 @@ hildon_pannable_area_size_allocate (GtkWidget * widget,
   HildonPannableAreaPrivate *priv;
   GtkWidget *child = gtk_bin_get_child (GTK_BIN (widget));
   gint border_width;
   HildonPannableAreaPrivate *priv;
   GtkWidget *child = gtk_bin_get_child (GTK_BIN (widget));
   gint border_width;
+  gdouble hv, vv;
 
   border_width = GTK_CONTAINER (widget)->border_width;
 
 
   border_width = GTK_CONTAINER (widget)->border_width;
 
@@ -1118,6 +1241,8 @@ hildon_pannable_area_size_allocate (GtkWidget * widget,
 
   if (child && GTK_WIDGET_VISIBLE (child)) {
 
 
   if (child && GTK_WIDGET_VISIBLE (child)) {
 
+    hildon_pannable_area_check_scrollbars (HILDON_PANNABLE_AREA (widget));
+
     hildon_pannable_area_child_allocate_calculate (widget,
                                                    allocation,
                                                    &child_allocation);
     hildon_pannable_area_child_allocate_calculate (widget,
                                                    allocation,
                                                    &child_allocation);
@@ -1132,18 +1257,25 @@ hildon_pannable_area_size_allocate (GtkWidget * widget,
       gtk_widget_size_allocate (child, &child_allocation);
     }
 
       gtk_widget_size_allocate (child, &child_allocation);
     }
 
+    hv = priv->hadjust->value;
+    vv = priv->vadjust->value;
+
     /* we have to do this after child size_allocate because page_size is
      * changed when we allocate the size of the children */
     if (priv->overshot_dist_y < 0) {
     /* we have to do this after child size_allocate because page_size is
      * changed when we allocate the size of the children */
     if (priv->overshot_dist_y < 0) {
-      gtk_adjustment_set_value (priv->vadjust, priv->vadjust->upper -
-                                priv->vadjust->page_size);
+      priv->vadjust->value = priv->vadjust->upper - priv->vadjust->page_size;
     }
 
     if (priv->overshot_dist_x < 0) {
     }
 
     if (priv->overshot_dist_x < 0) {
-      gtk_adjustment_set_value (priv->hadjust, priv->hadjust->upper -
-                                priv->hadjust->page_size);
+      priv->hadjust->value = priv->hadjust->upper - priv->hadjust->page_size;
     }
 
     }
 
+    if (hv != priv->hadjust->value)
+      gtk_adjustment_value_changed (priv->hadjust);
+
+    if (vv != priv->vadjust->value)
+      gtk_adjustment_value_changed (priv->vadjust);
+
   } else {
     hildon_pannable_area_check_scrollbars (HILDON_PANNABLE_AREA (widget));
   }
   } else {
     hildon_pannable_area_check_scrollbars (HILDON_PANNABLE_AREA (widget));
   }
@@ -1158,6 +1290,7 @@ hildon_pannable_area_style_set (GtkWidget * widget,
   GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->
     style_set (widget, previous_style);
 
   GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->
     style_set (widget, previous_style);
 
+  gtk_style_lookup_color (widget->style, "SecondaryTextColor", &priv->scroll_color);
   gtk_widget_style_get (widget, "indicator-width", &priv->indicator_width, NULL);
 }
 
   gtk_widget_style_get (widget, "indicator-width", &priv->indicator_width, NULL);
 }
 
@@ -1378,7 +1511,7 @@ hildon_pannable_draw_vscroll (GtkWidget *widget,
   HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
   gfloat y, height;
   GdkColor transp_color;
   HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
   gfloat y, height;
   GdkColor transp_color;
-  GdkGC *gc = widget->style->fg_gc[GTK_STATE_INSENSITIVE];
+  GdkGC *gc = priv->scrollbars_gc;
 
   gdk_draw_rectangle (widget->window,
                       widget->style->bg_gc[GTK_STATE_NORMAL],
 
   gdk_draw_rectangle (widget->window,
                       widget->style->bg_gc[GTK_STATE_NORMAL],
@@ -1402,14 +1535,13 @@ hildon_pannable_draw_vscroll (GtkWidget *widget,
            (priv->hscroll_visible ? priv->hscroll_rect.height : 0) -
            height);
 
            (priv->hscroll_visible ? priv->hscroll_rect.height : 0) -
            height);
 
-  if (priv->scroll_indicator_alpha < 1.0) {
+  if (priv->scroll_indicator_alpha == 1.0) {
+    transp_color = priv->scroll_color;
+  } else if (priv->scroll_indicator_alpha < 1.0) {
     tranparency_color (&transp_color, *back_color, *scroll_color,
                        priv->scroll_indicator_alpha);
     tranparency_color (&transp_color, *back_color, *scroll_color,
                        priv->scroll_indicator_alpha);
-
-    gdk_gc_set_rgb_fg_color (priv->scrollbars_gc, &transp_color);
-
-    gc = priv->scrollbars_gc;
   }
   }
+  gdk_gc_set_rgb_fg_color (gc, &transp_color);
 
   gdk_draw_rectangle (widget->window, gc,
                       TRUE, priv->vscroll_rect.x, y,
 
   gdk_draw_rectangle (widget->window, gc,
                       TRUE, priv->vscroll_rect.x, y,
@@ -1424,7 +1556,7 @@ hildon_pannable_draw_hscroll (GtkWidget *widget,
   HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
   gfloat x, width;
   GdkColor transp_color;
   HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
   gfloat x, width;
   GdkColor transp_color;
-  GdkGC *gc = widget->style->fg_gc[GTK_STATE_INSENSITIVE];
+  GdkGC *gc = priv->scrollbars_gc;
 
   gdk_draw_rectangle (widget->window,
                       widget->style->bg_gc[GTK_STATE_INSENSITIVE],
 
   gdk_draw_rectangle (widget->window,
                       widget->style->bg_gc[GTK_STATE_INSENSITIVE],
@@ -1449,14 +1581,13 @@ hildon_pannable_draw_hscroll (GtkWidget *widget,
            (priv->vscroll_visible ? priv->vscroll_rect.width : 0) -
            width);
 
            (priv->vscroll_visible ? priv->vscroll_rect.width : 0) -
            width);
 
-  if (priv->scroll_indicator_alpha < 1.0) {
+  if (priv->scroll_indicator_alpha == 1.0) {
+    transp_color = priv->scroll_color;
+  } else if (priv->scroll_indicator_alpha < 1.0) {
     tranparency_color (&transp_color, *back_color, *scroll_color,
                        priv->scroll_indicator_alpha);
     tranparency_color (&transp_color, *back_color, *scroll_color,
                        priv->scroll_indicator_alpha);
-
-    gdk_gc_set_rgb_fg_color (priv->scrollbars_gc, &transp_color);
-
-    gc = priv->scrollbars_gc;
   }
   }
+  gdk_gc_set_rgb_fg_color (gc, &transp_color);
 
   gdk_draw_rectangle (widget->window, gc,
                       TRUE, x, priv->hscroll_rect.y, width,
 
   gdk_draw_rectangle (widget->window, gc,
                       TRUE, x, priv->hscroll_rect.y, width,
@@ -1469,15 +1600,8 @@ static void
 hildon_pannable_area_initial_effect (GtkWidget * widget)
 {
   HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
 hildon_pannable_area_initial_effect (GtkWidget * widget)
 {
   HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
-  gboolean hscroll_visible, vscroll_visible;
 
   if (priv->initial_hint) {
 
   if (priv->initial_hint) {
-
-    vscroll_visible = (priv->vadjust->upper - priv->vadjust->lower >
-                       priv->vadjust->page_size);
-    hscroll_visible = (priv->hadjust->upper - priv->hadjust->lower >
-                       priv->hadjust->page_size);
-
     if (priv->vscroll_visible || priv->hscroll_visible) {
 
       priv->scroll_indicator_event_interrupt = 0;
     if (priv->vscroll_visible || priv->hscroll_visible) {
 
       priv->scroll_indicator_event_interrupt = 0;
@@ -1498,9 +1622,11 @@ hildon_pannable_area_launch_fade_timeout (HildonPannableArea * area,
 
   if (!priv->scroll_indicator_timeout)
     priv->scroll_indicator_timeout =
 
   if (!priv->scroll_indicator_timeout)
     priv->scroll_indicator_timeout =
-      gdk_threads_add_timeout (SCROLL_FADE_TIMEOUT,
-                               (GSourceFunc) hildon_pannable_area_scroll_indicator_fade,
-                               area);
+      gdk_threads_add_timeout_full (G_PRIORITY_HIGH_IDLE + 20,
+                                   SCROLL_FADE_TIMEOUT,
+                                   (GSourceFunc) hildon_pannable_area_scroll_indicator_fade,
+                                   area,
+                                   NULL);
 }
 
 static void
 }
 
 static void
@@ -1515,9 +1641,17 @@ static void
 hildon_pannable_area_adjust_value_changed (HildonPannableArea * area,
                                            gpointer data)
 {
 hildon_pannable_area_adjust_value_changed (HildonPannableArea * area,
                                            gpointer data)
 {
-  if (GTK_WIDGET_REALIZED (area)) {
-    HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (area)->priv;
+  HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (area)->priv;
+  gint xdiff, ydiff;
+  gint x = priv->x_offset;
+  gint y = priv->y_offset;
 
 
+  priv->x_offset = priv->hadjust->value;
+  xdiff = x - priv->x_offset;
+  priv->y_offset = priv->vadjust->value;
+  ydiff = y - priv->y_offset;
+
+  if ((xdiff || ydiff) && GTK_WIDGET_DRAWABLE (area)) {
     hildon_pannable_area_redraw (area);
 
     if ((priv->vscroll_visible) || (priv->hscroll_visible)) {
     hildon_pannable_area_redraw (area);
 
     if ((priv->vscroll_visible) || (priv->hscroll_visible)) {
@@ -1554,8 +1688,8 @@ hildon_pannable_area_scroll_indicator_fade(HildonPannableArea * area)
   HildonPannableAreaPrivate *priv = area->priv;
 
   /* if moving do not fade out */
   HildonPannableAreaPrivate *priv = area->priv;
 
   /* if moving do not fade out */
-  if (((ABS (priv->vel_y)>1.0)||
-       (ABS (priv->vel_x)>1.0))&&(!priv->clicked)) {
+  if (((ABS (priv->vel_y)>priv->vmin)||
+       (ABS (priv->vel_x)>priv->vmin))&&(!priv->button_pressed)) {
 
     return TRUE;
   }
 
     return TRUE;
   }
@@ -1611,9 +1745,15 @@ hildon_pannable_area_expose_event (GtkWidget * widget,
   GdkColor scroll_color = widget->style->base[GTK_STATE_SELECTED];
 #else /* USE_CAIRO_SCROLLBARS */
   GdkColor back_color = widget->style->bg[GTK_STATE_NORMAL];
   GdkColor scroll_color = widget->style->base[GTK_STATE_SELECTED];
 #else /* USE_CAIRO_SCROLLBARS */
   GdkColor back_color = widget->style->bg[GTK_STATE_NORMAL];
-  GdkColor scroll_color = widget->style->fg[GTK_STATE_INSENSITIVE];
+  GdkColor scroll_color = priv->scroll_color;
 #endif
 
 #endif
 
+  if (G_UNLIKELY (priv->initial_effect)) {
+    hildon_pannable_area_initial_effect (widget);
+
+    priv->initial_effect = FALSE;
+  }
+
   if (gtk_bin_get_child (GTK_BIN (widget))) {
 
     if (priv->scroll_indicator_alpha > 0.1) {
   if (gtk_bin_get_child (GTK_BIN (widget))) {
 
     if (priv->scroll_indicator_alpha > 0.1) {
@@ -1702,13 +1842,6 @@ hildon_pannable_area_expose_event (GtkWidget * widget,
 
   }
 
 
   }
 
-  if (G_UNLIKELY (priv->initial_effect)) {
-
-    hildon_pannable_area_initial_effect (widget);
-
-    priv->initial_effect = FALSE;
-  }
-
   return GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->expose_event (widget, event);
 }
 
   return GTK_WIDGET_CLASS (hildon_pannable_area_parent_class)->expose_event (widget, event);
 }
 
@@ -1755,9 +1888,9 @@ hildon_pannable_area_get_topmost (GdkWindow * window,
                                                             tx, ty, mask);
         if (!selected_window) {
           if (tx)
                                                             tx, ty, mask);
         if (!selected_window) {
           if (tx)
-            *tx = x;
+            *tx = x-wx;
           if (ty)
           if (ty)
-            *ty = y;
+            *ty = y-wy;
           selected_window = child;
         }
       } else {
           selected_window = child;
         }
       } else {
@@ -1808,7 +1941,8 @@ hildon_pannable_area_button_press_cb (GtkWidget * widget,
                                      GdkEventButton * event)
 {
   gint x, y;
                                      GdkEventButton * event)
 {
   gint x, y;
-  HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
+  HildonPannableArea *area = HILDON_PANNABLE_AREA (widget);
+  HildonPannableAreaPrivate *priv = area->priv;
 
   if ((!priv->enabled) || (event->button != 1) ||
       ((event->time == priv->last_time) &&
 
   if ((!priv->enabled) || (event->button != 1) ||
       ((event->time == priv->last_time) &&
@@ -1817,16 +1951,17 @@ hildon_pannable_area_button_press_cb (GtkWidget * widget,
 
   priv->scroll_indicator_event_interrupt = 1;
 
 
   priv->scroll_indicator_event_interrupt = 1;
 
-  hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget),
+  hildon_pannable_area_launch_fade_timeout (area,
                                             priv->scroll_indicator_alpha);
 
   priv->last_time = event->time;
                                             priv->scroll_indicator_alpha);
 
   priv->last_time = event->time;
+  priv->last_press_time = event->time;
   priv->last_type = 1;
 
   priv->scroll_to_x = -1;
   priv->scroll_to_y = -1;
 
   priv->last_type = 1;
 
   priv->scroll_to_x = -1;
   priv->scroll_to_y = -1;
 
-  if (priv->clicked && priv->child) {
+  if (priv->button_pressed && priv->child) {
     /* Widget stole focus on last click, send crossing-out event */
     synth_crossing (priv->child, 0, 0, event->x_root, event->y_root,
                    event->time, FALSE);
     /* Widget stole focus on last click, send crossing-out event */
     synth_crossing (priv->child, 0, 0, event->x_root, event->y_root,
                    event->time, FALSE);
@@ -1846,11 +1981,18 @@ hildon_pannable_area_button_press_cb (GtkWidget * widget,
   else
     priv->child = NULL;
 
   else
     priv->child = NULL;
 
-  priv->clicked = TRUE;
+  priv->button_pressed = TRUE;
 
   /* Stop scrolling on mouse-down (so you can flick, then hold to stop) */
 
   /* Stop scrolling on mouse-down (so you can flick, then hold to stop) */
+  priv->old_vel_x = priv->vel_x;
+  priv->old_vel_y = priv->vel_y;
   priv->vel_x = 0;
   priv->vel_y = 0;
   priv->vel_x = 0;
   priv->vel_y = 0;
+  if (priv->idle_id) {
+    g_source_remove (priv->idle_id);
+    priv->idle_id = 0;
+    g_signal_emit (area, pannable_area_signals[PANNING_FINISHED], 0);
+  }
 
   if (priv->child) {
 
 
   if (priv->child) {
 
@@ -1862,6 +2004,8 @@ hildon_pannable_area_button_press_cb (GtkWidget * widget,
                               (gpointer) & priv->child);
 
     event = (GdkEventButton *) gdk_event_copy ((GdkEvent *) event);
                               (gpointer) & priv->child);
 
     event = (GdkEventButton *) gdk_event_copy ((GdkEvent *) event);
+    /* remove the reference we added with the copy */
+    g_object_unref (priv->event_window);
     event->x = x;
     event->y = y;
     priv->cx = x;
     event->x = x;
     event->y = y;
     priv->cx = x;
@@ -1946,7 +2090,16 @@ hildon_pannable_area_refresh (HildonPannableArea * area)
 {
   if (GTK_WIDGET_DRAWABLE (area) &&
       hildon_pannable_area_check_scrollbars (area)) {
 {
   if (GTK_WIDGET_DRAWABLE (area) &&
       hildon_pannable_area_check_scrollbars (area)) {
+    HildonPannableAreaPrivate *priv = area->priv;
+
     gtk_widget_queue_resize (GTK_WIDGET (area));
     gtk_widget_queue_resize (GTK_WIDGET (area));
+
+    if ((priv->vscroll_visible) || (priv->hscroll_visible)) {
+      priv->scroll_indicator_event_interrupt = 0;
+      priv->scroll_delay_counter = area->priv->scrollbar_fade_delay;
+
+      hildon_pannable_area_launch_fade_timeout (area, 1.0);
+    }
   } else {
     hildon_pannable_area_redraw (area);
   }
   } else {
     hildon_pannable_area_redraw (area);
   }
@@ -2019,9 +2172,9 @@ hildon_pannable_axis_scroll (HildonPannableArea *area,
       }
     }
 
       }
     }
 
-    gtk_adjustment_set_value (adjust, dist);
+    adjust->value = dist;
   } else {
   } else {
-    if (!priv->clicked) {
+    if (!priv->button_pressed) {
 
       /* When the overshoot has started we continue for
        * PROP_BOUNCE_STEPS more steps into the overshoot before we
 
       /* When the overshoot has started we continue for
        * PROP_BOUNCE_STEPS more steps into the overshoot before we
@@ -2039,7 +2192,7 @@ hildon_pannable_axis_scroll (HildonPannableArea *area,
           *vel *= -1;
         } else if ((*overshooting > 1) && (*vel < 0)) {
           /* we add the MIN in order to avoid very small speeds */
           *vel *= -1;
         } else if ((*overshooting > 1) && (*vel < 0)) {
           /* we add the MIN in order to avoid very small speeds */
-          *vel = MIN ((((gdouble)*overshot_dist)*0.4) * -1, -2.0);
+          *vel = MIN (((((gdouble)*overshot_dist)*0.8) * -1), -10.0);
         }
 
         *overshot_dist = CLAMP (*overshot_dist + *vel, 0, overshoot_max);
         }
 
         *overshot_dist = CLAMP (*overshot_dist + *vel, 0, overshoot_max);
@@ -2055,7 +2208,7 @@ hildon_pannable_axis_scroll (HildonPannableArea *area,
           *vel *= -1;
         } else if ((*overshooting > 1) && (*vel > 0)) {
           /* we add the MAX in order to avoid very small speeds */
           *vel *= -1;
         } else if ((*overshooting > 1) && (*vel > 0)) {
           /* we add the MAX in order to avoid very small speeds */
-          *vel = MAX ((((gdouble)*overshot_dist)*0.4) * -1, 2.0);
+          *vel = MAX (((((gdouble)*overshot_dist)*0.8) * -1), 10.0);
         }
 
         *overshot_dist = CLAMP (*overshot_dist + (*vel), -overshoot_max, 0);
         }
 
         *overshot_dist = CLAMP (*overshot_dist + (*vel), -overshoot_max, 0);
@@ -2077,7 +2230,10 @@ hildon_pannable_axis_scroll (HildonPannableArea *area,
         *overshot_dist = CLAMP ((*overshot_dist) + inc, -1 * overshoot_max, 0);
       } else {
         *overshooting = 0;
         *overshot_dist = CLAMP ((*overshot_dist) + inc, -1 * overshoot_max, 0);
       } else {
         *overshooting = 0;
-        gtk_adjustment_set_value (adjust, dist);
+        adjust->value = CLAMP (dist,
+                               adjust->lower,
+                               adjust->upper -
+                               adjust->page_size);
       }
 
       if (*overshot_dist != overshot_dist_old)
       }
 
       if (*overshot_dist != overshot_dist_old)
@@ -2093,6 +2249,7 @@ hildon_pannable_area_scroll (HildonPannableArea *area,
   gboolean sx, sy;
   HildonPannableAreaPrivate *priv = area->priv;
   gboolean hscroll_visible, vscroll_visible;
   gboolean sx, sy;
   HildonPannableAreaPrivate *priv = area->priv;
   gboolean hscroll_visible, vscroll_visible;
+  gdouble hv, vv;
 
   if (gtk_bin_get_child (GTK_BIN (area)) == NULL)
     return;
 
   if (gtk_bin_get_child (GTK_BIN (area)) == NULL)
     return;
@@ -2105,6 +2262,9 @@ hildon_pannable_area_scroll (HildonPannableArea *area,
   sx = TRUE;
   sy = TRUE;
 
   sx = TRUE;
   sy = TRUE;
 
+  hv = priv->hadjust->value;
+  vv = priv->vadjust->value;
+
   if (vscroll_visible) {
     hildon_pannable_axis_scroll (area, priv->vadjust, &priv->vel_y, y,
                                  &priv->overshooting_y, &priv->overshot_dist_y,
   if (vscroll_visible) {
     hildon_pannable_axis_scroll (area, priv->vadjust, &priv->vel_y, y,
                                  &priv->overshooting_y, &priv->overshot_dist_y,
@@ -2121,6 +2281,12 @@ hildon_pannable_area_scroll (HildonPannableArea *area,
     priv->vel_x = 0;
   }
 
     priv->vel_x = 0;
   }
 
+  if (hv != priv->hadjust->value)
+    gtk_adjustment_value_changed (priv->hadjust);
+
+  if (vv != priv->vadjust->value)
+    gtk_adjustment_value_changed (priv->vadjust);
+
   /* If the scroll on a particular axis wasn't succesful, reset the
    * initial scroll position to the new mouse co-ordinate. This means
    * when you get to the top of the page, dragging down works immediately.
   /* If the scroll on a particular axis wasn't succesful, reset the
    * initial scroll position to the new mouse co-ordinate. This means
    * when you get to the top of the page, dragging down works immediately.
@@ -2143,11 +2309,12 @@ hildon_pannable_area_timeout (HildonPannableArea * area)
 
   if ((!priv->enabled) || (priv->mode == HILDON_PANNABLE_AREA_MODE_PUSH)) {
     priv->idle_id = 0;
 
   if ((!priv->enabled) || (priv->mode == HILDON_PANNABLE_AREA_MODE_PUSH)) {
     priv->idle_id = 0;
+    g_signal_emit (area, pannable_area_signals[PANNING_FINISHED], 0);
 
     return FALSE;
   }
 
 
     return FALSE;
   }
 
-  if (!priv->clicked) {
+  if (!priv->button_pressed) {
     /* Decelerate gradually when pointer is raised */
     if ((!priv->overshot_dist_y) &&
         (!priv->overshot_dist_x)) {
     /* Decelerate gradually when pointer is raised */
     if ((!priv->overshot_dist_y) &&
         (!priv->overshot_dist_x)) {
@@ -2179,6 +2346,8 @@ hildon_pannable_area_timeout (HildonPannableArea * area)
           priv->vel_y = 0;
           priv->idle_id = 0;
 
           priv->vel_y = 0;
           priv->idle_id = 0;
 
+          g_signal_emit (area, pannable_area_signals[PANNING_FINISHED], 0);
+
           return FALSE;
         }
       }
           return FALSE;
         }
       }
@@ -2191,6 +2360,8 @@ hildon_pannable_area_timeout (HildonPannableArea * area)
 
   hildon_pannable_area_scroll (area, priv->vel_x, priv->vel_y);
 
 
   hildon_pannable_area_scroll (area, priv->vel_x, priv->vel_y);
 
+  gdk_window_process_updates (GTK_WIDGET (area)->window, FALSE);
+
   return TRUE;
 }
 
   return TRUE;
 }
 
@@ -2246,43 +2417,27 @@ hildon_pannable_area_motion_event_scroll (HildonPannableArea *area,
     priv->motion_x = 0;
     priv->motion_y = 0;
 
     priv->motion_x = 0;
     priv->motion_y = 0;
 
-    priv->motion_event_scroll_timeout = gdk_threads_add_timeout
-      ((gint) (1000.0 / (gdouble) MOTION_EVENTS_PER_SECOND),
-       (GSourceFunc) hildon_pannable_area_motion_event_scroll_timeout, area);
+    priv->motion_event_scroll_timeout = gdk_threads_add_timeout_full
+      (G_PRIORITY_HIGH_IDLE + 20,
+       (gint) (1000.0 / (gdouble) MOTION_EVENTS_PER_SECOND),
+       (GSourceFunc) hildon_pannable_area_motion_event_scroll_timeout, area, NULL);
   }
 }
 
   }
 }
 
-static gboolean
-hildon_pannable_area_motion_notify_cb (GtkWidget * widget,
-                                      GdkEventMotion * event)
+static void
+hildon_pannable_area_check_move (HildonPannableArea *area,
+                                 GdkEventMotion * event,
+                                 gdouble *x,
+                                 gdouble *y)
 {
 {
-  HildonPannableArea *area = HILDON_PANNABLE_AREA (widget);
   HildonPannableAreaPrivate *priv = area->priv;
   HildonPannableAreaPrivate *priv = area->priv;
-  gdouble x, y;
-  gdouble delta;
-
-  if (gtk_bin_get_child (GTK_BIN (widget)) == NULL)
-    return TRUE;
-
-  if ((!priv->enabled) || (!priv->clicked) ||
-      ((event->time == priv->last_time) && (priv->last_type == 2))) {
-    gdk_window_get_pointer (widget->window, NULL, NULL, 0);
-    return TRUE;
-  }
-
-  if (priv->last_type == 1) {
-    priv->first_drag = TRUE;
-  }
-
-  x = event->x - priv->x;
-  y = event->y - priv->y;
 
   if (priv->first_drag && (!priv->moved) &&
 
   if (priv->first_drag && (!priv->moved) &&
-      ((ABS (x) > (priv->panning_threshold))
-       || (ABS (y) > (priv->panning_threshold)))) {
+      ((ABS (*x) > (priv->panning_threshold))
+       || (ABS (*y) > (priv->panning_threshold)))) {
     priv->moved = TRUE;
     priv->moved = TRUE;
-    x = 0;
-    y = 0;
+    *x = 0;
+    *y = 0;
 
     if (priv->first_drag) {
         gboolean vscroll_visible;
 
     if (priv->first_drag) {
         gboolean vscroll_visible;
@@ -2352,6 +2507,16 @@ hildon_pannable_area_motion_notify_cb (GtkWidget * widget,
         synth_crossing (priv->child, pos_x, pos_y, event->x_root,
                         event->y_root, event->time, FALSE);
       }
         synth_crossing (priv->child, pos_x, pos_y, event->x_root,
                         event->y_root, event->time, FALSE);
       }
+
+      if (priv->moved) {
+        gboolean result_val;
+
+        g_signal_emit (area,
+                       pannable_area_signals[PANNING_STARTED],
+                       0, &result_val);
+
+        priv->moved = !result_val;
+      }
     }
 
     priv->first_drag = FALSE;
     }
 
     priv->first_drag = FALSE;
@@ -2360,82 +2525,123 @@ hildon_pannable_area_motion_notify_cb (GtkWidget * widget,
        (priv->mode != HILDON_PANNABLE_AREA_MODE_AUTO)) {
 
       if (!priv->idle_id)
        (priv->mode != HILDON_PANNABLE_AREA_MODE_AUTO)) {
 
       if (!priv->idle_id)
-        priv->idle_id = gdk_threads_add_timeout ((gint)
-                                                 (1000.0 / (gdouble) priv->sps),
-                                                 (GSourceFunc)
-                                                 hildon_pannable_area_timeout, area);
+        priv->idle_id = gdk_threads_add_timeout_full
+          (G_PRIORITY_HIGH_IDLE + 20,
+          (gint)(1000.0 / (gdouble) priv->sps),
+          (GSourceFunc)
+          hildon_pannable_area_timeout, area, NULL);
     }
   }
     }
   }
+}
 
 
-  if (priv->moved) {
-    switch (priv->mode) {
-    case HILDON_PANNABLE_AREA_MODE_PUSH:
-      /* Scroll by the amount of pixels the cursor has moved
-       * since the last motion event.
-       */
-      hildon_pannable_area_motion_event_scroll (area, x, y);
+static void
+hildon_pannable_area_handle_move (HildonPannableArea *area,
+                                  GdkEventMotion * event,
+                                  gdouble *x,
+                                  gdouble *y)
+{
+  HildonPannableAreaPrivate *priv = area->priv;
+  gdouble delta;
+
+  switch (priv->mode) {
+  case HILDON_PANNABLE_AREA_MODE_PUSH:
+    /* Scroll by the amount of pixels the cursor has moved
+     * since the last motion event.
+     */
+    hildon_pannable_area_motion_event_scroll (area, *x, *y);
+    priv->x = event->x;
+    priv->y = event->y;
+    break;
+  case HILDON_PANNABLE_AREA_MODE_ACCEL:
+    /* Set acceleration relative to the initial click */
+    priv->ex = event->x;
+    priv->ey = event->y;
+    priv->vel_x = ((*x > 0) ? 1 : -1) *
+      (((ABS (*x) /
+         (gdouble) GTK_WIDGET (area)->allocation.width) *
+        (priv->vmax - priv->vmin)) + priv->vmin);
+    priv->vel_y = ((*y > 0) ? 1 : -1) *
+      (((ABS (*y) /
+         (gdouble) GTK_WIDGET (area)->allocation.height) *
+        (priv->vmax - priv->vmin)) + priv->vmin);
+    break;
+  case HILDON_PANNABLE_AREA_MODE_AUTO:
+
+    delta = event->time - priv->last_time;
+
+    if (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT) {
+      gdouble dist = event->y - priv->y;
+
+      hildon_pannable_area_calculate_velocity (&priv->vel_y,
+                                               delta,
+                                               dist,
+                                               priv->vmax,
+                                               priv->drag_inertia,
+                                               priv->force,
+                                               priv->sps);
+    } else {
+      *y = 0;
+      priv->vel_y = 0;
+    }
+
+    if (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ) {
+      gdouble dist = event->x - priv->x;
+
+      hildon_pannable_area_calculate_velocity (&priv->vel_x,
+                                               delta,
+                                               dist,
+                                               priv->vmax,
+                                               priv->drag_inertia,
+                                               priv->force,
+                                               priv->sps);
+    } else {
+      *x = 0;
+      priv->vel_x = 0;
+    }
+
+    hildon_pannable_area_motion_event_scroll (area, *x, *y);
+
+    if (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ)
       priv->x = event->x;
       priv->x = event->x;
+    if (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT)
       priv->y = event->y;
       priv->y = event->y;
-      break;
-    case HILDON_PANNABLE_AREA_MODE_ACCEL:
-      /* Set acceleration relative to the initial click */
-      priv->ex = event->x;
-      priv->ey = event->y;
-      priv->vel_x = ((x > 0) ? 1 : -1) *
-       (((ABS (x) /
-          (gdouble) widget->allocation.width) *
-         (priv->vmax - priv->vmin)) + priv->vmin);
-      priv->vel_y = ((y > 0) ? 1 : -1) *
-       (((ABS (y) /
-          (gdouble) widget->allocation.height) *
-         (priv->vmax - priv->vmin)) + priv->vmin);
-      break;
-    case HILDON_PANNABLE_AREA_MODE_AUTO:
-
-      delta = event->time - priv->last_time;
 
 
-      if (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT) {
-        gdouble dist = event->y - priv->y;
+    break;
+  default:
+    break;
+  }
+}
 
 
-        hildon_pannable_area_calculate_velocity (&priv->vel_y,
-                                                 delta,
-                                                 dist,
-                                                 priv->vmax,
-                                                 priv->drag_inertia,
-                                                 priv->force,
-                                                 priv->sps);
-      } else {
-        y = 0;
-        priv->vel_y = 0;
-      }
+static gboolean
+hildon_pannable_area_motion_notify_cb (GtkWidget * widget,
+                                      GdkEventMotion * event)
+{
+  HildonPannableArea *area = HILDON_PANNABLE_AREA (widget);
+  HildonPannableAreaPrivate *priv = area->priv;
+  gdouble x, y;
 
 
-      if (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ) {
-        gdouble dist = event->x - priv->x;
+  if (gtk_bin_get_child (GTK_BIN (widget)) == NULL)
+    return TRUE;
 
 
-        hildon_pannable_area_calculate_velocity (&priv->vel_x,
-                                                 delta,
-                                                 dist,
-                                                 priv->vmax,
-                                                 priv->drag_inertia,
-                                                 priv->force,
-                                                 priv->sps);
-      } else {
-        x = 0;
-        priv->vel_x = 0;
-      }
+  if ((!priv->enabled) || (!priv->button_pressed) ||
+      ((event->time == priv->last_time) && (priv->last_type == 2))) {
+    gdk_window_get_pointer (widget->window, NULL, NULL, 0);
+    return TRUE;
+  }
 
 
-      hildon_pannable_area_motion_event_scroll (area, x, y);
+  if (priv->last_type == 1) {
+    priv->first_drag = TRUE;
+  }
 
 
-      if (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ)
-        priv->x = event->x;
-      if (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT)
-        priv->y = event->y;
+  x = event->x - priv->x;
+  y = event->y - priv->y;
 
 
-      break;
+  if (!priv->moved) {
+    hildon_pannable_area_check_move (area, event, &x, &y);
+  }
 
 
-    default:
-      break;
-    }
+  if (priv->moved) {
+    hildon_pannable_area_handle_move (area, event, &x, &y);
   } else if (priv->child) {
     gboolean in;
     gint pos_x, pos_y;
   } else if (priv->child) {
     gboolean in;
     gint pos_x, pos_y;
@@ -2461,6 +2667,8 @@ hildon_pannable_area_motion_notify_cb (GtkWidget * widget,
   if (priv->child) {
     /* Send motion notify to child */
     event = (GdkEventMotion *) gdk_event_copy ((GdkEvent *) event);
   if (priv->child) {
     /* Send motion notify to child */
     event = (GdkEventMotion *) gdk_event_copy ((GdkEvent *) event);
+    /* remove the reference we added with the copy */
+    g_object_unref (priv->event_window);
     event->x = priv->cx + (event->x - priv->ix);
     event->y = priv->cy + (event->y - priv->iy);
     event->window = g_object_ref (priv->child);
     event->x = priv->cx + (event->x - priv->ix);
     event->y = priv->cy + (event->y - priv->iy);
     event->window = g_object_ref (priv->child);
@@ -2494,105 +2702,131 @@ static gboolean
 hildon_pannable_area_button_release_cb (GtkWidget * widget,
                                        GdkEventButton * event)
 {
 hildon_pannable_area_button_release_cb (GtkWidget * widget,
                                        GdkEventButton * event)
 {
-  HildonPannableAreaPrivate *priv = HILDON_PANNABLE_AREA (widget)->priv;
+  HildonPannableArea *area = HILDON_PANNABLE_AREA (widget);
+  HildonPannableAreaPrivate *priv = area->priv;
   gint x, y;
   gint x, y;
+  gdouble dx, dy;
   GdkWindow *child;
   GdkWindow *child;
+  gboolean force_fast = TRUE;
 
   if  (((event->time == priv->last_time) && (priv->last_type == 3))
        || (gtk_bin_get_child (GTK_BIN (widget)) == NULL)
 
   if  (((event->time == priv->last_time) && (priv->last_type == 3))
        || (gtk_bin_get_child (GTK_BIN (widget)) == NULL)
-       || (!priv->clicked) || (!priv->enabled) || (event->button != 1))
+       || (!priv->button_pressed) || (!priv->enabled) || (event->button != 1))
     return TRUE;
 
     return TRUE;
 
-  priv->scroll_indicator_event_interrupt = 0;
-  priv->scroll_delay_counter = priv->scrollbar_fade_delay;
+  /* if last event was a motion-notify we have to check the movement
+     and launch the animation */
+  if (priv->last_type == 2) {
 
 
-  /* move all the way to the last position */
-  if (priv->motion_event_scroll_timeout) {
-    g_source_remove (priv->motion_event_scroll_timeout);
-    hildon_pannable_area_motion_event_scroll_timeout (HILDON_PANNABLE_AREA (widget));
-    priv->motion_x = 0;
-    priv->motion_y = 0;
-  }
+    dx = event->x - priv->x;
+    dy = event->y - priv->y;
 
 
-  if (priv->last_type == 2) {
-    gdouble delta = event->time - priv->last_time;
+    hildon_pannable_area_check_move (area, (GdkEventMotion *) event, &dx, &dy);
 
 
-    if (priv->mov_mode&HILDON_MOVEMENT_MODE_VERT) {
-      gdouble dist = event->y - priv->y;
+    if (priv->moved) {
+      gdouble delta = event->time - priv->last_time;
 
 
-      if (ABS (dist) >= 1.0) {
-        hildon_pannable_area_calculate_velocity (&priv->vel_y,
-                                                 delta,
-                                                 dist,
-                                                 priv->vmax,
-                                                 priv->drag_inertia,
-                                                 priv->force,
-                                                 priv->sps);
+      hildon_pannable_area_handle_move (area, (GdkEventMotion *) event, &dx, &dy);
 
 
-        priv->motion_y = dist;
+      /* move all the way to the last position now */
+      if (priv->motion_event_scroll_timeout) {
+        g_source_remove (priv->motion_event_scroll_timeout);
         hildon_pannable_area_motion_event_scroll_timeout (HILDON_PANNABLE_AREA (widget));
         hildon_pannable_area_motion_event_scroll_timeout (HILDON_PANNABLE_AREA (widget));
-      } else
-        if (delta >= CURSOR_STOPPED_TIMEOUT) {
-          y = 0;
-          priv->vel_y = 0;
-        }
-    }
+        priv->motion_x = 0;
+        priv->motion_y = 0;
+      }
 
 
-    if (priv->mov_mode&HILDON_MOVEMENT_MODE_HORIZ) {
-      gdouble dist = event->x - priv->x;
+      if ((ABS (dx) < 4.0) && (delta >= CURSOR_STOPPED_TIMEOUT))
+        priv->vel_x = 0;
 
 
-      if (ABS (dist) >= 1.0) {
-        hildon_pannable_area_calculate_velocity (&priv->vel_x,
-                                                 delta,
-                                                 dist,
-                                                 priv->vmax,
-                                                 priv->drag_inertia,
-                                                 priv->force,
-                                                 priv->sps);
-        priv->motion_x = dist;
-        hildon_pannable_area_motion_event_scroll_timeout (HILDON_PANNABLE_AREA (widget));
-      } else
-        if (delta >= CURSOR_STOPPED_TIMEOUT) {
-          x = 0;
-          priv->vel_x = 0;
-        }
+      if ((ABS (dy) < 4.0) && (delta >= CURSOR_STOPPED_TIMEOUT))
+        priv->vel_y = 0;
     }
   }
 
     }
   }
 
-  if ((ABS (priv->vel_y) > 1.0)||
-      (ABS (priv->vel_x) > 1.0)) {
-    priv->scroll_indicator_alpha = 1.0;
+  /* If overshoot has been initiated with a finger down, on release set max speed */
+  if (priv->overshot_dist_y != 0) {
+    priv->overshooting_y = priv->bounce_steps; /* Hack to stop a bounce in the finger down case */
+    priv->vel_y = priv->overshot_dist_y * 0.9;
   }
 
   }
 
-  hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget),
-                                            priv->scroll_indicator_alpha);
+  if (priv->overshot_dist_x != 0) {
+    priv->overshooting_x = priv->bounce_steps; /* Hack to stop a bounce in the finger down case */
+    priv->vel_x = priv->overshot_dist_x * 0.9;
+  }
+
+  priv->button_pressed = FALSE;
+
+  /* if widget was moving fast in the panning, increase speed even more */
+  if ((event->time - priv->last_press_time < 125) &&
+      ((ABS (priv->old_vel_x) > priv->vmin) ||
+       (ABS (priv->old_vel_y) > priv->vmin)) &&
+      ((ABS (priv->old_vel_x) > 40) ||
+       (ABS (priv->old_vel_y) > 40)))
+    {
+      gint symbol = 0;
+
+      if (priv->vel_x != 0)
+        symbol = ((priv->vel_x * priv->old_vel_x) > 0) ? 1 : -1;
+
+      priv->vel_x = symbol *
+        (priv->old_vel_x + ((priv->old_vel_x > 0) ? priv->vmax : -priv->vmax));
+
+      symbol = 0;
 
 
-  priv->clicked = FALSE;
+      if (priv->vel_y != 0)
+        symbol = ((priv->vel_y * priv->old_vel_y) > 0) ? 1 : -1;
 
 
-  if (priv->mode == HILDON_PANNABLE_AREA_MODE_AUTO ||
-      priv->mode == HILDON_PANNABLE_AREA_MODE_ACCEL) {
+      priv->vel_y = symbol *
+        (priv->old_vel_y + ((priv->old_vel_y > 0) ? priv->vmax : -priv->vmax));
 
 
-    /* If overshoot has been initiated with a finger down, on release set max speed */
-    if (priv->overshot_dist_y != 0) {
-      priv->overshooting_y = priv->bounce_steps; /* Hack to stop a bounce in the finger down case */
-      priv->vel_y = priv->vmax_overshooting;
+      force_fast = FALSE;
     }
 
     }
 
-    if (priv->overshot_dist_x != 0) {
-      priv->overshooting_x = priv->bounce_steps; /* Hack to stop a bounce in the finger down case */
-      priv->vel_x = priv->vmax_overshooting;
+  if  ((ABS (priv->vel_y) >= priv->vmin) ||
+       (ABS (priv->vel_x) >= priv->vmin)) {
+
+    /* we have to move because we are in overshooting position*/
+    if (!priv->moved) {
+      gboolean result_val;
+
+      g_signal_emit (area,
+                     pannable_area_signals[PANNING_STARTED],
+                     0, &result_val);
     }
 
     }
 
-    if ((ABS (priv->vel_y) >= priv->vmin) ||
-        (ABS (priv->vel_x) >= priv->vmin)) {
+    priv->scroll_indicator_alpha = 1.0;
 
 
-      if (!priv->idle_id)
-        priv->idle_id = gdk_threads_add_timeout ((gint) (1000.0 / (gdouble) priv->sps),
-                                                 (GSourceFunc)
-                                                 hildon_pannable_area_timeout, widget);
+    if (force_fast) {
+      if (ABS (priv->vel_x) > MAX_SPEED_THRESHOLD)
+        priv->vel_x = (priv->vel_x > 0) ? priv->vmax : -priv->vmax;
+
+      if (ABS (priv->vel_y) > MAX_SPEED_THRESHOLD)
+        priv->vel_y = (priv->vel_y > 0) ? priv->vmax : -priv->vmax;
     }
     }
+
+    if (!priv->idle_id)
+      priv->idle_id = gdk_threads_add_timeout_full (G_PRIORITY_HIGH_IDLE + 20,
+                                                   (gint) (1000.0 / (gdouble) priv->sps),
+                                                   (GSourceFunc) hildon_pannable_area_timeout,
+                                                   widget, NULL);
+  } else {
+    if (priv->center_on_child_focus_pending) {
+      hildon_pannable_area_center_on_child_focus (area);
+    }
+
+    if (priv->moved)
+      g_signal_emit (widget, pannable_area_signals[PANNING_FINISHED], 0);
   }
 
   }
 
+  area->priv->center_on_child_focus_pending = FALSE;
+
+  priv->scroll_indicator_event_interrupt = 0;
+  priv->scroll_delay_counter = priv->scrollbar_fade_delay;
+
+  hildon_pannable_area_launch_fade_timeout (HILDON_PANNABLE_AREA (widget),
+                                            priv->scroll_indicator_alpha);
+
   priv->last_time = event->time;
   priv->last_type = 3;
 
   priv->last_time = event->time;
   priv->last_type = 3;
 
@@ -2606,6 +2840,8 @@ hildon_pannable_area_button_release_cb (GtkWidget * widget,
                                      event->x, event->y, &x, &y, GDK_BUTTON_RELEASE_MASK);
 
   event = (GdkEventButton *) gdk_event_copy ((GdkEvent *) event);
                                      event->x, event->y, &x, &y, GDK_BUTTON_RELEASE_MASK);
 
   event = (GdkEventButton *) gdk_event_copy ((GdkEvent *) event);
+  /* remove the reference we added with the copy */
+  g_object_unref (priv->event_window);
   event->x = x;
   event->y = y;
 
   event->x = x;
   event->y = y;
 
@@ -2616,6 +2852,10 @@ hildon_pannable_area_button_release_cb (GtkWidget * widget,
     /* Send synthetic leave event */
     synth_crossing (priv->child, x, y, event->x_root,
                    event->y_root, event->time, FALSE);
     /* Send synthetic leave event */
     synth_crossing (priv->child, x, y, event->x_root,
                    event->y_root, event->time, FALSE);
+    /* insure no click will happen for widgets that do not handle
+       leave-notify */
+    event->x = -16384;
+    event->y = -16384;
     /* Send synthetic button release event */
     ((GdkEventAny *) event)->window = g_object_ref (priv->child);
     gdk_event_put ((GdkEvent *) event);
     /* Send synthetic button release event */
     ((GdkEventAny *) event)->window = g_object_ref (priv->child);
     gdk_event_put ((GdkEvent *) event);
@@ -2667,6 +2907,8 @@ hildon_pannable_area_scroll_cb (GtkWidget *widget,
       gtk_widget_queue_resize (GTK_WIDGET (widget));
     }
 
       gtk_widget_queue_resize (GTK_WIDGET (widget));
     }
 
+    g_signal_emit (widget, pannable_area_signals[PANNING_FINISHED], 0);
+
     g_source_remove (priv->idle_id);
     priv->idle_id = 0;
   }
     g_source_remove (priv->idle_id);
     priv->idle_id = 0;
   }
@@ -2726,6 +2968,39 @@ hildon_pannable_area_add (GtkContainer *container, GtkWidget *child)
   }
 }
 
   }
 }
 
+/* call this function if you are not panning */
+static void
+hildon_pannable_area_center_on_child_focus      (HildonPannableArea *area)
+{
+  GtkWidget *focused_child = NULL;
+  GtkWidget *window = NULL;
+
+  window = gtk_widget_get_toplevel (GTK_WIDGET (area));
+
+  if (GTK_WIDGET_TOPLEVEL (window)) {
+    focused_child = gtk_window_get_focus (GTK_WINDOW (window));
+  }
+
+  if (focused_child) {
+    hildon_pannable_area_scroll_to_child (area, focused_child);
+  }
+}
+
+static void
+hildon_pannable_area_set_focus_child            (GtkContainer     *container,
+                                                 GtkWidget        *child)
+{
+  HildonPannableArea *area = HILDON_PANNABLE_AREA (container);
+
+  if (!area->priv->center_on_child_focus) {
+    return;
+  }
+
+  if (GTK_IS_WIDGET (child)) {
+    area->priv->center_on_child_focus_pending = TRUE;
+  }
+}
+
 static void
 hildon_pannable_area_remove (GtkContainer *container, GtkWidget *child)
 {
 static void
 hildon_pannable_area_remove (GtkContainer *container, GtkWidget *child)
 {
@@ -2743,6 +3018,32 @@ hildon_pannable_area_remove (GtkContainer *container, GtkWidget *child)
   GTK_CONTAINER_CLASS (hildon_pannable_area_parent_class)->remove (container, child);
 }
 
   GTK_CONTAINER_CLASS (hildon_pannable_area_parent_class)->remove (container, child);
 }
 
+/*
+ * This method calculates a factor necessary to determine the initial distance
+ * to jump in hildon_pannable_area_scroll_to(). For fixed time and frames per
+ * second, we know in how many frames 'n' we need to reach the destination
+ * point. We know that, for a distance d,
+ *
+ *   d = d_0 + d_1 + ... + d_n
+ *
+ * where d_i is the distance travelled in the i-th frame and decel_factor is
+ * the deceleration factor. This can be rewritten as
+ *
+ *   d = d_0 + (d_0 * decel_factor) + ... + (d_n-1 * decel_factor),
+ *
+ * since the distance travelled on each frame is the distance travelled in the
+ * previous frame reduced by the deceleration factor. Reducing this and
+ * factoring d_0 out, we get
+ *
+ *   d = d_0 (1 + decel_factor + ... + decel_factor^(n-1)).
+ *
+ * Since the sum is independent of the distance to be travelled, we can define
+ * vel_factor as
+ *
+ *   vel_factor = 1 + decel_factor + ... + decel_factor^(n-1).
+ *
+ * That's the gem we calculate in this method.
+ */
 static void
 hildon_pannable_calculate_vel_factor (HildonPannableArea * self)
 {
 static void
 hildon_pannable_calculate_vel_factor (HildonPannableArea * self)
 {
@@ -2925,7 +3226,9 @@ hildon_pannable_area_scroll_to (HildonPannableArea *area,
   g_return_if_fail (x < width || y < height);
 
   if ((x > -1)&&(hscroll_visible)) {
   g_return_if_fail (x < width || y < height);
 
   if ((x > -1)&&(hscroll_visible)) {
-    priv->scroll_to_x = x - priv->hadjust->page_size/2;
+    priv->scroll_to_x = CLAMP (x - priv->hadjust->page_size/2,
+                               priv->hadjust->lower,
+                               priv->hadjust->upper - priv->hadjust->page_size);
     dist_x = priv->scroll_to_x - priv->hadjust->value;
     if (dist_x == 0) {
       priv->scroll_to_x = -1;
     dist_x = priv->scroll_to_x - priv->hadjust->value;
     if (dist_x == 0) {
       priv->scroll_to_x = -1;
@@ -2937,7 +3240,9 @@ hildon_pannable_area_scroll_to (HildonPannableArea *area,
   }
 
   if ((y > -1)&&(vscroll_visible)) {
   }
 
   if ((y > -1)&&(vscroll_visible)) {
-    priv->scroll_to_y = y - priv->vadjust->page_size/2;
+    priv->scroll_to_y = CLAMP (y - priv->vadjust->page_size/2,
+                               priv->vadjust->lower,
+                               priv->vadjust->upper - priv->vadjust->page_size);
     dist_y = priv->scroll_to_y - priv->vadjust->value;
     if (dist_y == 0) {
       priv->scroll_to_y = -1;
     dist_y = priv->scroll_to_y - priv->vadjust->value;
     if (dist_y == 0) {
       priv->scroll_to_y = -1;
@@ -2948,16 +3253,17 @@ hildon_pannable_area_scroll_to (HildonPannableArea *area,
     priv->scroll_to_y = y;
   }
 
     priv->scroll_to_y = y;
   }
 
-  if ((priv->scroll_to_y == -1) && (priv->scroll_to_y == -1)) {
+  if ((priv->scroll_to_y == -1) && (priv->scroll_to_x == -1)) {
     return;
   }
 
   hildon_pannable_area_launch_fade_timeout (area, 1.0);
 
   if (!priv->idle_id)
     return;
   }
 
   hildon_pannable_area_launch_fade_timeout (area, 1.0);
 
   if (!priv->idle_id)
-    priv->idle_id = gdk_threads_add_timeout ((gint) (1000.0 / (gdouble) priv->sps),
-                                             (GSourceFunc)
-                                             hildon_pannable_area_timeout, area);
+    priv->idle_id = gdk_threads_add_timeout_full (G_PRIORITY_HIGH_IDLE + 20,
+                                                 (gint) (1000.0 / (gdouble) priv->sps),
+                                                 (GSourceFunc) hildon_pannable_area_timeout,
+                                                 area, NULL);
 }
 
 /**
 }
 
 /**
@@ -2985,6 +3291,7 @@ hildon_pannable_area_jump_to (HildonPannableArea *area,
 {
   HildonPannableAreaPrivate *priv;
   gint width, height;
 {
   HildonPannableAreaPrivate *priv;
   gint width, height;
+  gdouble hv, vv;
 
   g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
   g_return_if_fail (GTK_WIDGET_REALIZED (area));
 
   g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
   g_return_if_fail (GTK_WIDGET_REALIZED (area));
@@ -3001,26 +3308,33 @@ hildon_pannable_area_jump_to (HildonPannableArea *area,
 
   g_return_if_fail (x < width || y < height);
 
 
   g_return_if_fail (x < width || y < height);
 
+  hv = priv->hadjust->value;
+  vv = priv->vadjust->value;
+
   if (x != -1) {
     gdouble jump_to = x - priv->hadjust->page_size/2;
 
   if (x != -1) {
     gdouble jump_to = x - priv->hadjust->page_size/2;
 
-    if (jump_to > priv->hadjust->upper - priv->hadjust->page_size) {
-      jump_to = priv->hadjust->upper - priv->hadjust->page_size;
-    }
-
-    gtk_adjustment_set_value (priv->hadjust, jump_to);
+    priv->hadjust->value = CLAMP (jump_to,
+                                  priv->hadjust->lower,
+                                  priv->hadjust->upper -
+                                  priv->hadjust->page_size);
   }
 
   if (y != -1) {
     gdouble jump_to =  y - priv->vadjust->page_size/2;
 
   }
 
   if (y != -1) {
     gdouble jump_to =  y - priv->vadjust->page_size/2;
 
-    if (jump_to > priv->vadjust->upper - priv->vadjust->page_size) {
-      jump_to = priv->vadjust->upper - priv->vadjust->page_size;
-    }
-
-    gtk_adjustment_set_value (priv->vadjust, jump_to);
+    priv->vadjust->value = CLAMP (jump_to,
+                                  priv->vadjust->lower,
+                                  priv->vadjust->upper -
+                                  priv->vadjust->page_size);
   }
 
   }
 
+  if (hv != priv->hadjust->value)
+    gtk_adjustment_value_changed (priv->hadjust);
+
+  if (vv != priv->vadjust->value)
+    gtk_adjustment_value_changed (priv->vadjust);
+
   priv->scroll_indicator_alpha = 1.0;
 
   if (priv->scroll_indicator_timeout) {
   priv->scroll_indicator_alpha = 1.0;
 
   if (priv->scroll_indicator_timeout) {
@@ -3041,6 +3355,7 @@ hildon_pannable_area_jump_to (HildonPannableArea *area,
       gtk_widget_queue_resize (GTK_WIDGET (area));
     }
 
       gtk_widget_queue_resize (GTK_WIDGET (area));
     }
 
+    g_signal_emit (area, pannable_area_signals[PANNING_FINISHED], 0);
     g_source_remove (priv->idle_id);
     priv->idle_id = 0;
   }
     g_source_remove (priv->idle_id);
     priv->idle_id = 0;
   }
@@ -3277,3 +3592,44 @@ hildon_pannable_area_set_size_request_policy (HildonPannableArea *area,
   g_object_notify (G_OBJECT (area), "size-request-policy");
 }
 
   g_object_notify (G_OBJECT (area), "size-request-policy");
 }
 
+/**
+ * hildon_pannable_area_get_center_on_child_focus
+ * @area: A #HildonPannableArea
+ *
+ * Gets the @area #HildonPannableArea:center-on-child-focus property
+ * value.
+ *
+ * See #HildonPannableArea:center-on-child-focus for more information.
+ *
+ * Returns: the @area #HildonPannableArea:center-on-child-focus value
+ *
+ * Since: 2.2
+ **/
+gboolean
+hildon_pannable_area_get_center_on_child_focus  (HildonPannableArea *area)
+{
+  g_return_val_if_fail (HILDON_IS_PANNABLE_AREA (area), FALSE);
+
+  return area->priv->center_on_child_focus;
+}
+
+/**
+ * hildon_pannable_area_set_center_on_child_focus
+ * @area: A #HildonPannableArea
+ * @value: the new value
+ *
+ * Sets the @area #HildonPannableArea:center-on-child-focus property
+ * to @value.
+ *
+ * See #HildonPannableArea:center-on-child-focus for more information.
+ *
+ * Since: 2.2
+ **/
+void
+hildon_pannable_area_set_center_on_child_focus  (HildonPannableArea *area,
+                                                 gboolean value)
+{
+  g_return_if_fail (HILDON_IS_PANNABLE_AREA (area));
+
+  area->priv->center_on_child_focus = value;
+}