More tuning for the pannable large lists kinetics.
[hildon] / hildon / hildon-pannable-area.c
index 78f21f7..219810a 100644 (file)
@@ -56,8 +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 MAX_SPEED_THRESHOLD 250
+#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
 
@@ -78,6 +78,7 @@ struct _HildonPannableAreaPrivate {
   gboolean enabled;
   gboolean button_pressed;
   guint32 last_time;   /* Last event time, to stop infinite loops */
   gboolean enabled;
   gboolean button_pressed;
   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;
@@ -97,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;
@@ -141,12 +144,18 @@ struct _HildonPannableAreaPrivate {
   GtkPolicyType hscrollbar_policy;
 
   GdkGC *scrollbars_gc;
   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
 };
 
@@ -178,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
 };
 
@@ -191,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);
@@ -289,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
@@ -321,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;
@@ -400,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));
 
@@ -421,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));
 
@@ -461,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));
 
@@ -569,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",
@@ -629,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
@@ -643,6 +714,7 @@ hildon_pannable_area_init (HildonPannableArea * area)
   priv->moved = FALSE;
   priv->button_pressed = FALSE;
   priv->last_time = 0;
   priv->moved = FALSE;
   priv->button_pressed = FALSE;
   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;
@@ -654,6 +726,8 @@ 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_alpha = 0.0;
   priv->scroll_indicator_timeout = 0;
   priv->motion_event_scroll_timeout = 0;
@@ -669,8 +743,10 @@ hildon_pannable_area_init (HildonPannableArea * area)
   priv->last_in = TRUE;
   priv->x_offset = 0;
   priv->y_offset = 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));
@@ -781,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);
   }
@@ -896,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);
@@ -908,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,
@@ -999,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;
 
@@ -1015,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)
 {
@@ -1022,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);
@@ -1146,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);
@@ -1193,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);
 }
 
@@ -1413,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],
@@ -1437,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,
@@ -1459,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],
@@ -1484,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,
@@ -1526,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
@@ -1647,7 +1745,7 @@ 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
 
   if (G_UNLIKELY (priv->initial_effect)) {
 #endif
 
   if (G_UNLIKELY (priv->initial_effect)) {
@@ -1790,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 {
@@ -1843,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) &&
@@ -1852,10 +1951,11 @@ 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->last_type = 1;
 
   priv->scroll_to_x = -1;
@@ -1884,8 +1984,15 @@ hildon_pannable_area_button_press_cb (GtkWidget * widget,
   priv->button_pressed = TRUE;
 
   /* Stop scrolling on mouse-down (so you can flick, then hold to stop) */
   priv->button_pressed = TRUE;
 
   /* 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) {
 
@@ -1897,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;
@@ -2083,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);
@@ -2099,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);
@@ -2200,6 +2309,7 @@ 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;
   }
@@ -2236,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;
         }
       }
@@ -2248,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;
 }
 
@@ -2303,9 +2417,10 @@ 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);
   }
 }
 
   }
 }
 
@@ -2392,6 +2507,16 @@ hildon_pannable_area_check_move (HildonPannableArea *area,
         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;
@@ -2400,10 +2525,11 @@ hildon_pannable_area_check_move (HildonPannableArea *area,
        (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);
     }
   }
 }
     }
   }
 }
@@ -2541,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);
@@ -2579,6 +2707,7 @@ hildon_pannable_area_button_release_cb (GtkWidget * widget,
   gint x, y;
   gdouble dx, dy;
   GdkWindow *child;
   gint x, y;
   gdouble dx, dy;
   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)
@@ -2618,33 +2747,80 @@ hildon_pannable_area_button_release_cb (GtkWidget * widget,
   /* 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 */
   /* 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;
+    priv->vel_y = priv->overshot_dist_y * 0.9;
   }
 
   if (priv->overshot_dist_x != 0) {
     priv->overshooting_x = priv->bounce_steps; /* Hack to stop a bounce in the finger down case */
   }
 
   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;
+    priv->vel_x = priv->overshot_dist_x * 0.9;
   }
 
   priv->button_pressed = FALSE;
 
   }
 
   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;
+
+      if (priv->vel_y != 0)
+        symbol = ((priv->vel_y * priv->old_vel_y) > 0) ? 1 : -1;
+
+      priv->vel_y = symbol *
+        (priv->old_vel_y + ((priv->old_vel_y > 0) ? priv->vmax : -priv->vmax));
+
+      force_fast = FALSE;
+    }
+
   if  ((ABS (priv->vel_y) >= priv->vmin) ||
        (ABS (priv->vel_x) >= priv->vmin)) {
 
   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);
+    }
+
     priv->scroll_indicator_alpha = 1.0;
 
     priv->scroll_indicator_alpha = 1.0;
 
-    if (ABS (priv->vel_x) > MAX_SPEED_THRESHOLD)
-      priv->vel_x = (priv->vel_x > 0) ? priv->vmax : -priv->vmax;
+    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 (ABS (priv->vel_y) > MAX_SPEED_THRESHOLD)
+        priv->vel_y = (priv->vel_y > 0) ? priv->vmax : -priv->vmax;
+    }
 
     if (!priv->idle_id)
 
     if (!priv->idle_id)
-      priv->idle_id = gdk_threads_add_timeout ((gint) (1000.0 / (gdouble) priv->sps),
-                                               (GSourceFunc)
-                                               hildon_pannable_area_timeout, widget);
+      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;
 
   priv->scroll_indicator_event_interrupt = 0;
   priv->scroll_delay_counter = priv->scrollbar_fade_delay;
 
@@ -2664,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;
 
@@ -2674,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);
@@ -2725,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;
   }
@@ -2784,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)
 {
@@ -3009,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;
@@ -3021,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;
@@ -3032,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);
 }
 
 /**
 }
 
 /**
@@ -3133,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;
   }
@@ -3369,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;
+}