[elm] New scrollable interface in.
authorGustavo Lima Chaves <glima@profusion.mobi>
Wed, 4 Jul 2012 21:41:01 +0000 (21:41 +0000)
committerGustavo Lima Chaves <glima@profusion.mobi>
Wed, 4 Jul 2012 21:41:01 +0000 (21:41 +0000)
This is meant to be used for all widgets implementing scrolling views.
An Elementary interface adds a given behaviour to whichever widget,
from any class.

SVN revision: 73307

src/lib/Makefile.am
src/lib/elm_interface_scrollable.c [new file with mode: 0644]
src/lib/elm_interface_scrollable.h [new file with mode: 0644]
src/lib/elm_widget.c
src/lib/elm_widget.h

index 7a6af4b..44d7f7c 100644 (file)
@@ -41,6 +41,7 @@ Elementary_Cursor.h
 includesdir = $(includedir)/elementary-@VMAJ@
 
 includesunstable_HEADERS = \
+elm_interface_scrollable.h \
 elm_widget.h \
 elm_widget_button.h \
 elm_widget_container.h \
@@ -186,6 +187,7 @@ elm_hover.c \
 elm_icon.c \
 elm_image.c \
 elm_index.c \
+elm_interface_scrollable.c \
 elm_inwin.c \
 elm_label.c \
 elm_layout.c \
diff --git a/src/lib/elm_interface_scrollable.c b/src/lib/elm_interface_scrollable.c
new file mode 100644 (file)
index 0000000..6bb76a9
--- /dev/null
@@ -0,0 +1,3744 @@
+#include <Elementary.h>
+#include "elm_priv.h"
+#include "elm_interface_scrollable.h"
+
+static const char PAN_SMART_NAME[] = "elm_pan";
+
+#define ELM_PAN_DATA_GET(o, sd) \
+  Elm_Pan_Smart_Data * sd = evas_object_smart_data_get(o)
+
+#define ELM_PAN_DATA_GET_OR_RETURN(o, ptr)          \
+  ELM_PAN_DATA_GET(o, ptr);                         \
+  if (!ptr)                                         \
+    {                                               \
+       CRITICAL("No smart data for object %p (%s)", \
+                o, evas_object_type_get(o));        \
+       return;                                      \
+    }
+
+#define ELM_PAN_DATA_GET_OR_RETURN_VAL(o, ptr, val) \
+  ELM_PAN_DATA_GET(o, ptr);                         \
+  if (!ptr)                                         \
+    {                                               \
+       CRITICAL("No smart data for object %p (%s)", \
+                o, evas_object_type_get(o));        \
+       return val;                                  \
+    }
+
+static const char SIG_CHANGED[] = "changed";
+static const Evas_Smart_Cb_Description _smart_callbacks[] = {
+   {SIG_CHANGED, ""},
+   {NULL, NULL}
+};
+
+ELM_INTERNAL_SMART_SUBCLASS_NEW
+  (PAN_SMART_NAME, _elm_pan, Elm_Pan_Smart_Class, Evas_Smart_Class,
+  evas_object_smart_clipped_class_get, _smart_callbacks);
+
+static void _elm_pan_content_set(Evas_Object *, Evas_Object *);
+
+EAPI const Elm_Pan_Smart_Class *
+elm_pan_smart_class_get(void)
+{
+   static Elm_Pan_Smart_Class _sc =
+     ELM_PAN_SMART_CLASS_INIT_NAME_VERSION(PAN_SMART_NAME);
+   static const Elm_Pan_Smart_Class *class = NULL;
+   Evas_Smart_Class *esc = (Evas_Smart_Class *)&_sc;
+
+   if (class)
+     return class;
+
+   _elm_pan_smart_set(&_sc);
+   esc->callbacks = _smart_callbacks;
+   class = &_sc;
+
+   return class;
+}
+
+static void
+_elm_pan_update(Elm_Pan_Smart_Data *psd)
+{
+   if (!psd->gravity_x && !psd->gravity_y)
+     {
+        evas_object_move(psd->content, psd->x - psd->px, psd->y - psd->py);
+        return;
+     }
+
+   if ((!psd->px) && (!psd->py))
+     {
+        psd->px = psd->delta_posx * psd->gravity_x;
+        psd->py = psd->delta_posy * psd->gravity_y;
+     }
+   psd->delta_posx += psd->content_w - psd->prev_cw;
+   psd->prev_cw = psd->content_w;
+   psd->delta_posy += psd->content_h - psd->prev_ch;
+   psd->prev_ch = psd->content_h;
+
+   evas_object_move(psd->content, psd->x - psd->px, psd->y - psd->py);
+   psd->px = psd->delta_posx * psd->gravity_x;
+   psd->py = psd->delta_posy * psd->gravity_y;
+}
+
+static void
+_elm_pan_smart_add(Evas_Object *obj)
+{
+   const Evas_Smart_Class *sc;
+   const Evas_Smart *smart;
+
+   EVAS_SMART_DATA_ALLOC(obj, Elm_Pan_Smart_Data);
+
+   _elm_pan_parent_sc->add(obj);
+
+   priv->self = obj;
+
+   priv->x = 0;
+   priv->y = 0;
+   priv->w = 0;
+   priv->h = 0;
+   priv->gravity_x = 0.0;
+   priv->gravity_y = 0.0;
+
+   smart = evas_object_smart_smart_get(obj);
+   sc = evas_smart_class_get(smart);
+   priv->api = (const Elm_Pan_Smart_Class *)sc;
+}
+
+static void
+_elm_pan_smart_del(Evas_Object *obj)
+{
+   _elm_pan_content_set(obj, NULL);
+
+   _elm_pan_parent_sc->del(obj);
+}
+
+static void
+_elm_pan_smart_move(Evas_Object *obj,
+                    Evas_Coord x,
+                    Evas_Coord y)
+{
+   ELM_PAN_DATA_GET(obj, psd);
+
+   /* we don't want the clipped smart object version here */
+
+   psd->x = x;
+   psd->y = y;
+
+   _elm_pan_update(psd);
+}
+
+static void
+_elm_pan_smart_resize(Evas_Object *obj,
+                      Evas_Coord w,
+                      Evas_Coord h)
+{
+   ELM_PAN_DATA_GET(obj, psd);
+
+   psd->w = w;
+   psd->h = h;
+
+   _elm_pan_update(psd);
+   evas_object_smart_callback_call(psd->self, SIG_CHANGED, NULL);
+}
+
+static void
+_elm_pan_smart_show(Evas_Object *obj)
+{
+   ELM_PAN_DATA_GET(obj, psd);
+
+   _elm_pan_parent_sc->show(obj);
+
+   if (psd->content)
+     evas_object_show(psd->content);
+}
+
+static void
+_elm_pan_smart_hide(Evas_Object *obj)
+{
+   ELM_PAN_DATA_GET(obj, psd);
+
+   _elm_pan_parent_sc->hide(obj);
+
+   if (psd->content)
+     evas_object_hide(psd->content);
+}
+
+static void
+_elm_pan_pos_set(Evas_Object *obj,
+                 Evas_Coord x,
+                 Evas_Coord y)
+{
+   ELM_PAN_DATA_GET(obj, psd);
+
+   if ((x == psd->px) && (y == psd->py)) return;
+   psd->px = x;
+   psd->py = y;
+
+   _elm_pan_update(psd);
+   evas_object_smart_callback_call(psd->self, SIG_CHANGED, NULL);
+}
+
+static void
+_elm_pan_pos_get(const Evas_Object *obj,
+                 Evas_Coord *x,
+                 Evas_Coord *y)
+{
+   ELM_PAN_DATA_GET(obj, psd);
+
+   if (x) *x = psd->px;
+   if (y) *y = psd->py;
+}
+
+static void
+_elm_pan_pos_max_get(const Evas_Object *obj,
+                     Evas_Coord *x,
+                     Evas_Coord *y)
+{
+   ELM_PAN_DATA_GET(obj, psd);
+
+   if (x)
+     {
+        if (psd->w < psd->content_w) *x = psd->content_w - psd->w;
+        else *x = 0;
+     }
+   if (y)
+     {
+        if (psd->h < psd->content_h) *y = psd->content_h - psd->h;
+        else *y = 0;
+     }
+}
+
+static void
+_elm_pan_pos_min_get(const Evas_Object *obj __UNUSED__,
+                     Evas_Coord *x,
+                     Evas_Coord *y)
+{
+   if (x)
+     *x = 0;
+   if (y)
+     *y = 0;
+}
+
+static void
+_elm_pan_content_size_get(const Evas_Object *obj,
+                          Evas_Coord *w,
+                          Evas_Coord *h)
+{
+   ELM_PAN_DATA_GET(obj, psd);
+
+   if (w) *w = psd->content_w;
+   if (h) *h = psd->content_h;
+}
+
+static void
+_elm_pan_gravity_set(Evas_Object *obj,
+                     double x,
+                     double y)
+{
+   ELM_PAN_DATA_GET(obj, psd);
+
+   psd->gravity_x = x;
+   psd->gravity_y = y;
+   psd->prev_cw = psd->content_w;
+   psd->prev_ch = psd->content_h;
+   psd->delta_posx = 0;
+   psd->delta_posy = 0;
+}
+
+static void
+_elm_pan_gravity_get(const Evas_Object *obj,
+                     double *x,
+                     double *y)
+{
+   ELM_PAN_DATA_GET(obj, psd);
+
+   if (x) *x = psd->gravity_x;
+   if (y) *y = psd->gravity_y;
+}
+
+static void
+_elm_pan_smart_set_user(Elm_Pan_Smart_Class *sc)
+{
+   sc->base.add = _elm_pan_smart_add;
+   sc->base.del = _elm_pan_smart_del;
+   sc->base.move = _elm_pan_smart_move;
+   sc->base.resize = _elm_pan_smart_resize;
+   sc->base.show = _elm_pan_smart_show;
+   sc->base.hide = _elm_pan_smart_hide;
+
+   sc->pos_set = _elm_pan_pos_set;
+   sc->pos_get = _elm_pan_pos_get;
+   sc->pos_max_get = _elm_pan_pos_max_get;
+   sc->pos_min_get = _elm_pan_pos_min_get;
+   sc->content_size_get = _elm_pan_content_size_get;
+   sc->gravity_set = _elm_pan_gravity_set;
+   sc->gravity_get = _elm_pan_gravity_get;
+}
+
+static Evas_Object *
+_elm_pan_add(Evas *evas)
+{
+   return evas_object_smart_add(evas, _elm_pan_smart_class_new());
+}
+
+static void
+_elm_pan_content_del_cb(void *data,
+                        Evas *e __UNUSED__,
+                        Evas_Object *obj __UNUSED__,
+                        void *event_info __UNUSED__)
+{
+   Elm_Pan_Smart_Data *psd;
+
+   psd = data;
+   psd->content = NULL;
+   psd->content_w = psd->content_h = psd->px = psd->py =
+           psd->prev_cw = psd->prev_ch = psd->delta_posx = psd->delta_posy = 0;
+   evas_object_smart_callback_call(psd->self, SIG_CHANGED, NULL);
+}
+
+static void
+_elm_pan_content_resize_cb(void *data,
+                           Evas *e __UNUSED__,
+                           Evas_Object *obj __UNUSED__,
+                           void *event_info __UNUSED__)
+{
+   Elm_Pan_Smart_Data *psd;
+   Evas_Coord w, h;
+
+   psd = data;
+   evas_object_geometry_get(psd->content, NULL, NULL, &w, &h);
+   if ((w != psd->content_w) || (h != psd->content_h))
+     {
+        psd->content_w = w;
+        psd->content_h = h;
+        _elm_pan_update(psd);
+     }
+   evas_object_smart_callback_call(psd->self, SIG_CHANGED, NULL);
+}
+
+static void
+_elm_pan_content_set(Evas_Object *obj,
+                     Evas_Object *content)
+{
+   Evas_Coord w, h;
+
+   ELM_PAN_DATA_GET(obj, psd);
+
+   if (content == psd->content) return;
+   if (psd->content)
+     {
+        evas_object_smart_member_del(psd->content);
+        evas_object_event_callback_del_full
+          (psd->content, EVAS_CALLBACK_DEL, _elm_pan_content_del_cb, psd);
+        evas_object_event_callback_del_full
+          (psd->content, EVAS_CALLBACK_RESIZE, _elm_pan_content_resize_cb,
+          psd);
+        psd->content = NULL;
+     }
+   if (!content) goto end;
+
+   psd->content = content;
+   evas_object_smart_member_add(psd->content, psd->self);
+   evas_object_geometry_get(psd->content, NULL, NULL, &w, &h);
+   psd->content_w = w;
+   psd->content_h = h;
+   evas_object_event_callback_add
+     (content, EVAS_CALLBACK_DEL, _elm_pan_content_del_cb, psd);
+   evas_object_event_callback_add
+     (content, EVAS_CALLBACK_RESIZE, _elm_pan_content_resize_cb, psd);
+
+   if (evas_object_visible_get(psd->self))
+     evas_object_show(psd->content);
+   else
+     evas_object_hide(psd->content);
+
+   _elm_pan_update(psd);
+
+end:
+   evas_object_smart_callback_call(psd->self, SIG_CHANGED, NULL);
+}
+
+/* pan smart object on top, scroller interface on bottom */
+/* ============================================================ */
+
+static const char SCROLL_SMART_NAME[] = "elm_scroll";
+
+#define ELM_SCROLL_IFACE_DATA_GET(o, sid)     \
+  Elm_Scrollable_Smart_Interface_Data * sid = \
+    evas_object_smart_interface_data_get(o, &(ELM_SCROLLABLE_IFACE.base))
+
+#define ELM_SCROLL_IFACE_DATA_GET_OR_RETURN(o, ptr)     \
+  ELM_SCROLL_IFACE_DATA_GET(o, ptr);                    \
+  if (!ptr)                                             \
+    {                                                   \
+       CRITICAL("No interface data for object %p (%s)", \
+                o, evas_object_type_get(o));            \
+       return;                                          \
+    }
+
+#define ELM_SCROLL_IFACE_DATA_GET_OR_RETURN_VAL(o, ptr, val) \
+  ELM_SCROLL_IFACE_DATA_GET(o, ptr);                         \
+  if (!ptr)                                                  \
+    {                                                        \
+       CRITICAL("No interface data for object %p (%s)",      \
+                o, evas_object_type_get(o));                 \
+       return val;                                           \
+    }
+
+static void _elm_scroll_scroll_bar_size_adjust(
+  Elm_Scrollable_Smart_Interface_Data *);
+static void _elm_scroll_wanted_region_set(Evas_Object *);
+static void _elm_scroll_content_pos_get(const Evas_Object *,
+                                        Evas_Coord *,
+                                        Evas_Coord *);
+static void _elm_scroll_content_pos_set(Evas_Object *,
+                                        Evas_Coord,
+                                        Evas_Coord);
+
+#define LEFT   0
+#define RIGHT  1
+#define UP     2
+#define DOWN   3
+#define EVTIME 1
+//#define SCROLLDBG 1
+
+static int
+_elm_scroll_scroll_bar_h_visibility_adjust(
+  Elm_Scrollable_Smart_Interface_Data *sid)
+{
+   int scroll_h_vis_change = 0;
+   Evas_Coord w, vw = 0, vh = 0;
+
+   if (!sid->edje_obj) return 0;
+
+   w = sid->content_info.w;
+   if (sid->pan_obj)
+     evas_object_geometry_get(sid->pan_obj, NULL, NULL, &vw, &vh);
+   if (sid->hbar_visible)
+     {
+        if (sid->hbar_flags == ELM_SCROLLER_POLICY_AUTO)
+          {
+             if ((sid->content) || (sid->extern_pan))
+               {
+                  if (w <= vw)
+                    {
+                       scroll_h_vis_change = 1;
+                       sid->hbar_visible = EINA_FALSE;
+                    }
+               }
+             else
+               {
+                  scroll_h_vis_change = 1;
+                  sid->hbar_visible = EINA_FALSE;
+               }
+          }
+        else if (sid->hbar_flags == ELM_SCROLLER_POLICY_OFF)
+          {
+             scroll_h_vis_change = 1;
+             sid->hbar_visible = EINA_FALSE;
+          }
+     }
+   else
+     {
+        if (sid->hbar_flags == ELM_SCROLLER_POLICY_AUTO)
+          {
+             if ((sid->content) || (sid->extern_pan))
+               {
+                  if (w > vw)
+                    {
+                       scroll_h_vis_change = 1;
+                       sid->hbar_visible = EINA_TRUE;
+                    }
+               }
+          }
+        else if (sid->hbar_flags == ELM_SCROLLER_POLICY_ON)
+          {
+             scroll_h_vis_change = 1;
+             sid->hbar_visible = EINA_TRUE;
+          }
+     }
+   if (scroll_h_vis_change)
+     {
+        if (sid->hbar_flags != ELM_SCROLLER_POLICY_OFF)
+          {
+             if (sid->hbar_visible)
+               edje_object_signal_emit
+                 (sid->edje_obj, "elm,action,show,hbar", "elm");
+             else
+               edje_object_signal_emit
+                 (sid->edje_obj, "elm,action,hide,hbar", "elm");
+             edje_object_message_signal_process(sid->edje_obj);
+             _elm_scroll_scroll_bar_size_adjust(sid);
+          }
+        else
+          edje_object_signal_emit
+            (sid->edje_obj, "elm,action,hide,hbar", "elm");
+        _elm_scroll_scroll_bar_size_adjust(sid);
+     }
+
+   return scroll_h_vis_change;
+}
+
+static int
+_elm_scroll_scroll_bar_v_visibility_adjust(
+  Elm_Scrollable_Smart_Interface_Data *sid)
+{
+   int scroll_v_vis_change = 0;
+   Evas_Coord h, vw = 0, vh = 0;
+
+   if (!sid->edje_obj) return 0;
+
+   h = sid->content_info.h;
+   if (sid->pan_obj)
+     evas_object_geometry_get(sid->pan_obj, NULL, NULL, &vw, &vh);
+   if (sid->vbar_visible)
+     {
+        if (sid->vbar_flags == ELM_SCROLLER_POLICY_AUTO)
+          {
+             if ((sid->content) || (sid->extern_pan))
+               {
+                  if (h <= vh)
+                    {
+                       scroll_v_vis_change = 1;
+                       sid->vbar_visible = EINA_FALSE;
+                    }
+               }
+             else
+               {
+                  scroll_v_vis_change = 1;
+                  sid->vbar_visible = EINA_FALSE;
+               }
+          }
+        else if (sid->vbar_flags == ELM_SCROLLER_POLICY_OFF)
+          {
+             scroll_v_vis_change = 1;
+             sid->vbar_visible = EINA_FALSE;
+          }
+     }
+   else
+     {
+        if (sid->vbar_flags == ELM_SCROLLER_POLICY_AUTO)
+          {
+             if ((sid->content) || (sid->extern_pan))
+               {
+                  if (h > vh)
+                    {
+                       scroll_v_vis_change = 1;
+                       sid->vbar_visible = EINA_TRUE;
+                    }
+               }
+          }
+        else if (sid->vbar_flags == ELM_SCROLLER_POLICY_ON)
+          {
+             scroll_v_vis_change = 1;
+             sid->vbar_visible = EINA_TRUE;
+          }
+     }
+   if (scroll_v_vis_change)
+     {
+        if (sid->vbar_flags != ELM_SCROLLER_POLICY_OFF)
+          {
+             if (sid->vbar_visible)
+               edje_object_signal_emit
+                 (sid->edje_obj, "elm,action,show,vbar", "elm");
+             else
+               edje_object_signal_emit
+                 (sid->edje_obj, "elm,action,hide,vbar", "elm");
+
+             edje_object_message_signal_process(sid->edje_obj);
+             _elm_scroll_scroll_bar_size_adjust(sid);
+          }
+        else
+          edje_object_signal_emit
+            (sid->edje_obj, "elm,action,hide,vbar", "elm");
+     }
+
+   return scroll_v_vis_change;
+}
+
+static void
+_elm_scroll_scroll_bar_visibility_adjust(
+  Elm_Scrollable_Smart_Interface_Data *sid)
+{
+   int changed = 0;
+
+   changed |= _elm_scroll_scroll_bar_h_visibility_adjust(sid);
+   changed |= _elm_scroll_scroll_bar_v_visibility_adjust(sid);
+
+   if (changed)
+     {
+        _elm_scroll_scroll_bar_h_visibility_adjust(sid);
+        _elm_scroll_scroll_bar_v_visibility_adjust(sid);
+     }
+}
+
+static void
+_elm_scroll_scroll_bar_size_adjust(Elm_Scrollable_Smart_Interface_Data *sid)
+{
+   if (!sid->pan_obj || !sid->edje_obj) return;
+
+   ELM_PAN_DATA_GET(sid->pan_obj, psd);
+
+   if ((sid->content) || (sid->extern_pan))
+     {
+        Evas_Coord x, y, w, h, mx = 0, my = 0, vw = 0, vh = 0, px, py,
+                   minx = 0, miny = 0;
+        double vx, vy, size;
+
+        edje_object_part_geometry_get
+          (sid->edje_obj, "elm.swallow.content", NULL, NULL, &vw, &vh);
+        w = sid->content_info.w;
+        if (w < 1) w = 1;
+        size = (double)vw / (double)w;
+
+        if (size > 1.0)
+          {
+             size = 1.0;
+             edje_object_part_drag_value_set
+               (sid->edje_obj, "elm.dragable.hbar", 0.0, 0.0);
+          }
+        edje_object_part_drag_size_set
+          (sid->edje_obj, "elm.dragable.hbar", size, 1.0);
+
+        h = sid->content_info.h;
+        if (h < 1) h = 1;
+        size = (double)vh / (double)h;
+        if (size > 1.0)
+          {
+             size = 1.0;
+             edje_object_part_drag_value_set
+               (sid->edje_obj, "elm.dragable.vbar", 0.0, 0.0);
+          }
+        edje_object_part_drag_size_set
+          (sid->edje_obj, "elm.dragable.vbar", 1.0, size);
+
+        edje_object_part_drag_value_get
+          (sid->edje_obj, "elm.dragable.hbar", &vx, NULL);
+        edje_object_part_drag_value_get
+          (sid->edje_obj, "elm.dragable.vbar", NULL, &vy);
+
+        psd->api->pos_max_get(sid->pan_obj, &mx, &my);
+        psd->api->pos_min_get(sid->pan_obj, &minx, &miny);
+        x = vx * mx + minx;
+        y = vy * my + miny;
+
+        edje_object_part_drag_step_set
+          (sid->edje_obj, "elm.dragable.hbar", (double)sid->step.x /
+          (double)w, 0.0);
+        edje_object_part_drag_step_set
+          (sid->edje_obj, "elm.dragable.vbar", 0.0, (double)sid->step.y /
+          (double)h);
+        if (sid->page.x > 0)
+          edje_object_part_drag_page_set
+            (sid->edje_obj, "elm.dragable.hbar", (double)sid->page.x /
+            (double)w, 0.0);
+        else
+          edje_object_part_drag_page_set
+            (sid->edje_obj, "elm.dragable.hbar",
+            -((double)sid->page.x * ((double)vw / (double)w)) / 100.0, 0.0);
+        if (sid->page.y > 0)
+          edje_object_part_drag_page_set
+            (sid->edje_obj, "elm.dragable.vbar", 0.0,
+            (double)sid->page.y / (double)h);
+        else
+          edje_object_part_drag_page_set
+            (sid->edje_obj, "elm.dragable.vbar", 0.0,
+            -((double)sid->page.y * ((double)vh / (double)h)) / 100.0);
+
+        psd->api->pos_get(sid->pan_obj, &px, &py);
+        if (vx != mx) x = px;
+        if (vy != my) y = py;
+        psd->api->pos_set(sid->pan_obj, x, y);
+     }
+   else
+     {
+        Evas_Coord px = 0, py = 0, minx = 0, miny = 0;
+
+        edje_object_part_drag_size_set
+          (sid->edje_obj, "elm.dragable.vbar", 1.0, 1.0);
+        edje_object_part_drag_size_set
+          (sid->edje_obj, "elm.dragable.hbar", 1.0, 1.0);
+        psd->api->pos_min_get(sid->pan_obj, &minx, &miny);
+        psd->api->pos_get(sid->pan_obj, &px, &py);
+        psd->api->pos_set(sid->pan_obj, minx, miny);
+        if ((px != minx) || (py != miny))
+          edje_object_signal_emit(sid->edje_obj, "elm,action,scroll", "elm");
+     }
+   _elm_scroll_scroll_bar_visibility_adjust(sid);
+}
+
+static void
+_elm_scroll_scroll_bar_read_and_update(
+  Elm_Scrollable_Smart_Interface_Data *sid)
+{
+   Evas_Coord x, y, mx = 0, my = 0, px, py, minx = 0, miny = 0;
+   double vx, vy;
+
+   if (!sid->edje_obj || !sid->pan_obj) return;
+
+   ELM_PAN_DATA_GET(sid->pan_obj, psd);
+
+   if ((sid->down.dragged) || (sid->down.bounce_x_animator)
+       || (sid->down.bounce_y_animator) || (sid->down.momentum_animator)
+       || (sid->scrollto.x.animator) || (sid->scrollto.y.animator))
+     return;
+   edje_object_part_drag_value_get
+     (sid->edje_obj, "elm.dragable.vbar", NULL, &vy);
+   edje_object_part_drag_value_get
+     (sid->edje_obj, "elm.dragable.hbar", &vx, NULL);
+   psd->api->pos_max_get(sid->pan_obj, &mx, &my);
+   psd->api->pos_min_get(sid->pan_obj, &minx, &miny);
+   x = vx * (double)mx + minx;
+   y = vy * (double)my + miny;
+   psd->api->pos_get(sid->pan_obj, &px, &py);
+   psd->api->pos_set(sid->pan_obj, x, y);
+   if ((px != x) || (py != y))
+     edje_object_signal_emit(sid->edje_obj, "elm,action,scroll", "elm");
+}
+
+static void
+_elm_scroll_drag_start(Elm_Scrollable_Smart_Interface_Data *sid)
+{
+   if (sid->cb_func.drag_start)
+     sid->cb_func.drag_start(sid->obj, NULL);
+}
+
+static void
+_elm_scroll_drag_stop(Elm_Scrollable_Smart_Interface_Data *sid)
+{
+   if (sid->cb_func.drag_stop)
+     sid->cb_func.drag_stop(sid->obj, NULL);
+}
+
+static void
+_elm_scroll_anim_start(Elm_Scrollable_Smart_Interface_Data *sid)
+{
+   if (sid->cb_func.animate_start)
+     sid->cb_func.animate_start(sid->obj, NULL);
+}
+
+static void
+_elm_scroll_anim_stop(Elm_Scrollable_Smart_Interface_Data *sid)
+{
+   if (sid->cb_func.animate_stop)
+     sid->cb_func.animate_stop(sid->obj, NULL);
+}
+
+static void
+_elm_scroll_edje_drag_v_start_cb(void *data,
+                                 Evas_Object *obj __UNUSED__,
+                                 const char *emission __UNUSED__,
+                                 const char *source __UNUSED__)
+{
+   Elm_Scrollable_Smart_Interface_Data *sid = data;
+
+   _elm_scroll_scroll_bar_read_and_update(sid);
+   _elm_scroll_drag_start(sid);
+   sid->freeze = EINA_TRUE;
+}
+
+static void
+_elm_scroll_edje_drag_v_stop_cb(void *data,
+                                Evas_Object *obj __UNUSED__,
+                                const char *emission __UNUSED__,
+                                const char *source __UNUSED__)
+{
+   Elm_Scrollable_Smart_Interface_Data *sid = data;
+
+   _elm_scroll_scroll_bar_read_and_update(sid);
+   _elm_scroll_drag_stop(sid);
+   sid->freeze = EINA_FALSE;
+}
+
+static void
+_elm_scroll_edje_drag_v_cb(void *data,
+                           Evas_Object *obj __UNUSED__,
+                           const char *emission __UNUSED__,
+                           const char *source __UNUSED__)
+{
+   Elm_Scrollable_Smart_Interface_Data *sid = data;
+
+   _elm_scroll_scroll_bar_read_and_update(sid);
+}
+
+static void
+_elm_scroll_edje_drag_h_start_cb(void *data,
+                                 Evas_Object *obj __UNUSED__,
+                                 const char *emission __UNUSED__,
+                                 const char *source __UNUSED__)
+{
+   Elm_Scrollable_Smart_Interface_Data *sid = data;
+
+   _elm_scroll_scroll_bar_read_and_update(sid);
+   _elm_scroll_drag_start(sid);
+   sid->freeze = EINA_TRUE;
+}
+
+static void
+_elm_scroll_edje_drag_h_stop_cb(void *data,
+                                Evas_Object *obj __UNUSED__,
+                                const char *emission __UNUSED__,
+                                const char *source __UNUSED__)
+{
+   Elm_Scrollable_Smart_Interface_Data *sid = data;
+
+   _elm_scroll_scroll_bar_read_and_update(sid);
+   _elm_scroll_drag_stop(sid);
+   sid->freeze = EINA_FALSE;
+}
+
+static void
+_elm_scroll_edje_drag_h_cb(void *data,
+                           Evas_Object *obj __UNUSED__,
+                           const char *emission __UNUSED__,
+                           const char *source __UNUSED__)
+{
+   Elm_Scrollable_Smart_Interface_Data *sid = data;
+
+   _elm_scroll_scroll_bar_read_and_update(sid);
+}
+
+static void
+_elm_scroll_content_size_get(const Evas_Object *obj,
+                             Evas_Coord *w,
+                             Evas_Coord *h)
+{
+   ELM_SCROLL_IFACE_DATA_GET_OR_RETURN(obj, sid);
+
+   if (!sid->content) return;
+
+   evas_object_geometry_get(sid->content, NULL, NULL, w, h);
+}
+
+static void
+_elm_scroll_content_viewport_size_get(const Evas_Object *obj,
+                                      Evas_Coord *w,
+                                      Evas_Coord *h)
+{
+   ELM_SCROLL_IFACE_DATA_GET_OR_RETURN(obj, sid);
+
+   if (!sid->pan_obj || !sid->edje_obj) return;
+
+   edje_object_calc_force(sid->edje_obj);
+   evas_object_geometry_get(sid->pan_obj, NULL, NULL, w, h);
+}
+
+static void
+_elm_scroll_content_min_limit(Evas_Object *obj,
+                              Eina_Bool w,
+                              Eina_Bool h)
+{
+   ELM_SCROLL_IFACE_DATA_GET_OR_RETURN(obj, sid);
+
+   if (!sid->pan_obj || !sid->edje_obj) return;
+
+   if (!sid->cb_func.content_min_limit)
+     {
+        ERR("Content minimim size limiting is unimplemented -- you "
+            "must provide it yourself\n");
+        return;
+     }
+
+   sid->cb_func.content_min_limit(obj, w, h);
+}
+
+static Evas_Coord
+_elm_scroll_x_mirrored_get(const Evas_Object *obj,
+                           Evas_Coord x)
+{
+   Evas_Coord cw, ch, w, ret;
+
+   ELM_SCROLL_IFACE_DATA_GET_OR_RETURN_VAL(obj, sid, x);
+
+   if (!sid->pan_obj) return 0;
+
+   ELM_PAN_DATA_GET(sid->pan_obj, psd);
+
+   _elm_scroll_content_viewport_size_get(obj, &w, NULL);
+   psd->api->content_size_get(sid->pan_obj, &cw, &ch);
+   ret = (cw - (x + w));
+
+   return (ret >= 0) ? ret : 0;
+}
+
+/* Update the wanted coordinates according to the x, y passed
+ * widget directionality, content size and etc. */
+static void
+_elm_scroll_wanted_coordinates_update(Elm_Scrollable_Smart_Interface_Data *sid,
+                                      Evas_Coord x,
+                                      Evas_Coord y)
+{
+   Evas_Coord cw, ch;
+
+   if (!sid->pan_obj) return;
+
+   ELM_PAN_DATA_GET(sid->pan_obj, psd);
+
+   psd->api->content_size_get(sid->pan_obj, &cw, &ch);
+
+   /* Update wx/y/w/h - and if the requested positions aren't legal
+    * adjust a bit. */
+   _elm_scroll_content_viewport_size_get(sid->obj, &sid->ww, &sid->wh);
+   if (x < 0)
+     sid->wx = 0;
+   else if ((x + sid->ww) > cw)
+     sid->wx = cw - sid->ww;
+   else if (sid->is_mirrored)
+     sid->wx = _elm_scroll_x_mirrored_get(sid->obj, x);
+   else
+     sid->wx = x;
+   if (y < 0) sid->wy = 0;
+   else if ((y + sid->wh) > ch)
+     sid->wy = ch - sid->wh;
+   else sid->wy = y;
+}
+
+static void
+_elm_scroll_momentum_end(Elm_Scrollable_Smart_Interface_Data *sid)
+{
+   if ((sid->down.bounce_x_animator) || (sid->down.bounce_y_animator)) return;
+   if (sid->down.momentum_animator)
+     {
+        Evas_Coord px, py;
+        _elm_scroll_content_pos_get(sid->obj, &px, &py);
+        _elm_scroll_wanted_coordinates_update(sid, px, py);
+
+        ecore_animator_del(sid->down.momentum_animator);
+        sid->down.momentum_animator = NULL;
+        sid->down.bounce_x_hold = EINA_FALSE;
+        sid->down.bounce_y_hold = EINA_FALSE;
+        sid->down.ax = 0;
+        sid->down.ay = 0;
+        sid->down.dx = 0;
+        sid->down.dy = 0;
+        sid->down.pdx = 0;
+        sid->down.pdy = 0;
+        if (sid->content_info.resized)
+          _elm_scroll_wanted_region_set(sid->obj);
+     }
+}
+
+static Eina_Bool
+_elm_scroll_bounce_x_animator(void *data)
+{
+   Elm_Scrollable_Smart_Interface_Data *sid;
+   Evas_Coord x, y, dx, w, odx, ed, md;
+   double t, p, dt, pd, r;
+
+   sid = data;
+   t = ecore_loop_time_get();
+   dt = t - sid->down.anim_start2;
+   if (dt >= 0.0)
+     {
+        dt = dt / _elm_config->thumbscroll_bounce_friction;
+        odx = sid->down.b2x - sid->down.bx;
+        _elm_scroll_content_viewport_size_get(sid->obj, &w, NULL);
+        if (!sid->down.momentum_animator && (w > abs(odx)))
+          {
+             pd = (double)odx / (double)w;
+             pd = (pd > 0) ? pd : -pd;
+             pd = 1.0 - ((1.0 - pd) * (1.0 - pd));
+             dt = dt / pd;
+          }
+        if (dt > 1.0) dt = 1.0;
+        p = 1.0 - ((1.0 - dt) * (1.0 - dt));
+        _elm_scroll_content_pos_get(sid->obj, &x, &y);
+        dx = (odx * p);
+        r = 1.0;
+        if (sid->down.momentum_animator)
+          {
+             ed = abs(sid->down.dx * (_elm_config->thumbscroll_friction +
+                                      sid->down.extra_time) - sid->down.b0x);
+             md = abs(_elm_config->thumbscroll_friction * 5 * w);
+             if (ed > md) r = (double)(md) / (double)ed;
+          }
+        x = sid->down.b2x + (int)((double)(dx - odx) * r);
+        if (!sid->down.cancelled)
+          _elm_scroll_content_pos_set(sid->obj, x, y);
+        if (dt >= 1.0)
+          {
+             if (sid->down.momentum_animator)
+               sid->down.bounce_x_hold = EINA_TRUE;
+             if ((!sid->down.bounce_y_animator) &&
+                 (!sid->scrollto.y.animator))
+               _elm_scroll_anim_stop(sid);
+             sid->down.bounce_x_animator = NULL;
+             sid->down.pdx = 0;
+             sid->bouncemex = EINA_FALSE;
+             _elm_scroll_momentum_end(sid);
+             if (sid->content_info.resized)
+               _elm_scroll_wanted_region_set(sid->obj);
+             return ECORE_CALLBACK_CANCEL;
+          }
+     }
+   return ECORE_CALLBACK_RENEW;
+}
+
+static Eina_Bool
+_elm_scroll_bounce_y_animator(void *data)
+{
+   Elm_Scrollable_Smart_Interface_Data *sid;
+   Evas_Coord x, y, dy, h, ody, ed, md;
+   double t, p, dt, pd, r;
+
+   sid = data;
+   t = ecore_loop_time_get();
+   dt = t - sid->down.anim_start3;
+   if (dt >= 0.0)
+     {
+        dt = dt / _elm_config->thumbscroll_bounce_friction;
+        ody = sid->down.b2y - sid->down.by;
+        _elm_scroll_content_viewport_size_get(sid->obj, NULL, &h);
+        if (!sid->down.momentum_animator && (h > abs(ody)))
+          {
+             pd = (double)ody / (double)h;
+             pd = (pd > 0) ? pd : -pd;
+             pd = 1.0 - ((1.0 - pd) * (1.0 - pd));
+             dt = dt / pd;
+          }
+        if (dt > 1.0) dt = 1.0;
+        p = 1.0 - ((1.0 - dt) * (1.0 - dt));
+        _elm_scroll_content_pos_get(sid->obj, &x, &y);
+        dy = (ody * p);
+        r = 1.0;
+        if (sid->down.momentum_animator)
+          {
+             ed = abs(sid->down.dy * (_elm_config->thumbscroll_friction +
+                                      sid->down.extra_time) - sid->down.b0y);
+             md = abs(_elm_config->thumbscroll_friction * 5 * h);
+             if (ed > md) r = (double)(md) / (double)ed;
+          }
+        y = sid->down.b2y + (int)((double)(dy - ody) * r);
+        if (!sid->down.cancelled)
+          _elm_scroll_content_pos_set(sid->obj, x, y);
+        if (dt >= 1.0)
+          {
+             if (sid->down.momentum_animator)
+               sid->down.bounce_y_hold = EINA_TRUE;
+             if ((!sid->down.bounce_x_animator) &&
+                 (!sid->scrollto.y.animator))
+               _elm_scroll_anim_stop(sid);
+             sid->down.bounce_y_animator = NULL;
+             sid->down.pdy = 0;
+             sid->bouncemey = EINA_FALSE;
+             _elm_scroll_momentum_end(sid);
+             if (sid->content_info.resized)
+               _elm_scroll_wanted_region_set(sid->obj);
+             return ECORE_CALLBACK_CANCEL;
+          }
+     }
+
+   return ECORE_CALLBACK_RENEW;
+}
+
+static void
+_elm_scroll_bounce_eval(Elm_Scrollable_Smart_Interface_Data *sid)
+{
+   Evas_Coord mx, my, px, py, bx, by, b2x, b2y, minx = 0, miny = 0;
+
+   if (!sid->pan_obj) return;
+
+   ELM_PAN_DATA_GET(sid->pan_obj, psd);
+
+   if (sid->freeze) return;
+   if ((!sid->bouncemex) && (!sid->bouncemey)) return;
+   if (sid->down.now) return;  // down bounce while still held down
+   if (sid->down.onhold_animator)
+     {
+        ecore_animator_del(sid->down.onhold_animator);
+        sid->down.onhold_animator = NULL;
+        if (sid->content_info.resized)
+          _elm_scroll_wanted_region_set(sid->obj);
+     }
+   if (sid->down.hold_animator)
+     {
+        ecore_animator_del(sid->down.hold_animator);
+        sid->down.hold_animator = NULL;
+        if (sid->content_info.resized)
+          _elm_scroll_wanted_region_set(sid->obj);
+     }
+   psd->api->pos_max_get(sid->pan_obj, &mx, &my);
+   psd->api->pos_min_get(sid->pan_obj, &minx, &miny);
+   psd->api->pos_get(sid->pan_obj, &px, &py);
+   bx = px;
+   by = py;
+   if (px < minx) px = minx;
+   if ((px - minx) > mx) px = mx + minx;
+   if (py < miny) py = miny;
+   if ((py - miny) > my) py = my + miny;
+   b2x = px;
+   b2y = py;
+   if ((!sid->obj) ||
+       (!elm_widget_drag_child_locked_x_get(sid->obj)))
+     {
+        if ((!sid->down.bounce_x_animator) && (!sid->bounce_animator_disabled))
+          {
+             if (sid->bouncemex)
+               {
+                  if (sid->scrollto.x.animator)
+                    {
+                       ecore_animator_del(sid->scrollto.x.animator);
+                       sid->scrollto.x.animator = NULL;
+                    }
+                  sid->down.bounce_x_animator =
+                    ecore_animator_add(_elm_scroll_bounce_x_animator, sid);
+                  sid->down.anim_start2 = ecore_loop_time_get();
+                  sid->down.bx = bx;
+                  sid->down.bx0 = bx;
+                  sid->down.b2x = b2x;
+                  if (sid->down.momentum_animator)
+                    sid->down.b0x = sid->down.ax;
+                  else sid->down.b0x = 0;
+               }
+          }
+     }
+   if ((!sid->obj) ||
+       (!elm_widget_drag_child_locked_y_get(sid->obj)))
+     {
+        if ((!sid->down.bounce_y_animator) && (!sid->bounce_animator_disabled))
+          {
+             if (sid->bouncemey)
+               {
+                  if (sid->scrollto.y.animator)
+                    {
+                       ecore_animator_del(sid->scrollto.y.animator);
+                       sid->scrollto.y.animator = NULL;
+                    }
+                  sid->down.bounce_y_animator =
+                    ecore_animator_add(_elm_scroll_bounce_y_animator, sid);
+                  sid->down.anim_start3 = ecore_loop_time_get();
+                  sid->down.by = by;
+                  sid->down.by0 = by;
+                  sid->down.b2y = b2y;
+                  if (sid->down.momentum_animator)
+                    sid->down.b0y = sid->down.ay;
+                  else sid->down.b0y = 0;
+               }
+          }
+     }
+}
+
+static void
+_elm_scroll_content_pos_get(const Evas_Object *obj,
+                            Evas_Coord *x,
+                            Evas_Coord *y)
+{
+   ELM_SCROLL_IFACE_DATA_GET_OR_RETURN(obj, sid);
+
+   if (!sid->pan_obj) return;
+
+   ELM_PAN_DATA_GET(sid->pan_obj, psd);
+
+   psd->api->pos_get(sid->pan_obj, x, y);
+}
+
+static void
+_elm_scroll_content_pos_set(Evas_Object *obj,
+                            Evas_Coord x,
+                            Evas_Coord y)
+{
+   Evas_Coord mx = 0, my = 0, px = 0, py = 0, minx = 0, miny = 0;
+   double vx, vy;
+
+   ELM_SCROLL_IFACE_DATA_GET_OR_RETURN(obj, sid);
+
+   if (!sid->edje_obj || !sid->pan_obj) return;
+
+   ELM_PAN_DATA_GET(sid->pan_obj, psd);
+
+   // FIXME: allow for bounce outside of range
+   psd->api->pos_max_get(sid->pan_obj, &mx, &my);
+   psd->api->pos_min_get(sid->pan_obj, &minx, &miny);
+
+   if (mx > 0) vx = (double)(x - minx) / (double)mx;
+   else vx = 0.0;
+
+   if (vx < 0.0) vx = 0.0;
+   else if (vx > 1.0)
+     vx = 1.0;
+
+   if (my > 0) vy = (double)(y - miny) / (double)my;
+   else vy = 0.0;
+
+   if (vy < 0.0) vy = 0.0;
+   else if (vy > 1.0)
+     vy = 1.0;
+
+   edje_object_part_drag_value_set
+     (sid->edje_obj, "elm.dragable.vbar", 0.0, vy);
+   edje_object_part_drag_value_set
+     (sid->edje_obj, "elm.dragable.hbar", vx, 0.0);
+   psd->api->pos_get(sid->pan_obj, &px, &py);
+   if (!_elm_config->thumbscroll_bounce_enable)
+     {
+        if (x < minx) x = minx;
+        if ((x - minx) > mx) x = mx + minx;
+        if (y < miny) y = miny;
+        if ((y - miny) > my) y = my + miny;
+     }
+
+   if (!sid->bounce_horiz)
+     {
+        if (x < minx) x = minx;
+        if ((x - minx) > mx) x = mx + minx;
+     }
+   if (!sid->bounce_vert)
+     {
+        if (y < miny) y = miny;
+        if (y - miny > my) y = my + miny;
+     }
+
+   psd->api->pos_set(sid->pan_obj, x, y);
+   if ((px != x) || (py != y))
+     edje_object_signal_emit(sid->edje_obj, "elm,action,scroll", "elm");
+   if (!sid->down.bounce_x_animator)
+     {
+        if (((x < minx) && (0 <= sid->down.dx)) ||
+            ((x > (mx + minx)) && (0 >= sid->down.dx)))
+          {
+             sid->bouncemex = EINA_TRUE;
+             _elm_scroll_bounce_eval(sid);
+          }
+        else
+          sid->bouncemex = EINA_FALSE;
+     }
+   if (!sid->down.bounce_y_animator)
+     {
+        if (((y < miny) && (0 <= sid->down.dy)) ||
+            ((y > (my + miny)) && (0 >= sid->down.dy)))
+          {
+             sid->bouncemey = EINA_TRUE;
+             _elm_scroll_bounce_eval(sid);
+          }
+        else
+          sid->bouncemey = EINA_FALSE;
+     }
+
+   if ((x != px) || (y != py))
+     {
+        if (sid->cb_func.scroll)
+          sid->cb_func.scroll(obj, NULL);
+     }
+   if (x != px)
+     {
+        if (x == minx)
+          {
+             if (sid->cb_func.edge_left)
+               sid->cb_func.edge_left(obj, NULL);
+          }
+        if (x == (mx + minx))
+          {
+             if (sid->cb_func.edge_right)
+               sid->cb_func.edge_right(obj, NULL);
+          }
+     }
+   if (y != py)
+     {
+        if (y == miny)
+          {
+             if (sid->cb_func.edge_top)
+               sid->cb_func.edge_top(obj, NULL);
+          }
+        if (y == my + miny)
+          {
+             if (sid->cb_func.edge_bottom)
+               sid->cb_func.edge_bottom(obj, NULL);
+          }
+     }
+}
+
+static void
+_elm_scroll_mirrored_set(Evas_Object *obj,
+                         Eina_Bool mirrored)
+{
+   Evas_Coord wx;
+
+   ELM_SCROLL_IFACE_DATA_GET_OR_RETURN(obj, sid);
+
+   if (!sid->edje_obj) return;
+
+   mirrored = !!mirrored;
+
+   if (sid->is_mirrored == mirrored)
+     return;
+
+   sid->is_mirrored = mirrored;
+   edje_object_mirrored_set(sid->edje_obj, mirrored);
+
+   if (sid->is_mirrored)
+     wx = _elm_scroll_x_mirrored_get(sid->obj, sid->wx);
+   else
+     wx = sid->wx;
+
+   _elm_scroll_content_pos_set(sid->obj, wx, sid->wy);
+}
+
+/* returns TRUE when we need to move the scroller, FALSE otherwise.
+ * Updates w and h either way, so save them if you need them. */
+static Eina_Bool
+_elm_scroll_content_region_show_internal(Evas_Object *obj,
+                                         Evas_Coord *_x,
+                                         Evas_Coord *_y,
+                                         Evas_Coord w,
+                                         Evas_Coord h)
+{
+   Evas_Coord mx = 0, my = 0, cw = 0, ch = 0, px = 0, py = 0, nx, ny,
+              minx = 0, miny = 0, pw = 0, ph = 0, x = *_x, y = *_y;
+
+   ELM_SCROLL_IFACE_DATA_GET(obj, sid);
+
+   if (!sid->pan_obj) return EINA_FALSE;
+
+   ELM_PAN_DATA_GET(sid->pan_obj, psd);
+
+   psd->api->pos_max_get(sid->pan_obj, &mx, &my);
+   psd->api->pos_min_get(sid->pan_obj, &minx, &miny);
+   psd->api->content_size_get(sid->pan_obj, &cw, &ch);
+   psd->api->pos_get(sid->pan_obj, &px, &py);
+   evas_object_geometry_get(sid->pan_obj, NULL, NULL, &pw, &ph);
+
+   nx = px;
+   if ((x < px) && ((x + w) < (px + (cw - mx)))) nx = x;
+   else if ((x > px) && ((x + w) > (px + (cw - mx))))
+     nx = x + w - (cw - mx);
+   ny = py;
+   if ((y < py) && ((y + h) < (py + (ch - my)))) ny = y;
+   else if ((y > py) && ((y + h) > (py + (ch - my))))
+     ny = y + h - (ch - my);
+
+   if ((sid->down.bounce_x_animator) || (sid->down.bounce_y_animator) ||
+       (sid->scrollto.x.animator) || (sid->scrollto.y.animator))
+     {
+        _elm_scroll_anim_stop(sid);
+     }
+   if (sid->scrollto.x.animator)
+     {
+        ecore_animator_del(sid->scrollto.x.animator);
+        sid->scrollto.x.animator = NULL;
+     }
+   if (sid->scrollto.y.animator)
+     {
+        ecore_animator_del(sid->scrollto.y.animator);
+        sid->scrollto.y.animator = NULL;
+     }
+   if (sid->down.bounce_x_animator)
+     {
+        ecore_animator_del(sid->down.bounce_x_animator);
+        sid->down.bounce_x_animator = NULL;
+        sid->bouncemex = EINA_FALSE;
+        if (sid->content_info.resized)
+          _elm_scroll_wanted_region_set(sid->obj);
+     }
+   if (sid->down.bounce_y_animator)
+     {
+        ecore_animator_del(sid->down.bounce_y_animator);
+        sid->down.bounce_y_animator = NULL;
+        sid->bouncemey = EINA_FALSE;
+        if (sid->content_info.resized)
+          _elm_scroll_wanted_region_set(sid->obj);
+     }
+   if (sid->down.hold_animator)
+     {
+        ecore_animator_del(sid->down.hold_animator);
+        sid->down.hold_animator = NULL;
+        _elm_scroll_drag_stop(sid);
+        if (sid->content_info.resized)
+          _elm_scroll_wanted_region_set(sid->obj);
+     }
+   if (sid->down.momentum_animator)
+     {
+        ecore_animator_del(sid->down.momentum_animator);
+        sid->down.momentum_animator = NULL;
+        sid->down.bounce_x_hold = EINA_FALSE;
+        sid->down.bounce_y_hold = EINA_FALSE;
+        sid->down.ax = 0;
+        sid->down.ay = 0;
+        sid->down.pdx = 0;
+        sid->down.pdy = 0;
+        if (sid->content_info.resized)
+          _elm_scroll_wanted_region_set(sid->obj);
+     }
+
+   x = nx;
+   if ((x + pw) > cw) x = cw - pw;
+   if (x < minx) x = minx;
+   y = ny;
+   if ((y + ph) > ch) y = ch - ph;
+   if (y < miny) y = miny;
+
+   if ((x == px) && (y == py)) return EINA_FALSE;
+   *_x = x;
+   *_y = y;
+   return EINA_TRUE;
+}
+
+/* Set should be used for calculated positions, for example, when we move
+ * because of an animation or because this is the correct position after
+ * constraints. */
+static void
+_elm_scroll_content_region_set(Evas_Object *obj,
+                               Evas_Coord x,
+                               Evas_Coord y,
+                               Evas_Coord w,
+                               Evas_Coord h)
+{
+   ELM_SCROLL_IFACE_DATA_GET_OR_RETURN(obj, sid);
+
+   if (_elm_scroll_content_region_show_internal(obj, &x, &y, w, h))
+     {
+        _elm_scroll_content_pos_set(obj, x, y);
+        sid->down.sx = x;
+        sid->down.sy = y;
+        sid->down.x = sid->down.history[0].x;
+        sid->down.y = sid->down.history[0].y;
+     }
+}
+
+/* Set should be used for setting the wanted position, for example a
+ * user scroll or moving the cursor in an entry. */
+static void
+_elm_scroll_content_region_show(Evas_Object *obj,
+                                Evas_Coord x,
+                                Evas_Coord y,
+                                Evas_Coord w,
+                                Evas_Coord h)
+{
+   ELM_SCROLL_IFACE_DATA_GET_OR_RETURN(obj, sid);
+
+   sid->wx = x;
+   sid->wy = y;
+   sid->ww = w;
+   sid->wh = h;
+   if (_elm_scroll_content_region_show_internal(obj, &x, &y, w, h))
+     {
+        _elm_scroll_content_pos_set(obj, x, y);
+        sid->down.sx = x;
+        sid->down.sy = y;
+        sid->down.x = sid->down.history[0].x;
+        sid->down.y = sid->down.history[0].y;
+     }
+}
+
+static void
+_elm_scroll_wanted_region_set(Evas_Object *obj)
+{
+   Evas_Coord ww, wh, wx;
+
+   ELM_SCROLL_IFACE_DATA_GET(obj, sid);
+
+   wx = sid->wx;
+
+   if (sid->down.now || sid->down.momentum_animator ||
+       sid->down.bounce_x_animator || sid->down.bounce_y_animator ||
+       sid->down.hold_animator || sid->down.onhold_animator ||
+       sid->scrollto.x.animator || sid->scrollto.y.animator)
+     return;
+
+   sid->content_info.resized = EINA_FALSE;
+
+   /* Flip to RTL cords only if init in RTL mode */
+   if (sid->is_mirrored)
+     wx = _elm_scroll_x_mirrored_get(obj, sid->wx);
+
+   if (sid->ww == -1)
+     {
+        _elm_scroll_content_viewport_size_get(obj, &ww, &wh);
+     }
+   else
+     {
+        ww = sid->ww;
+        wh = sid->wh;
+     }
+
+   _elm_scroll_content_region_set(obj, wx, sid->wy, ww, wh);
+}
+
+static void
+_elm_scroll_wheel_event_cb(void *data,
+                           Evas *e __UNUSED__,
+                           Evas_Object *obj __UNUSED__,
+                           void *event_info)
+{
+   Elm_Scrollable_Smart_Interface_Data *sid;
+   Evas_Event_Mouse_Wheel *ev;
+   Evas_Coord x = 0, y = 0;
+   int direction = 0;
+
+   sid = data;
+   ev = event_info;
+   direction = ev->direction;
+   if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return;
+   if ((evas_key_modifier_is_set(ev->modifiers, "Control")) ||
+       (evas_key_modifier_is_set(ev->modifiers, "Alt")) ||
+       (evas_key_modifier_is_set(ev->modifiers, "Meta")) ||
+       (evas_key_modifier_is_set(ev->modifiers, "Hyper")) ||
+       (evas_key_modifier_is_set(ev->modifiers, "Super")))
+     return;
+   else if (evas_key_modifier_is_set(ev->modifiers, "Shift"))
+     direction = 1;
+   _elm_scroll_content_pos_get(sid->obj, &x, &y);
+   if ((sid->down.bounce_x_animator) || (sid->down.bounce_y_animator) ||
+       (sid->scrollto.x.animator) || (sid->scrollto.y.animator))
+     {
+        _elm_scroll_anim_stop(sid);
+     }
+   if (sid->scrollto.x.animator)
+     {
+        ecore_animator_del(sid->scrollto.x.animator);
+        sid->scrollto.x.animator = NULL;
+     }
+   if (sid->scrollto.y.animator)
+     {
+        ecore_animator_del(sid->scrollto.y.animator);
+        sid->scrollto.y.animator = NULL;
+     }
+   if (sid->down.bounce_x_animator)
+     {
+        ecore_animator_del(sid->down.bounce_x_animator);
+        sid->down.bounce_x_animator = NULL;
+        sid->bouncemex = EINA_FALSE;
+        if (sid->content_info.resized)
+          _elm_scroll_wanted_region_set(sid->obj);
+     }
+   if (sid->down.bounce_y_animator)
+     {
+        ecore_animator_del(sid->down.bounce_y_animator);
+        sid->down.bounce_y_animator = NULL;
+        sid->bouncemey = EINA_FALSE;
+        if (sid->content_info.resized)
+          _elm_scroll_wanted_region_set(sid->obj);
+     }
+   if (!direction)
+     y += ev->z * sid->step.y;
+   else if (direction == 1)
+     x += ev->z * sid->step.x;
+
+   if ((!sid->hold) && (!sid->freeze))
+     {
+        _elm_scroll_wanted_coordinates_update(sid, x, y);
+        _elm_scroll_content_pos_set(sid->obj, x, y);
+     }
+}
+
+static Eina_Bool
+_elm_scroll_post_event_up(void *data,
+                          Evas *e __UNUSED__)
+{
+   Elm_Scrollable_Smart_Interface_Data *sid = data;
+
+   if (sid->obj)
+     {
+        if (sid->down.dragged)
+          {
+             elm_widget_drag_lock_x_set(sid->obj, EINA_FALSE);
+             elm_widget_drag_lock_y_set(sid->obj, EINA_FALSE);
+          }
+     }
+   return EINA_TRUE;
+}
+
+static Eina_Bool
+_paging_is_enabled(Elm_Scrollable_Smart_Interface_Data *sid)
+{
+   if ((sid->pagerel_h == 0.0) && (!sid->pagesize_h) &&
+       (sid->pagerel_v == 0.0) && (!sid->pagesize_v))
+     return EINA_FALSE;
+   return EINA_TRUE;
+}
+
+static Eina_Bool
+_elm_scroll_momentum_animator(void *data)
+{
+   double t, dt, p;
+   Elm_Scrollable_Smart_Interface_Data *sid = data;
+   Evas_Coord x, y, dx, dy, px, py, maxx, maxy, minx, miny;
+   Eina_Bool no_bounce_x_end = EINA_FALSE, no_bounce_y_end = EINA_FALSE;
+
+   if (!sid->pan_obj) return ECORE_CALLBACK_CANCEL;
+
+   ELM_PAN_DATA_GET(sid->pan_obj, psd);
+
+   t = ecore_loop_time_get();
+   dt = t - sid->down.anim_start;
+   if (dt >= 0.0)
+     {
+        dt = dt / (_elm_config->thumbscroll_friction + sid->down.extra_time);
+        if (dt > 1.0) dt = 1.0;
+        p = 1.0 - ((1.0 - dt) * (1.0 - dt));
+        dx = (sid->down.dx * (_elm_config->thumbscroll_friction +
+                              sid->down.extra_time) * p);
+        dy = (sid->down.dy * (_elm_config->thumbscroll_friction +
+                              sid->down.extra_time) * p);
+        sid->down.ax = dx;
+        sid->down.ay = dy;
+        x = sid->down.sx - dx;
+        y = sid->down.sy - dy;
+        _elm_scroll_content_pos_get(sid->obj, &px, &py);
+        if ((sid->down.bounce_x_animator) ||
+            (sid->down.bounce_x_hold))
+          {
+             sid->down.bx = sid->down.bx0 - dx + sid->down.b0x;
+             x = px;
+          }
+        if ((sid->down.bounce_y_animator) ||
+            (sid->down.bounce_y_hold))
+          {
+             sid->down.by = sid->down.by0 - dy + sid->down.b0y;
+             y = py;
+          }
+        _elm_scroll_content_pos_set(sid->obj, x, y);
+        _elm_scroll_wanted_coordinates_update(sid, x, y);
+        psd->api->pos_max_get(sid->pan_obj, &maxx, &maxy);
+        psd->api->pos_min_get(sid->pan_obj, &minx, &miny);
+        if (!sid->bounce_horiz)
+          {
+             if (x <= minx) no_bounce_x_end = EINA_TRUE;
+             if ((x - minx) >= maxx) no_bounce_x_end = EINA_TRUE;
+          }
+        if (!sid->bounce_vert)
+          {
+             if (y <= miny) no_bounce_y_end = EINA_TRUE;
+             if ((y - miny) >= maxy) no_bounce_y_end = EINA_TRUE;
+          }
+        if ((dt >= 1.0) ||
+            ((sid->down.bounce_x_hold) && (sid->down.bounce_y_hold)) ||
+            (no_bounce_x_end && no_bounce_y_end))
+          {
+             _elm_scroll_anim_stop(sid);
+
+             sid->down.momentum_animator = NULL;
+             sid->down.bounce_x_hold = EINA_FALSE;
+             sid->down.bounce_y_hold = EINA_FALSE;
+             sid->down.ax = 0;
+             sid->down.ay = 0;
+             sid->down.pdx = 0;
+             sid->down.pdy = 0;
+             if (sid->content_info.resized)
+               _elm_scroll_wanted_region_set(sid->obj);
+
+             return ECORE_CALLBACK_CANCEL;
+          }
+     }
+
+   return ECORE_CALLBACK_RENEW;
+}
+
+static Evas_Coord
+_elm_scroll_page_x_get(Elm_Scrollable_Smart_Interface_Data *sid,
+                       int offset)
+{
+   Evas_Coord x, y, w, h, cw, ch, minx = 0;
+
+   if (!sid->pan_obj) return 0;
+
+   ELM_PAN_DATA_GET(sid->pan_obj, psd);
+
+   _elm_scroll_content_pos_get(sid->obj, &x, &y);
+   _elm_scroll_content_viewport_size_get(sid->obj, &w, &h);
+   psd->api->content_size_get(sid->pan_obj, &cw, &ch);
+   psd->api->pos_min_get(sid->pan_obj, &minx, NULL);
+
+   x += offset;
+
+   if (sid->pagerel_h > 0.0)
+     sid->pagesize_h = w * sid->pagerel_h;
+   if (sid->pagesize_h > 0)
+     {
+        x = x + (sid->pagesize_h * 0.5);
+        x = x / (sid->pagesize_h);
+        x = x * (sid->pagesize_h);
+     }
+   if ((x + w) > cw) x = cw - w;
+   if (x < minx) x = minx;
+
+   return x;
+}
+
+static Evas_Coord
+_elm_scroll_page_y_get(Elm_Scrollable_Smart_Interface_Data *sid,
+                       int offset)
+{
+   Evas_Coord x, y, w, h, cw, ch, miny = 0;
+
+   if (!sid->pan_obj) return 0;
+
+   ELM_PAN_DATA_GET(sid->pan_obj, psd);
+
+   _elm_scroll_content_pos_get(sid->obj, &x, &y);
+   _elm_scroll_content_viewport_size_get(sid->obj, &w, &h);
+   psd->api->content_size_get(sid->pan_obj, &cw, &ch);
+   psd->api->pos_min_get(sid->pan_obj, NULL, &miny);
+
+   y += offset;
+
+   if (sid->pagerel_v > 0.0)
+     sid->pagesize_v = h * sid->pagerel_v;
+   if (sid->pagesize_v > 0)
+     {
+        y = y + (sid->pagesize_v * 0.5);
+        y = y / (sid->pagesize_v);
+        y = y * (sid->pagesize_v);
+     }
+   if ((y + h) > ch) y = ch - h;
+   if (y < miny) y = miny;
+
+   return y;
+}
+
+static Eina_Bool
+_elm_scroll_scroll_to_x_animator(void *data)
+{
+   Elm_Scrollable_Smart_Interface_Data *sid = data;
+   Evas_Coord px, py;
+   double t, tt;
+
+   if (!sid->pan_obj) return ECORE_CALLBACK_CANCEL;
+
+   ELM_PAN_DATA_GET(sid->pan_obj, psd);
+
+   t = ecore_loop_time_get();
+   tt = (t - sid->scrollto.x.t_start) /
+     (sid->scrollto.x.t_end - sid->scrollto.x.t_start);
+   tt = 1.0 - tt;
+   tt = 1.0 - (tt * tt);
+   psd->api->pos_get(sid->pan_obj, &px, &py);
+   px = (sid->scrollto.x.start * (1.0 - tt)) +
+     (sid->scrollto.x.end * tt);
+   if (t >= sid->scrollto.x.t_end)
+     {
+        px = sid->scrollto.x.end;
+        _elm_scroll_content_pos_set(sid->obj, px, py);
+        sid->down.sx = px;
+        sid->down.x = sid->down.history[0].x;
+        _elm_scroll_wanted_coordinates_update(sid, px, py);
+        sid->scrollto.x.animator = NULL;
+        if ((!sid->scrollto.y.animator) && (!sid->down.bounce_y_animator))
+          _elm_scroll_anim_stop(sid);
+        return ECORE_CALLBACK_CANCEL;
+     }
+   _elm_scroll_content_pos_set(sid->obj, px, py);
+   _elm_scroll_wanted_coordinates_update(sid, px, py);
+   return ECORE_CALLBACK_RENEW;
+}
+
+static Eina_Bool
+_elm_scroll_scroll_to_y_animator(void *data)
+{
+   Elm_Scrollable_Smart_Interface_Data *sid = data;
+   Evas_Coord px, py;
+   double t, tt;
+
+   if (!sid->pan_obj) return EINA_FALSE;
+
+   ELM_PAN_DATA_GET(sid->pan_obj, psd);
+
+   t = ecore_loop_time_get();
+   tt = (t - sid->scrollto.y.t_start) /
+     (sid->scrollto.y.t_end - sid->scrollto.y.t_start);
+   tt = 1.0 - tt;
+   tt = 1.0 - (tt * tt);
+   psd->api->pos_get(sid->pan_obj, &px, &py);
+   py = (sid->scrollto.y.start * (1.0 - tt)) +
+     (sid->scrollto.y.end * tt);
+   if (t >= sid->scrollto.y.t_end)
+     {
+        py = sid->scrollto.y.end;
+        _elm_scroll_content_pos_set(sid->obj, px, py);
+        sid->down.sy = py;
+        sid->down.y = sid->down.history[0].y;
+        _elm_scroll_wanted_coordinates_update(sid, px, py);
+        sid->scrollto.y.animator = NULL;
+        if ((!sid->scrollto.x.animator) && (!sid->down.bounce_x_animator))
+          _elm_scroll_anim_stop(sid);
+        return ECORE_CALLBACK_CANCEL;
+     }
+   _elm_scroll_content_pos_set(sid->obj, px, py);
+   _elm_scroll_wanted_coordinates_update(sid, px, py);
+
+   return ECORE_CALLBACK_RENEW;
+}
+
+static void
+_elm_scroll_scroll_to_y(Elm_Scrollable_Smart_Interface_Data *sid,
+                        double t_in,
+                        Evas_Coord pos_y)
+{
+   Evas_Coord px, py, x, y, w, h;
+   double t;
+
+   if (!sid->pan_obj) return;
+
+   ELM_PAN_DATA_GET(sid->pan_obj, psd);
+
+   if (sid->freeze) return;
+   if (t_in <= 0.0)
+     {
+        _elm_scroll_content_pos_get(sid->obj, &x, &y);
+        _elm_scroll_content_viewport_size_get(sid->obj, &w, &h);
+        y = pos_y;
+        _elm_scroll_content_region_set(sid->obj, x, y, w, h);
+        return;
+     }
+   t = ecore_loop_time_get();
+   psd->api->pos_get(sid->pan_obj, &px, &py);
+   sid->scrollto.y.start = py;
+   sid->scrollto.y.end = pos_y;
+   sid->scrollto.y.t_start = t;
+   sid->scrollto.y.t_end = t + t_in;
+   if (!sid->scrollto.y.animator)
+     {
+        sid->scrollto.y.animator =
+          ecore_animator_add(_elm_scroll_scroll_to_y_animator, sid);
+        if (!sid->scrollto.x.animator)
+          _elm_scroll_anim_start(sid);
+     }
+   if (sid->down.bounce_y_animator)
+     {
+        ecore_animator_del(sid->down.bounce_y_animator);
+        sid->down.bounce_y_animator = NULL;
+        _elm_scroll_momentum_end(sid);
+        if (sid->content_info.resized)
+          _elm_scroll_wanted_region_set(sid->obj);
+     }
+   sid->bouncemey = EINA_FALSE;
+}
+
+static void
+_elm_scroll_scroll_to_x(Elm_Scrollable_Smart_Interface_Data *sid,
+                        double t_in,
+                        Evas_Coord pos_x)
+{
+   Evas_Coord px, py, x, y, w, h;
+   double t;
+
+   if (!sid->pan_obj) return;
+
+   ELM_PAN_DATA_GET(sid->pan_obj, psd);
+
+   if (sid->freeze) return;
+   if (t_in <= 0.0)
+     {
+        _elm_scroll_content_pos_get(sid->obj, &x, &y);
+        _elm_scroll_content_viewport_size_get(sid->obj, &w, &h);
+        x = pos_x;
+        _elm_scroll_content_region_set(sid->obj, x, y, w, h);
+        return;
+     }
+   t = ecore_loop_time_get();
+   psd->api->pos_get(sid->pan_obj, &px, &py);
+   sid->scrollto.x.start = px;
+   sid->scrollto.x.end = pos_x;
+   sid->scrollto.x.t_start = t;
+   sid->scrollto.x.t_end = t + t_in;
+   if (!sid->scrollto.x.animator)
+     {
+        sid->scrollto.x.animator =
+          ecore_animator_add(_elm_scroll_scroll_to_x_animator, sid);
+        if (!sid->scrollto.y.animator)
+          _elm_scroll_anim_start(sid);
+     }
+   if (sid->down.bounce_x_animator)
+     {
+        ecore_animator_del(sid->down.bounce_x_animator);
+        sid->down.bounce_x_animator = NULL;
+        _elm_scroll_momentum_end(sid);
+        if (sid->content_info.resized)
+          _elm_scroll_wanted_region_set(sid->obj);
+     }
+   sid->bouncemex = EINA_FALSE;
+}
+
+static void
+_elm_scroll_mouse_up_event_cb(void *data,
+                              Evas *e,
+                              Evas_Object *obj __UNUSED__,
+                              void *event_info)
+{
+   Elm_Scrollable_Smart_Interface_Data *sid = data;
+   Evas_Coord x = 0, y = 0, ox = 0, oy = 0;
+   Evas_Event_Mouse_Down *ev;
+
+   if (!sid->pan_obj) return;
+
+   ELM_PAN_DATA_GET(sid->pan_obj, psd);
+
+   ev = event_info;
+   sid->down.hold_parent = EINA_FALSE;
+   sid->down.dx = 0;
+   sid->down.dy = 0;
+   evas_post_event_callback_push(e, _elm_scroll_post_event_up, sid);
+
+   // FIXME: respect elm_widget_scroll_hold_get of parent container
+   if (!_elm_config->thumbscroll_enable) return;
+
+   if (ev->button == 1)
+     {
+        if (sid->down.onhold_animator)
+          {
+             ecore_animator_del(sid->down.onhold_animator);
+             sid->down.onhold_animator = NULL;
+             if (sid->content_info.resized)
+               _elm_scroll_wanted_region_set(sid->obj);
+          }
+        x = ev->canvas.x - sid->down.x;
+        y = ev->canvas.y - sid->down.y;
+        if (sid->down.dragged)
+          {
+             _elm_scroll_drag_stop(sid);
+             if ((!sid->hold) && (!sid->freeze))
+               {
+                  int i;
+                  double t, at, dt;
+                  Evas_Coord ax, ay, dx, dy, vel;
+
+#ifdef EVTIME
+                  t = ev->timestamp / 1000.0;
+#else
+                  t = ecore_loop_time_get();
+#endif
+
+                  ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
+                  ax = ev->canvas.x;
+                  ay = ev->canvas.y;
+                  at = 0.0;
+#ifdef SCROLLDBG
+                  DBG("------ %i %i\n", ev->canvas.x, ev->canvas.y);
+#endif
+                  for (i = 0; i < 60; i++)
+                    {
+                       dt = t - sid->down.history[i].timestamp;
+                       if (dt > 0.2) break;
+#ifdef SCROLLDBG
+                       DBG("H: %i %i @ %1.3f\n",
+                           sid->down.history[i].x,
+                           sid->down.history[i].y, dt);
+#endif
+                       at += dt;
+                       ax += sid->down.history[i].x;
+                       ay += sid->down.history[i].y;
+                    }
+                  ax /= (i + 1);
+                  ay /= (i + 1);
+                  at /= (i + 1);
+                  at /= _elm_config->thumbscroll_sensitivity_friction;
+                  dx = ev->canvas.x - ax;
+                  dy = ev->canvas.y - ay;
+                  if (at > 0)
+                    {
+                       vel = sqrt((dx * dx) + (dy * dy)) / at;
+                       if ((_elm_config->thumbscroll_friction > 0.0) &&
+                           (vel > _elm_config->thumbscroll_momentum_threshold))
+                         {
+                            int minx, miny, mx, my, px, py;
+
+                            psd->api->pos_min_get
+                              (sid->pan_obj, &minx, &miny);
+                            psd->api->pos_max_get(sid->pan_obj, &mx, &my);
+                            psd->api->pos_get(sid->pan_obj, &px, &py);
+                            sid->down.dx = ((double)dx / at);
+                            sid->down.dy = ((double)dy / at);
+                            if (((sid->down.dx > 0) && (sid->down.pdx > 0)) ||
+                                ((sid->down.dx < 0) && (sid->down.pdx < 0)))
+                              if (px > minx && px < mx)
+                                sid->down.dx += (double)sid->down.pdx * 1.5;
+                            // FIXME: * 1.5 - probably should be config
+                            if (((sid->down.dy > 0) && (sid->down.pdy > 0)) ||
+                                ((sid->down.dy < 0) && (sid->down.pdy < 0)))
+                              if (py > miny && py < my)
+                                sid->down.dy += (double)sid->down.pdy * 1.5;
+                            // FIXME: * 1.5 - probably should be config
+                            if (((sid->down.dx > 0) && (sid->down.pdx > 0)) ||
+                                ((sid->down.dx < 0) && (sid->down.pdx < 0)) ||
+                                ((sid->down.dy > 0) && (sid->down.pdy > 0)) ||
+                                ((sid->down.dy < 0) && (sid->down.pdy < 0)))
+                              {
+                                 double tt = ecore_loop_time_get();
+                                 double dtt = tt - sid->down.anim_start;
+
+                                 if (dtt < 0.0) dtt = 0.0;
+                                 else if (dtt >
+                                          _elm_config->thumbscroll_friction)
+                                   dtt = _elm_config->thumbscroll_friction;
+                                 sid->down.extra_time =
+                                   _elm_config->thumbscroll_friction - dtt;
+                              }
+                            else
+                              sid->down.extra_time = 0.0;
+                            sid->down.pdx = sid->down.dx;
+                            sid->down.pdy = sid->down.dy;
+                            ox = -sid->down.dx;
+                            oy = -sid->down.dy;
+                            if (!_paging_is_enabled(sid))
+                              {
+                                 if ((!sid->down.momentum_animator) &&
+                                     (!sid->momentum_animator_disabled) &&
+                                     (sid->obj) &&
+                                     (!elm_widget_drag_child_locked_y_get
+                                        (sid->obj)))
+                                   {
+                                      sid->down.momentum_animator =
+                                        ecore_animator_add
+                                          (_elm_scroll_momentum_animator, sid);
+                                      ev->event_flags |=
+                                        EVAS_EVENT_FLAG_ON_SCROLL;
+                                      _elm_scroll_anim_start(sid);
+                                   }
+                                 sid->down.anim_start = ecore_loop_time_get();
+                                 _elm_scroll_content_pos_get(sid->obj, &x, &y);
+                                 sid->down.sx = x;
+                                 sid->down.sy = y;
+                                 sid->down.b0x = 0;
+                                 sid->down.b0y = 0;
+                              }
+                         }
+                    }
+               }
+             else
+               {
+                  sid->down.pdx = 0;
+                  sid->down.pdy = 0;
+               }
+             evas_event_feed_hold(e, 0, ev->timestamp, ev->data);
+             if (_paging_is_enabled(sid))
+               {
+                  Evas_Coord pgx, pgy;
+
+                  _elm_scroll_content_pos_get(sid->obj, &x, &y);
+                  if ((!sid->obj) ||
+                      (!elm_widget_drag_child_locked_x_get
+                         (sid->obj)))
+                    {
+                       pgx = _elm_scroll_page_x_get(sid, ox);
+                       if (pgx != x)
+                         {
+                            ev->event_flags |= EVAS_EVENT_FLAG_ON_SCROLL;
+                            _elm_scroll_scroll_to_x
+                              (sid, _elm_config->page_scroll_friction, pgx);
+                         }
+                    }
+                  if ((!sid->obj) ||
+                      (!elm_widget_drag_child_locked_y_get
+                         (sid->obj)))
+                    {
+                       pgy = _elm_scroll_page_y_get(sid, oy);
+                       if (pgy != y)
+                         {
+                            ev->event_flags |= EVAS_EVENT_FLAG_ON_SCROLL;
+                            _elm_scroll_scroll_to_y
+                              (sid, _elm_config->page_scroll_friction, pgy);
+                         }
+                    }
+               }
+          }
+        else
+          {
+             sid->down.pdx = 0;
+             sid->down.pdy = 0;
+             if (_paging_is_enabled(sid))
+               {
+                  Evas_Coord pgx, pgy;
+
+                  _elm_scroll_content_pos_get(sid->obj, &x, &y);
+                  if ((!sid->obj) ||
+                      (!elm_widget_drag_child_locked_x_get
+                         (sid->obj)))
+                    {
+                       pgx = _elm_scroll_page_x_get(sid, ox);
+                       if (pgx != x)
+                         _elm_scroll_scroll_to_x
+                           (sid, _elm_config->page_scroll_friction, pgx);
+                    }
+                  if ((!sid->obj) ||
+                      (!elm_widget_drag_child_locked_y_get
+                         (sid->obj)))
+                    {
+                       pgy = _elm_scroll_page_y_get(sid, oy);
+                       if (pgy != y)
+                         _elm_scroll_scroll_to_y
+                           (sid, _elm_config->page_scroll_friction, pgy);
+                    }
+               }
+          }
+        if (sid->down.hold_animator)
+          {
+             ecore_animator_del(sid->down.hold_animator);
+             sid->down.hold_animator = NULL;
+             if (sid->content_info.resized)
+               _elm_scroll_wanted_region_set(sid->obj);
+          }
+        if (sid->down.scroll)
+          {
+             ev->event_flags |= EVAS_EVENT_FLAG_ON_SCROLL;
+             sid->down.scroll = EINA_FALSE;
+          }
+        if (sid->down.hold)
+          {
+             ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
+             sid->down.hold = EINA_FALSE;
+          }
+        sid->down.dragged_began = EINA_FALSE;
+        sid->down.dir_x = EINA_FALSE;
+        sid->down.dir_y = EINA_FALSE;
+        sid->down.want_dragged = EINA_FALSE;
+        sid->down.dragged = EINA_FALSE;
+        sid->down.now = EINA_FALSE;
+        _elm_scroll_content_pos_get(sid->obj, &x, &y);
+        _elm_scroll_content_pos_set(sid->obj, x, y);
+        _elm_scroll_wanted_coordinates_update(sid, x, y);
+
+        if (sid->content_info.resized)
+          _elm_scroll_wanted_region_set(sid->obj);
+
+        if (!_paging_is_enabled(sid))
+          _elm_scroll_bounce_eval(sid);
+     }
+}
+
+static void
+_elm_scroll_mouse_down_event_cb(void *data,
+                                Evas *e __UNUSED__,
+                                Evas_Object *obj __UNUSED__,
+                                void *event_info)
+{
+   Elm_Scrollable_Smart_Interface_Data *sid;
+   Evas_Event_Mouse_Down *ev;
+   Evas_Coord x = 0, y = 0;
+
+   sid = data;
+   ev = event_info;
+
+   if (_elm_config->thumbscroll_enable)
+     {
+        sid->down.hold = EINA_FALSE;
+        if ((sid->down.bounce_x_animator) || (sid->down.bounce_y_animator) ||
+            (sid->down.momentum_animator) || (sid->scrollto.x.animator) ||
+            (sid->scrollto.y.animator))
+          {
+             ev->event_flags |= EVAS_EVENT_FLAG_ON_SCROLL |
+               EVAS_EVENT_FLAG_ON_HOLD;
+             sid->down.scroll = EINA_TRUE;
+             sid->down.hold = EINA_TRUE;
+             _elm_scroll_anim_stop(sid);
+          }
+        if (sid->scrollto.x.animator)
+          {
+             ecore_animator_del(sid->scrollto.x.animator);
+             sid->scrollto.x.animator = NULL;
+          }
+        if (sid->scrollto.y.animator)
+          {
+             ecore_animator_del(sid->scrollto.y.animator);
+             sid->scrollto.y.animator = NULL;
+          }
+        if (sid->down.bounce_x_animator)
+          {
+             ecore_animator_del(sid->down.bounce_x_animator);
+             sid->down.bounce_x_animator = NULL;
+             sid->bouncemex = EINA_FALSE;
+             if (sid->content_info.resized)
+               _elm_scroll_wanted_region_set(sid->obj);
+          }
+        if (sid->down.bounce_y_animator)
+          {
+             ecore_animator_del(sid->down.bounce_y_animator);
+             sid->down.bounce_y_animator = NULL;
+             sid->bouncemey = EINA_FALSE;
+             if (sid->content_info.resized)
+               _elm_scroll_wanted_region_set(sid->obj);
+          }
+        if (sid->down.hold_animator)
+          {
+             ecore_animator_del(sid->down.hold_animator);
+             sid->down.hold_animator = NULL;
+             _elm_scroll_drag_stop(sid);
+             if (sid->content_info.resized)
+               _elm_scroll_wanted_region_set(sid->obj);
+          }
+        if (sid->down.momentum_animator)
+          {
+             ecore_animator_del(sid->down.momentum_animator);
+             sid->down.momentum_animator = NULL;
+             sid->down.bounce_x_hold = EINA_FALSE;
+             sid->down.bounce_y_hold = EINA_FALSE;
+             sid->down.ax = 0;
+             sid->down.ay = 0;
+             if (sid->content_info.resized)
+               _elm_scroll_wanted_region_set(sid->obj);
+          }
+        if (ev->button == 1)
+          {
+             sid->down.hist.est_timestamp_diff =
+               ecore_loop_time_get() - ((double)ev->timestamp / 1000.0);
+             sid->down.hist.tadd = 0.0;
+             sid->down.hist.dxsum = 0.0;
+             sid->down.hist.dysum = 0.0;
+             sid->down.now = EINA_TRUE;
+             sid->down.dragged = EINA_FALSE;
+             sid->down.dir_x = EINA_FALSE;
+             sid->down.dir_y = EINA_FALSE;
+             sid->down.x = ev->canvas.x;
+             sid->down.y = ev->canvas.y;
+             _elm_scroll_content_pos_get(sid->obj, &x, &y);
+             sid->down.sx = x;
+             sid->down.sy = y;
+             sid->down.locked = EINA_FALSE;
+             memset(&(sid->down.history[0]), 0,
+                    sizeof(sid->down.history[0]) * 60);
+#ifdef EVTIME
+             sid->down.history[0].timestamp = ev->timestamp / 1000.0;
+             sid->down.history[0].localtimestamp = ecore_loop_time_get();
+#else
+             sid->down.history[0].timestamp = ecore_loop_time_get();
+#endif
+             sid->down.history[0].x = ev->canvas.x;
+             sid->down.history[0].y = ev->canvas.y;
+          }
+        sid->down.dragged_began = EINA_FALSE;
+        sid->down.hold_parent = EINA_FALSE;
+        sid->down.cancelled = EINA_FALSE;
+        if (sid->hold || sid->freeze)
+          sid->down.want_reset = EINA_TRUE;
+        else
+          sid->down.want_reset = EINA_FALSE;
+     }
+}
+
+static Eina_Bool
+_elm_scroll_can_scroll(Elm_Scrollable_Smart_Interface_Data *sid,
+                       int dir)
+{
+   Evas_Coord mx = 0, my = 0, px = 0, py = 0, minx = 0, miny = 0;
+
+   if (!sid->pan_obj) return EINA_FALSE;
+
+   ELM_PAN_DATA_GET(sid->pan_obj, psd);
+
+   psd->api->pos_max_get(sid->pan_obj, &mx, &my);
+   psd->api->pos_min_get(sid->pan_obj, &minx, &miny);
+   psd->api->pos_get(sid->pan_obj, &px, &py);
+   switch (dir)
+     {
+      case LEFT:
+        if (px > minx) return EINA_TRUE;
+        break;
+
+      case RIGHT:
+        if ((px - minx) < mx) return EINA_TRUE;
+        break;
+
+      case UP:
+        if (py > miny) return EINA_TRUE;
+        break;
+
+      case DOWN:
+        if ((py - miny) < my) return EINA_TRUE;
+        break;
+
+      default:
+        break;
+     }
+   return EINA_FALSE;
+}
+
+static Eina_Bool
+_elm_scroll_post_event_move(void *data,
+                            Evas *e __UNUSED__)
+{
+   Elm_Scrollable_Smart_Interface_Data *sid = data;
+
+   if (sid->down.want_dragged)
+     {
+        int start = 0;
+
+        if (sid->down.hold_parent)
+          {
+             if ((sid->down.dir_x) &&
+                 !_elm_scroll_can_scroll(sid, sid->down.hdir))
+               {
+                  sid->down.dir_x = EINA_FALSE;
+               }
+             if ((sid->down.dir_y) &&
+                 !_elm_scroll_can_scroll(sid, sid->down.vdir))
+               {
+                  sid->down.dir_y = EINA_FALSE;
+               }
+          }
+        if (sid->down.dir_x)
+          {
+             if ((!sid->obj) ||
+                 (!elm_widget_drag_child_locked_x_get(sid->obj)))
+               {
+                  sid->down.want_dragged = EINA_FALSE;
+                  sid->down.dragged = EINA_TRUE;
+                  if (sid->obj)
+                    {
+                       elm_widget_drag_lock_x_set(sid->obj, 1);
+                    }
+                  start = 1;
+               }
+             else
+               sid->down.dir_x = EINA_FALSE;
+          }
+        if (sid->down.dir_y)
+          {
+             if ((!sid->obj) ||
+                 (!elm_widget_drag_child_locked_y_get(sid->obj)))
+               {
+                  sid->down.want_dragged = EINA_FALSE;
+                  sid->down.dragged = EINA_TRUE;
+                  if (sid->obj)
+                    {
+                       elm_widget_drag_lock_y_set
+                         (sid->obj, EINA_TRUE);
+                    }
+                  start = 1;
+               }
+             else
+               sid->down.dir_y = EINA_FALSE;
+          }
+        if ((!sid->down.dir_x) && (!sid->down.dir_y))
+          {
+             sid->down.cancelled = EINA_TRUE;
+          }
+        if (start) _elm_scroll_drag_start(sid);
+     }
+
+   return EINA_TRUE;
+}
+
+static void
+_elm_scroll_down_coord_eval(Elm_Scrollable_Smart_Interface_Data *sid,
+                            Evas_Coord *x,
+                            Evas_Coord *y)
+{
+   Evas_Coord minx, miny;
+
+   if (!sid->pan_obj) return;
+
+   ELM_PAN_DATA_GET(sid->pan_obj, psd);
+
+   if (sid->down.dir_x) *x = sid->down.sx - (*x - sid->down.x);
+   else *x = sid->down.sx;
+   if (sid->down.dir_y) *y = sid->down.sy - (*y - sid->down.y);
+   else *y = sid->down.sy;
+
+   if ((sid->down.dir_x) || (sid->down.dir_y))
+     {
+        if (!((sid->down.dir_x) && (sid->down.dir_y)))
+          {
+             if (sid->down.dir_x) *y = sid->down.locked_y;
+             else *x = sid->down.locked_x;
+          }
+     }
+
+   psd->api->pos_min_get(sid->pan_obj, &minx, &miny);
+
+   if (*x < minx)
+     *x += (minx - *x) * _elm_config->thumbscroll_border_friction;
+   else if (sid->content_info.w <= sid->w)
+     *x += (sid->down.sx - *x) * _elm_config->thumbscroll_border_friction;
+   else if ((sid->content_info.w - sid->w + minx) < *x)
+     *x += (sid->content_info.w - sid->w + minx - *x) *
+       _elm_config->thumbscroll_border_friction;
+
+   if (*y < miny)
+     *y += (miny - *y) * _elm_config->thumbscroll_border_friction;
+   else if (sid->content_info.h <= sid->h)
+     *y += (sid->down.sy - *y) * _elm_config->thumbscroll_border_friction;
+   else if ((sid->content_info.h - sid->h + miny) < *y)
+     *y += (sid->content_info.h - sid->h + miny - *y) *
+       _elm_config->thumbscroll_border_friction;
+}
+
+static Eina_Bool
+_elm_scroll_hold_animator(void *data)
+{
+   Elm_Scrollable_Smart_Interface_Data *sid = data;
+   Evas_Coord ox = 0, oy = 0, fx = 0, fy = 0;
+
+   fx = sid->down.hold_x;
+   fy = sid->down.hold_y;
+   if (_elm_config->scroll_smooth_amount > 0.0)
+     {
+        int i, count = 0;
+        Evas_Coord basex = 0, basey = 0, x, y;
+        double dt, t, tdiff, tnow, twin;
+        struct
+        {
+           Evas_Coord x, y, dx, dy;
+           double     t, dt;
+        } pos[60];
+
+        tdiff = sid->down.hist.est_timestamp_diff;
+        tnow = ecore_time_get() - tdiff;
+        t = tnow;
+        twin = _elm_config->scroll_smooth_time_window;
+        for (i = 0; i < 60; i++)
+          {
+             // oldest point is sid->down.history[i]
+             // newset is sid->down.history[0]
+             dt = t - sid->down.history[i].timestamp;
+             if (dt > twin)
+               {
+                  i--;
+                  break;
+               }
+             x = sid->down.history[i].x;
+             y = sid->down.history[i].y;
+             _elm_scroll_down_coord_eval(sid, &x, &y);
+             if (i == 0)
+               {
+                  basex = x;
+                  basey = y;
+               }
+             pos[i].x = x - basex;
+             pos[i].y = y - basey;
+             pos[i].t =
+               sid->down.history[i].timestamp - sid->down.history[0].timestamp;
+             count++;
+          }
+        count = i;
+        if (count >= 2)
+          {
+             double dtsum = 0.0, tadd, maxdt;
+             double dxsum = 0.0, dysum = 0.0, xsum = 0.0, ysum = 0.0;
+
+             for (i = 0; i < (count - 1); i++)
+               {
+                  pos[i].dx = pos[i].x - pos[i + 1].x;
+                  pos[i].dy = pos[i].y - pos[i + 1].y;
+                  pos[i].dt = pos[i].t - pos[i + 1].t;
+                  dxsum += pos[i].dx;
+                  dysum += pos[i].dy;
+                  dtsum += pos[i].dt;
+                  xsum += pos[i].x;
+                  ysum += pos[i].y;
+               }
+             maxdt = pos[i].t;
+             dxsum /= (double)i;
+             dysum /= (double)i;
+             dtsum /= (double)i;
+             xsum /= (double)i;
+             ysum /= (double)i;
+             tadd = tnow - sid->down.history[0].timestamp +
+               _elm_config->scroll_smooth_future_time;
+             tadd = tadd - (maxdt / 2);
+#define WEIGHT(n, o, v) n = (((double)o * (1.0 - v)) + ((double)n * v))
+             WEIGHT(tadd, sid->down.hist.tadd,
+                    _elm_config->scroll_smooth_history_weight);
+             WEIGHT(dxsum, sid->down.hist.dxsum,
+                    _elm_config->scroll_smooth_history_weight);
+             WEIGHT(dysum, sid->down.hist.dysum,
+                    _elm_config->scroll_smooth_history_weight);
+             fx = basex + xsum + ((dxsum * tadd) / dtsum);
+             fy = basey + ysum + ((dysum * tadd) / dtsum);
+             sid->down.hist.tadd = tadd;
+             sid->down.hist.dxsum = dxsum;
+             sid->down.hist.dysum = dysum;
+             WEIGHT(fx, sid->down.hold_x, _elm_config->scroll_smooth_amount);
+             WEIGHT(fy, sid->down.hold_y, _elm_config->scroll_smooth_amount);
+#undef WEIGHT
+          }
+     }
+
+   _elm_scroll_content_pos_get(sid->obj, &ox, &oy);
+   if (sid->down.dir_x)
+     {
+        if ((!sid->obj) ||
+            (!elm_widget_drag_child_locked_x_get(sid->obj)))
+          ox = fx;
+     }
+   if (sid->down.dir_y)
+     {
+        if ((!sid->obj) ||
+            (!elm_widget_drag_child_locked_y_get(sid->obj)))
+          oy = fy;
+     }
+
+   _elm_scroll_content_pos_set(sid->obj, ox, oy);
+   return ECORE_CALLBACK_RENEW;
+}
+
+static Eina_Bool
+_elm_scroll_on_hold_animator(void *data)
+{
+   double t, td;
+   double vx, vy;
+   Evas_Coord x, y, ox, oy;
+   Elm_Scrollable_Smart_Interface_Data *sid;
+
+   sid = data;
+   t = ecore_loop_time_get();
+   if (sid->down.onhold_tlast > 0.0)
+     {
+        td = t - sid->down.onhold_tlast;
+        vx = sid->down.onhold_vx * td *
+          (double)_elm_config->thumbscroll_threshold * 2.0;
+        vy = sid->down.onhold_vy * td *
+          (double)_elm_config->thumbscroll_threshold * 2.0;
+        _elm_scroll_content_pos_get(sid->obj, &ox, &oy);
+        x = ox;
+        y = oy;
+
+        if (sid->down.dir_x)
+          {
+             if ((!sid->obj) ||
+                 (!elm_widget_drag_child_locked_x_get(sid->obj)))
+               {
+                  sid->down.onhold_vxe += vx;
+                  x = ox + (int)sid->down.onhold_vxe;
+                  sid->down.onhold_vxe -= (int)sid->down.onhold_vxe;
+               }
+          }
+
+        if (sid->down.dir_y)
+          {
+             if ((!sid->obj) ||
+                 (!elm_widget_drag_child_locked_y_get(sid->obj)))
+               {
+                  sid->down.onhold_vye += vy;
+                  y = oy + (int)sid->down.onhold_vye;
+                  sid->down.onhold_vye -= (int)sid->down.onhold_vye;
+               }
+          }
+
+        _elm_scroll_content_pos_set(sid->obj, x, y);
+     }
+   sid->down.onhold_tlast = t;
+
+   return ECORE_CALLBACK_RENEW;
+}
+
+static void
+_elm_scroll_mouse_move_event_cb(void *data,
+                                Evas *e,
+                                Evas_Object *obj __UNUSED__,
+                                void *event_info)
+{
+   Elm_Scrollable_Smart_Interface_Data *sid = data;
+   Evas_Event_Mouse_Move *ev;
+   Evas_Coord x = 0, y = 0;
+
+   if (!sid->pan_obj) return;
+
+   ELM_PAN_DATA_GET(sid->pan_obj, psd);
+
+   ev = event_info;
+   if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD)
+     sid->down.hold_parent = EINA_TRUE;
+   evas_post_event_callback_push(e, _elm_scroll_post_event_move, sid);
+
+   // FIXME: respect elm_widget_scroll_hold_get of parent container
+   if (_elm_config->thumbscroll_enable)
+     {
+        if (sid->down.now)
+          {
+             int dodir = 0;
+
+             if ((sid->scrollto.x.animator) && (!sid->hold) && (!sid->freeze))
+               {
+                  Evas_Coord px;
+                  ecore_animator_del(sid->scrollto.x.animator);
+                  sid->scrollto.x.animator = NULL;
+                  psd->api->pos_get(sid->pan_obj, &px, NULL);
+                  sid->down.sx = px;
+                  sid->down.x = sid->down.history[0].x;
+               }
+
+             if ((sid->scrollto.y.animator) && (!sid->hold) && (!sid->freeze))
+               {
+                  Evas_Coord py;
+                  ecore_animator_del(sid->scrollto.y.animator);
+                  sid->scrollto.y.animator = NULL;
+                  psd->api->pos_get(sid->pan_obj, NULL, &py);
+                  sid->down.sy = py;
+                  sid->down.y = sid->down.history[0].y;
+               }
+
+#ifdef SCROLLDBG
+             DBG("::: %i %i\n", ev->cur.canvas.x, ev->cur.canvas.y);
+#endif
+             memmove(&(sid->down.history[1]), &(sid->down.history[0]),
+                     sizeof(sid->down.history[0]) * (60 - 1));
+#ifdef EVTIME
+             sid->down.history[0].timestamp = ev->timestamp / 1000.0;
+             sid->down.history[0].localtimestamp = ecore_loop_time_get();
+#else
+             sid->down.history[0].timestamp = ecore_loop_time_get();
+#endif
+             sid->down.history[0].x = ev->cur.canvas.x;
+             sid->down.history[0].y = ev->cur.canvas.y;
+
+             if (!sid->down.dragged_began)
+               {
+                  x = ev->cur.canvas.x - sid->down.x;
+                  y = ev->cur.canvas.y - sid->down.y;
+
+                  sid->down.hdir = -1;
+                  sid->down.vdir = -1;
+
+                  if (x > 0) sid->down.hdir = LEFT;
+                  else if (x < 0)
+                    sid->down.hdir = RIGHT;
+                  if (y > 0) sid->down.vdir = UP;
+                  else if (y < 0)
+                    sid->down.vdir = DOWN;
+
+                  if (x < 0) x = -x;
+                  if (y < 0) y = -y;
+
+                  if ((sid->one_direction_at_a_time) &&
+                      (!((sid->down.dir_x) || (sid->down.dir_y))))
+                    {
+                       if (x > _elm_config->thumbscroll_threshold)
+                         {
+                            if (x > (y * 2))
+                              {
+                                 sid->down.dir_x = EINA_TRUE;
+                                 sid->down.dir_y = EINA_FALSE;
+                                 dodir++;
+                              }
+                         }
+                       if (y > _elm_config->thumbscroll_threshold)
+                         {
+                            if (y > (x * 2))
+                              {
+                                 sid->down.dir_x = EINA_FALSE;
+                                 sid->down.dir_y = EINA_TRUE;
+                                 dodir++;
+                              }
+                         }
+                       if (!dodir)
+                         {
+                            sid->down.dir_x = EINA_TRUE;
+                            sid->down.dir_y = EINA_TRUE;
+                         }
+                    }
+                  else
+                    {
+                       sid->down.dir_x = EINA_TRUE;
+                       sid->down.dir_y = EINA_TRUE;
+                    }
+               }
+             if ((!sid->hold) && (!sid->freeze))
+               {
+                  if ((sid->down.dragged) ||
+                      (((x * x) + (y * y)) >
+                       (_elm_config->thumbscroll_threshold *
+                        _elm_config->thumbscroll_threshold)))
+                    {
+                       sid->down.dragged_began = EINA_TRUE;
+                       if (!sid->down.dragged)
+                         {
+                            sid->down.want_dragged = EINA_TRUE;
+                            ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
+                         }
+                       if (sid->down.dragged)
+                         {
+                            ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
+                         }
+                       if (sid->down.dir_x)
+                         x = sid->down.sx - (ev->cur.canvas.x - sid->down.x);
+                       else
+                         x = sid->down.sx;
+                       if (sid->down.dir_y)
+                         y = sid->down.sy - (ev->cur.canvas.y - sid->down.y);
+                       else
+                         y = sid->down.sy;
+                       if (sid->down.want_reset)
+                         {
+                            sid->down.x = ev->cur.canvas.x;
+                            sid->down.y = ev->cur.canvas.y;
+                            sid->down.want_reset = EINA_FALSE;
+                         }
+                       if ((sid->down.dir_x) || (sid->down.dir_y))
+                         {
+                            if (!sid->down.locked)
+                              {
+                                 sid->down.locked_x = x;
+                                 sid->down.locked_y = y;
+                                 sid->down.locked = EINA_TRUE;
+                              }
+                            if (!((sid->down.dir_x) && (sid->down.dir_y)))
+                              {
+                                 if (sid->down.dir_x) y = sid->down.locked_y;
+                                 else x = sid->down.locked_x;
+                              }
+                         }
+                       {
+                          Evas_Coord minx, miny;
+
+                          psd->api->pos_min_get(sid->pan_obj, &minx, &miny);
+                          if (y < miny)
+                            y += (miny - y) *
+                              _elm_config->thumbscroll_border_friction;
+                          else if (sid->content_info.h <= sid->h)
+                            y += (sid->down.sy - y) *
+                              _elm_config->thumbscroll_border_friction;
+                          else if ((sid->content_info.h - sid->h + miny) < y)
+                            y += (sid->content_info.h - sid->h + miny - y) *
+                              _elm_config->thumbscroll_border_friction;
+                          if (x < minx)
+                            x += (minx - x) *
+                              _elm_config->thumbscroll_border_friction;
+                          else if (sid->content_info.w <= sid->w)
+                            x += (sid->down.sx - x) *
+                              _elm_config->thumbscroll_border_friction;
+                          else if ((sid->content_info.w - sid->w + minx) < x)
+                            x += (sid->content_info.w - sid->w + minx - x) *
+                              _elm_config->thumbscroll_border_friction;
+                       }
+
+                       sid->down.hold_x = x;
+                       sid->down.hold_y = y;
+                       if (!sid->down.hold_animator)
+                         sid->down.hold_animator =
+                           ecore_animator_add(_elm_scroll_hold_animator, sid);
+                    }
+                  else
+                    {
+                       if (sid->down.dragged_began)
+                         {
+                            ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
+                            if (!sid->down.hold)
+                              {
+                                 sid->down.hold = EINA_TRUE;
+                                 evas_event_feed_hold
+                                   (e, 1, ev->timestamp, ev->data);
+                              }
+                         }
+                    }
+               }
+             else if (!sid->freeze)
+               {
+                  double vx = 0.0, vy = 0.0;
+
+                  x = ev->cur.canvas.x - sid->x;
+                  y = ev->cur.canvas.y - sid->y;
+                  if (x < _elm_config->thumbscroll_threshold)
+                    {
+                       if (_elm_config->thumbscroll_threshold > 0.0)
+                         vx = -(double)(_elm_config->thumbscroll_threshold - x)
+                           / _elm_config->thumbscroll_threshold;
+                       else
+                         vx = -1.0;
+                    }
+                  else if (x > (sid->w - _elm_config->thumbscroll_threshold))
+                    {
+                       if (_elm_config->thumbscroll_threshold > 0.0)
+                         vx = (double)(_elm_config->thumbscroll_threshold -
+                                       (sid->w - x)) /
+                           _elm_config->thumbscroll_threshold;
+                       else
+                         vx = 1.0;
+                    }
+                  if (y < _elm_config->thumbscroll_threshold)
+                    {
+                       if (_elm_config->thumbscroll_threshold > 0.0)
+                         vy = -(double)(_elm_config->thumbscroll_threshold - y)
+                           / _elm_config->thumbscroll_threshold;
+                       else
+                         vy = -1.0;
+                    }
+                  else if (y > (sid->h - _elm_config->thumbscroll_threshold))
+                    {
+                       if (_elm_config->thumbscroll_threshold > 0.0)
+                         vy = (double)(_elm_config->thumbscroll_threshold -
+                                       (sid->h - y)) /
+                           _elm_config->thumbscroll_threshold;
+                       else
+                         vy = 1.0;
+                    }
+                  if ((vx != 0.0) || (vy != 0.0))
+                    {
+                       sid->down.onhold_vx = vx;
+                       sid->down.onhold_vy = vy;
+                       if (!sid->down.onhold_animator)
+                         {
+                            sid->down.onhold_vxe = 0.0;
+                            sid->down.onhold_vye = 0.0;
+                            sid->down.onhold_tlast = 0.0;
+                            sid->down.onhold_animator = ecore_animator_add
+                                (_elm_scroll_on_hold_animator, sid);
+                         }
+                    }
+                  else
+                    {
+                       if (sid->down.onhold_animator)
+                         {
+                            ecore_animator_del(sid->down.onhold_animator);
+                            sid->down.onhold_animator = NULL;
+                            if (sid->content_info.resized)
+                              _elm_scroll_wanted_region_set(sid->obj);
+                         }
+                    }
+               }
+          }
+     }
+}
+
+static void
+_elm_scroll_page_adjust(Elm_Scrollable_Smart_Interface_Data *sid)
+{
+   Evas_Coord x, y, w, h;
+
+   if (!_paging_is_enabled(sid)) return;
+
+   _elm_scroll_content_viewport_size_get(sid->obj, &w, &h);
+
+   x = _elm_scroll_page_x_get(sid, 0);
+   y = _elm_scroll_page_y_get(sid, 0);
+
+   _elm_scroll_content_region_set(sid->obj, x, y, w, h);
+}
+
+static void
+_elm_scroll_reconfigure(Elm_Scrollable_Smart_Interface_Data *sid)
+{
+   _elm_scroll_scroll_bar_size_adjust(sid);
+   _elm_scroll_page_adjust(sid);
+}
+
+static void
+_on_edje_move(void *data,
+              Evas *e __UNUSED__,
+              Evas_Object *edje_obj,
+              void *event_info __UNUSED__)
+{
+   Elm_Scrollable_Smart_Interface_Data *sid = data;
+   int x, y;
+
+   evas_object_geometry_get(edje_obj, &x, &y, NULL, NULL);
+
+   sid->x = x;
+   sid->y = y;
+
+   _elm_scroll_reconfigure(sid);
+}
+
+static void
+_on_edje_resize(void *data,
+                Evas *e __UNUSED__,
+                Evas_Object *edje_obj,
+                void *event_info __UNUSED__)
+{
+   Elm_Scrollable_Smart_Interface_Data *sid = data;
+   int w, h;
+
+   evas_object_geometry_get(edje_obj, NULL, NULL, &w, &h);
+
+   sid->w = w;
+   sid->h = h;
+
+   _elm_scroll_reconfigure(sid);
+   _elm_scroll_wanted_region_set(sid->obj);
+}
+
+static void
+_elm_scroll_objects_attach(Evas_Object *obj)
+{
+   ELM_SCROLL_IFACE_DATA_GET(obj, sid);
+
+   evas_object_event_callback_add
+     (sid->edje_obj, EVAS_CALLBACK_RESIZE, _on_edje_resize, sid);
+   evas_object_event_callback_add
+     (sid->edje_obj, EVAS_CALLBACK_MOVE, _on_edje_move, sid);
+
+   edje_object_signal_callback_add
+     (sid->edje_obj, "drag", "elm.dragable.vbar", _elm_scroll_edje_drag_v_cb,
+     sid);
+   edje_object_signal_callback_add
+     (sid->edje_obj, "drag,set", "elm.dragable.vbar",
+     _elm_scroll_edje_drag_v_cb, sid);
+   edje_object_signal_callback_add
+     (sid->edje_obj, "drag,start", "elm.dragable.vbar",
+     _elm_scroll_edje_drag_v_start_cb, sid);
+   edje_object_signal_callback_add
+     (sid->edje_obj, "drag,stop", "elm.dragable.vbar",
+     _elm_scroll_edje_drag_v_stop_cb, sid);
+   edje_object_signal_callback_add
+     (sid->edje_obj, "drag,step", "elm.dragable.vbar",
+     _elm_scroll_edje_drag_v_cb, sid);
+   edje_object_signal_callback_add
+     (sid->edje_obj, "drag,page", "elm.dragable.vbar",
+     _elm_scroll_edje_drag_v_cb, sid);
+   edje_object_signal_callback_add
+     (sid->edje_obj, "drag", "elm.dragable.hbar", _elm_scroll_edje_drag_h_cb,
+     sid);
+   edje_object_signal_callback_add
+     (sid->edje_obj, "drag,set", "elm.dragable.hbar",
+     _elm_scroll_edje_drag_h_cb, sid);
+   edje_object_signal_callback_add
+     (sid->edje_obj, "drag,start", "elm.dragable.hbar",
+     _elm_scroll_edje_drag_h_start_cb, sid);
+   edje_object_signal_callback_add
+     (sid->edje_obj, "drag,stop", "elm.dragable.hbar",
+     _elm_scroll_edje_drag_h_stop_cb, sid);
+   edje_object_signal_callback_add
+     (sid->edje_obj, "drag,step", "elm.dragable.hbar",
+     _elm_scroll_edje_drag_h_cb, sid);
+   edje_object_signal_callback_add
+     (sid->edje_obj, "drag,page", "elm.dragable.hbar",
+     _elm_scroll_edje_drag_h_cb, sid);
+
+   evas_object_event_callback_add
+     (sid->event_rect, EVAS_CALLBACK_MOUSE_WHEEL, _elm_scroll_wheel_event_cb,
+     sid);
+   evas_object_event_callback_add
+     (sid->event_rect, EVAS_CALLBACK_MOUSE_DOWN,
+     _elm_scroll_mouse_down_event_cb, sid);
+   evas_object_event_callback_add
+     (sid->event_rect, EVAS_CALLBACK_MOUSE_UP,
+     _elm_scroll_mouse_up_event_cb, sid);
+   evas_object_event_callback_add
+     (sid->event_rect, EVAS_CALLBACK_MOUSE_MOVE,
+     _elm_scroll_mouse_move_event_cb, sid);
+}
+
+static void
+_elm_scroll_objects_set(Evas_Object *obj,
+                        Evas_Object *edje_object,
+                        Evas_Object *hit_rectangle)
+{
+   Evas_Coord mw, mh;
+
+   ELM_SCROLL_IFACE_DATA_GET_OR_RETURN(obj, sid);
+
+   if (!edje_object) return;
+   if (sid->edje_obj) evas_object_del(sid->edje_obj);
+   sid->edje_obj = edje_object;
+
+   if (sid->event_rect) evas_object_del(sid->event_rect);
+   sid->event_rect = hit_rectangle;
+   evas_object_repeat_events_set(hit_rectangle, EINA_TRUE);
+
+   _elm_scroll_objects_attach(obj);
+
+   edje_object_scale_set
+     (sid->edje_obj, elm_widget_scale_get(sid->obj) *
+     elm_config_scale_get());
+
+   mw = mh = -1;
+   elm_coords_finger_size_adjust(1, &mw, 1, &mh);
+   if (edje_object_part_exists(sid->edje_obj, "elm.scrollbar.base"))
+     {
+        Evas_Object *base;
+
+        base = edje_object_part_swallow_get
+            (sid->edje_obj, "elm.scrollbar.base");
+        if (!base)
+          {
+             base = evas_object_rectangle_add
+                 (evas_object_evas_get(sid->edje_obj));
+             evas_object_color_set(base, 0, 0, 0, 0);
+             edje_object_part_swallow
+               (sid->edje_obj, "elm.scrollbar.base", base);
+          }
+        if (!_elm_config->thumbscroll_enable)
+          evas_object_size_hint_min_set(base, mw, mh);
+     }
+   sid->vbar_visible = !sid->vbar_visible;
+   sid->hbar_visible = !sid->hbar_visible;
+
+   _elm_scroll_scroll_bar_visibility_adjust(sid);
+}
+
+static void
+_elm_scroll_scroll_bar_reset(Elm_Scrollable_Smart_Interface_Data *sid)
+{
+   Evas_Coord px = 0, py = 0, minx = 0, miny = 0;
+
+   if (!sid->edje_obj) return;
+
+   edje_object_part_drag_value_set
+     (sid->edje_obj, "elm.dragable.vbar", 0.0, 0.0);
+   edje_object_part_drag_value_set
+     (sid->edje_obj, "elm.dragable.hbar", 0.0, 0.0);
+   if ((!sid->content) && (!sid->extern_pan))
+     {
+        edje_object_part_drag_size_set
+          (sid->edje_obj, "elm.dragable.vbar", 1.0, 1.0);
+        edje_object_part_drag_size_set
+          (sid->edje_obj, "elm.dragable.hbar", 1.0, 1.0);
+     }
+   if (sid->pan_obj)
+     {
+        ELM_PAN_DATA_GET(sid->pan_obj, psd);
+
+        psd->api->pos_min_get(sid->pan_obj, &minx, &miny);
+        psd->api->pos_get(sid->pan_obj, &px, &py);
+        psd->api->pos_set(sid->pan_obj, minx, miny);
+     }
+   if ((px != minx) || (py != miny))
+     edje_object_signal_emit(sid->edje_obj, "elm,action,scroll", "elm");
+}
+
+/* even external pan objects get this */
+static void
+_elm_scroll_pan_changed_cb(void *data,
+                           Evas_Object *obj __UNUSED__,
+                           void *event_info __UNUSED__)
+{
+   Evas_Coord w, h;
+   Elm_Scrollable_Smart_Interface_Data *sid = data;
+
+   if (!sid->pan_obj) return;
+
+   ELM_PAN_DATA_GET(sid->pan_obj, psd);
+
+   psd->api->content_size_get(sid->pan_obj, &w, &h);
+   if ((w != sid->content_info.w) || (h != sid->content_info.h))
+     {
+        sid->content_info.w = w;
+        sid->content_info.h = h;
+        _elm_scroll_scroll_bar_size_adjust(sid);
+
+        evas_object_size_hint_min_set
+          (sid->edje_obj, sid->content_info.w, sid->content_info.h);
+        sid->content_info.resized = EINA_TRUE;
+        _elm_scroll_wanted_region_set(sid->obj);
+     }
+}
+
+static void
+_elm_scroll_content_del_cb(void *data,
+                           Evas *e __UNUSED__,
+                           Evas_Object *obj __UNUSED__,
+                           void *event_info __UNUSED__)
+{
+   Elm_Scrollable_Smart_Interface_Data *sid = data;
+
+   sid->content = NULL;
+   _elm_scroll_scroll_bar_size_adjust(sid);
+   _elm_scroll_scroll_bar_reset(sid);
+}
+
+static void
+_elm_scroll_content_set(Evas_Object *obj,
+                        Evas_Object *content)
+{
+   Evas_Coord w, h;
+   Evas_Object *o;
+
+   ELM_SCROLL_IFACE_DATA_GET_OR_RETURN(obj, sid);
+
+   if (!sid->edje_obj) return;
+
+   if (sid->content)
+     {
+        /* if we had content, for sure we had a pan object */
+        _elm_pan_content_set(sid->pan_obj, NULL);
+        evas_object_event_callback_del_full
+          (sid->content, EVAS_CALLBACK_DEL, _elm_scroll_content_del_cb, sid);
+     }
+
+   sid->content = content;
+   sid->wx = sid->wy = 0;
+   /* (-1) means want viewports size */
+   sid->ww = sid->wh = -1;
+   if (!content) return;
+
+   if (!sid->pan_obj)
+     {
+        o = _elm_pan_add(evas_object_evas_get(obj));
+        sid->pan_obj = o;
+        evas_object_smart_callback_add
+          (o, SIG_CHANGED, _elm_scroll_pan_changed_cb, sid);
+        edje_object_part_swallow(sid->edje_obj, "elm.swallow.content", o);
+     }
+
+   ELM_PAN_DATA_GET(sid->pan_obj, psd);
+
+   evas_object_event_callback_add
+     (content, EVAS_CALLBACK_DEL, _elm_scroll_content_del_cb, sid);
+
+   _elm_pan_content_set(sid->pan_obj, content);
+   psd->api->content_size_get(sid->pan_obj, &w, &h);
+   sid->content_info.w = w;
+   sid->content_info.h = h;
+
+   _elm_scroll_scroll_bar_size_adjust(sid);
+   _elm_scroll_scroll_bar_reset(sid);
+}
+
+static void
+_elm_scroll_extern_pan_set(Evas_Object *obj,
+                           Evas_Object *pan)
+{
+   ELM_SCROLL_IFACE_DATA_GET_OR_RETURN(obj, sid);
+
+   if (!sid->edje_obj) return;
+
+   _elm_scroll_content_set(obj, NULL);
+
+   if (sid->pan_obj)
+     {
+        evas_object_smart_callback_del
+          (sid->pan_obj, SIG_CHANGED, _elm_scroll_pan_changed_cb);
+     }
+
+   if (sid->extern_pan)
+     {
+        if (sid->pan_obj)
+          {
+             /* not owned by scroller, just leave (was external already) */
+             edje_object_part_unswallow(sid->edje_obj, sid->pan_obj);
+             sid->pan_obj = NULL;
+          }
+     }
+   else
+     {
+        if (sid->pan_obj)
+          {
+             evas_object_del(sid->pan_obj);
+             sid->pan_obj = NULL;
+          }
+     }
+   if (!pan)
+     {
+        sid->extern_pan = EINA_FALSE;
+        return;
+     }
+
+   sid->pan_obj = pan;
+
+   sid->extern_pan = EINA_TRUE;
+   evas_object_smart_callback_add
+     (sid->pan_obj, SIG_CHANGED, _elm_scroll_pan_changed_cb, sid);
+   edje_object_part_swallow
+     (sid->edje_obj, "elm.swallow.content", sid->pan_obj);
+}
+
+static void
+_elm_scroll_drag_start_cb_set(Evas_Object *obj,
+                              void (*drag_start_cb)(Evas_Object *obj,
+                                                    void *data))
+{
+   ELM_SCROLL_IFACE_DATA_GET_OR_RETURN(obj, sid);
+
+   sid->cb_func.drag_start = drag_start_cb;
+}
+
+static void
+_elm_scroll_drag_stop_cb_set(Evas_Object *obj,
+                             void (*drag_stop_cb)(Evas_Object *obj,
+                                                  void *data))
+{
+   ELM_SCROLL_IFACE_DATA_GET_OR_RETURN(obj, sid);
+
+   sid->cb_func.drag_stop = drag_stop_cb;
+}
+
+static void
+_elm_scroll_animate_start_cb_set(Evas_Object *obj,
+                                 void (*animate_start_cb)(Evas_Object *obj,
+                                                          void *data))
+{
+   ELM_SCROLL_IFACE_DATA_GET_OR_RETURN(obj, sid);
+
+   sid->cb_func.animate_start = animate_start_cb;
+}
+
+static void
+_elm_scroll_animate_stop_cb_set(Evas_Object *obj,
+                                void (*animate_stop_cb)(Evas_Object *obj,
+                                                        void *data))
+{
+   ELM_SCROLL_IFACE_DATA_GET_OR_RETURN(obj, sid);
+
+   sid->cb_func.animate_stop = animate_stop_cb;
+}
+
+static void
+_elm_scroll_scroll_cb_set(Evas_Object *obj,
+                          void (*scroll_cb)(Evas_Object *obj,
+                                            void *data))
+{
+   ELM_SCROLL_IFACE_DATA_GET_OR_RETURN(obj, sid);
+
+   sid->cb_func.scroll = scroll_cb;
+}
+
+static void
+_elm_scroll_edge_left_cb_set(Evas_Object *obj,
+                             void (*edge_left_cb)(Evas_Object *obj,
+                                                  void *data))
+{
+   ELM_SCROLL_IFACE_DATA_GET_OR_RETURN(obj, sid);
+
+   sid->cb_func.edge_left = edge_left_cb;
+}
+
+static void
+_elm_scroll_edge_right_cb_set(Evas_Object *obj,
+                              void (*edge_right_cb)(Evas_Object *obj,
+                                                    void *data))
+{
+   ELM_SCROLL_IFACE_DATA_GET_OR_RETURN(obj, sid);
+
+   sid->cb_func.edge_right = edge_right_cb;
+}
+
+static void
+_elm_scroll_edge_top_cb_set(Evas_Object *obj,
+                            void (*edge_top_cb)(Evas_Object *obj,
+                                                void *data))
+{
+   ELM_SCROLL_IFACE_DATA_GET_OR_RETURN(obj, sid);
+
+   sid->cb_func.edge_top = edge_top_cb;
+}
+
+static void
+_elm_scroll_edge_bottom_cb_set(Evas_Object *obj,
+                               void (*edge_bottom_cb)(Evas_Object *obj,
+                                                      void *data))
+{
+   ELM_SCROLL_IFACE_DATA_GET_OR_RETURN(obj, sid);
+
+   sid->cb_func.edge_bottom = edge_bottom_cb;
+}
+
+static void
+_elm_content_min_limit_cb_set(Evas_Object *obj,
+                              void (*content_min_limit_cb)(Evas_Object *obj,
+                                                           Eina_Bool w,
+                                                           Eina_Bool h))
+{
+   ELM_SCROLL_IFACE_DATA_GET_OR_RETURN(obj, sid);
+
+   sid->cb_func.content_min_limit = content_min_limit_cb;
+}
+
+static Eina_Bool
+_elm_scroll_momentum_animator_disabled_get(const Evas_Object *obj)
+{
+   ELM_SCROLL_IFACE_DATA_GET_OR_RETURN_VAL(obj, sid, EINA_FALSE);
+
+   return sid->momentum_animator_disabled;
+}
+
+static void
+_elm_scroll_momentum_animator_disabled_set(Evas_Object *obj,
+                                           Eina_Bool disabled)
+{
+   ELM_SCROLL_IFACE_DATA_GET_OR_RETURN(obj, sid);
+
+   sid->momentum_animator_disabled = disabled;
+   if (sid->momentum_animator_disabled)
+     {
+        if (sid->down.momentum_animator)
+          {
+             ecore_animator_del(sid->down.momentum_animator);
+             sid->down.momentum_animator = NULL;
+             if (sid->content_info.resized)
+               _elm_scroll_wanted_region_set(sid->obj);
+          }
+     }
+}
+
+static Eina_Bool
+_elm_scroll_bounce_animator_disabled_get(const Evas_Object *obj)
+{
+   ELM_SCROLL_IFACE_DATA_GET_OR_RETURN_VAL(obj, sid, EINA_FALSE);
+
+   return sid->bounce_animator_disabled;
+}
+
+static void
+_elm_scroll_bounce_animator_disabled_set(Evas_Object *obj,
+                                         Eina_Bool disabled)
+{
+   ELM_SCROLL_IFACE_DATA_GET_OR_RETURN(obj, sid);
+
+   sid->bounce_animator_disabled = disabled;
+   if (sid->bounce_animator_disabled)
+     {
+        if (sid->scrollto.x.animator)
+          {
+             ecore_animator_del(sid->scrollto.x.animator);
+             sid->scrollto.x.animator = NULL;
+          }
+
+        if (sid->scrollto.y.animator)
+          {
+             ecore_animator_del(sid->scrollto.y.animator);
+             sid->scrollto.y.animator = NULL;
+          }
+     }
+}
+
+static Eina_Bool
+_elm_scroll_wheel_disabled_get(const Evas_Object *obj)
+{
+   ELM_SCROLL_IFACE_DATA_GET_OR_RETURN_VAL(obj, sid, EINA_FALSE);
+
+   return sid->wheel_disabled;
+}
+
+static void
+_elm_scroll_wheel_disabled_set(Evas_Object *obj,
+                               Eina_Bool disabled)
+{
+   ELM_SCROLL_IFACE_DATA_GET_OR_RETURN(obj, sid);
+
+   if (!sid->event_rect) return;
+
+   if ((!sid->wheel_disabled) && (disabled))
+     evas_object_event_callback_del_full
+       (sid->event_rect, EVAS_CALLBACK_MOUSE_WHEEL,
+       _elm_scroll_wheel_event_cb, sid);
+   else if ((sid->wheel_disabled) && (!disabled))
+     evas_object_event_callback_add
+       (sid->event_rect, EVAS_CALLBACK_MOUSE_WHEEL,
+       _elm_scroll_wheel_event_cb, sid);
+   sid->wheel_disabled = disabled;
+}
+
+static void
+_elm_scroll_step_size_set(Evas_Object *obj,
+                          Evas_Coord x,
+                          Evas_Coord y)
+{
+   ELM_SCROLL_IFACE_DATA_GET_OR_RETURN(obj, sid);
+
+   if (x < 1) x = 1;
+   if (y < 1) y = 1;
+   sid->step.x = x;
+   sid->step.y = y;
+
+   _elm_scroll_scroll_bar_size_adjust(sid);
+}
+
+static void
+_elm_scroll_step_size_get(const Evas_Object *obj,
+                          Evas_Coord *x,
+                          Evas_Coord *y)
+{
+   ELM_SCROLL_IFACE_DATA_GET_OR_RETURN(obj, sid);
+
+   if (x) *x = sid->step.x;
+   if (y) *y = sid->step.y;
+}
+
+static void
+_elm_scroll_page_size_set(Evas_Object *obj,
+                          Evas_Coord x,
+                          Evas_Coord y)
+{
+   ELM_SCROLL_IFACE_DATA_GET_OR_RETURN(obj, sid);
+
+   sid->page.x = x;
+   sid->page.y = y;
+
+   _elm_scroll_scroll_bar_size_adjust(sid);
+}
+
+static void
+_elm_scroll_page_size_get(const Evas_Object *obj,
+                          Evas_Coord *x,
+                          Evas_Coord *y)
+{
+   ELM_SCROLL_IFACE_DATA_GET_OR_RETURN(obj, sid);
+
+   if (x) *x = sid->page.x;
+   if (y) *y = sid->page.y;
+}
+
+static void
+_elm_scroll_policy_set(Evas_Object *obj,
+                       Elm_Scroller_Policy hbar,
+                       Elm_Scroller_Policy vbar)
+{
+   ELM_SCROLL_IFACE_DATA_GET_OR_RETURN(obj, sid);
+
+   if (!sid->edje_obj) return;
+
+   if ((sid->hbar_flags == hbar) && (sid->vbar_flags == vbar)) return;
+
+   sid->hbar_flags = hbar;
+   sid->vbar_flags = vbar;
+   if (sid->hbar_flags == ELM_SCROLLER_POLICY_ON)
+     edje_object_signal_emit
+       (sid->edje_obj, "elm,action,show_always,hbar", "elm");
+   else if (sid->hbar_flags == ELM_SCROLLER_POLICY_OFF)
+     edje_object_signal_emit
+       (sid->edje_obj, "elm,action,hide,hbar", "elm");
+   else
+     edje_object_signal_emit
+       (sid->edje_obj, "elm,action,show_notalways,hbar", "elm");
+   if (sid->vbar_flags == ELM_SCROLLER_POLICY_ON)
+     edje_object_signal_emit
+       (sid->edje_obj, "elm,action,show_always,vbar", "elm");
+   else if (sid->vbar_flags == ELM_SCROLLER_POLICY_OFF)
+     edje_object_signal_emit
+       (sid->edje_obj, "elm,action,hide,vbar", "elm");
+   else
+     edje_object_signal_emit
+       (sid->edje_obj, "elm,action,show_notalways,vbar", "elm");
+
+   _elm_scroll_scroll_bar_size_adjust(sid);
+}
+
+static void
+_elm_scroll_policy_get(const Evas_Object *obj,
+                       Elm_Scroller_Policy *hbar,
+                       Elm_Scroller_Policy *vbar)
+{
+   ELM_SCROLL_IFACE_DATA_GET_OR_RETURN(obj, sid);
+
+   if (hbar) *hbar = sid->hbar_flags;
+   if (vbar) *vbar = sid->vbar_flags;
+}
+
+static void
+_elm_scroll_single_direction_set(Evas_Object *obj,
+                                 Eina_Bool single_dir)
+{
+   ELM_SCROLL_IFACE_DATA_GET_OR_RETURN(obj, sid);
+
+   sid->one_direction_at_a_time = single_dir;
+}
+
+static Eina_Bool
+_elm_scroll_single_direction_get(const Evas_Object *obj)
+{
+   ELM_SCROLL_IFACE_DATA_GET_OR_RETURN_VAL(obj, sid, EINA_FALSE);
+
+   return sid->one_direction_at_a_time;
+}
+
+static void
+_elm_scroll_hold_set(Evas_Object *obj,
+                     Eina_Bool hold)
+{
+   ELM_SCROLL_IFACE_DATA_GET_OR_RETURN(obj, sid);
+
+   sid->hold = hold;
+}
+
+static void
+_elm_scroll_freeze_set(Evas_Object *obj,
+                       Eina_Bool freeze)
+{
+   ELM_SCROLL_IFACE_DATA_GET_OR_RETURN(obj, sid);
+
+   sid->freeze = freeze;
+   if (sid->freeze)
+     {
+        if (sid->down.onhold_animator)
+          {
+             ecore_animator_del(sid->down.onhold_animator);
+             sid->down.onhold_animator = NULL;
+             if (sid->content_info.resized)
+               _elm_scroll_wanted_region_set(sid->obj);
+          }
+     }
+   else
+     _elm_scroll_bounce_eval(sid);
+}
+
+static void
+_elm_scroll_bounce_allow_set(Evas_Object *obj,
+                             Eina_Bool horiz,
+                             Eina_Bool vert)
+{
+   ELM_SCROLL_IFACE_DATA_GET_OR_RETURN(obj, sid);
+
+   sid->bounce_horiz = !!horiz;
+   sid->bounce_vert = !!vert;
+}
+
+static void
+_elm_scroll_bounce_allow_get(const Evas_Object *obj,
+                             Eina_Bool *horiz,
+                             Eina_Bool *vert)
+{
+   ELM_SCROLL_IFACE_DATA_GET_OR_RETURN(obj, sid);
+
+   if (horiz) *horiz = sid->bounce_horiz;
+   if (vert) *vert = sid->bounce_vert;
+}
+
+static void
+_elm_scroll_paging_set(Evas_Object *obj,
+                       double pagerel_h,
+                       double pagerel_v,
+                       Evas_Coord pagesize_h,
+                       Evas_Coord pagesize_v)
+{
+   ELM_SCROLL_IFACE_DATA_GET_OR_RETURN(obj, sid);
+
+   sid->pagerel_h = pagerel_h;
+   sid->pagerel_v = pagerel_v;
+   sid->pagesize_h = pagesize_h;
+   sid->pagesize_v = pagesize_v;
+
+   _elm_scroll_page_adjust(sid);
+}
+
+static void
+_elm_scroll_paging_get(const Evas_Object *obj,
+                       double *pagerel_h,
+                       double *pagerel_v,
+                       Evas_Coord *pagesize_h,
+                       Evas_Coord *pagesize_v)
+{
+   ELM_SCROLL_IFACE_DATA_GET_OR_RETURN(obj, sid);
+
+   if (pagerel_h) *pagerel_h = sid->pagerel_h;
+   if (pagerel_v) *pagerel_v = sid->pagerel_v;
+   if (pagesize_h) *pagesize_h = sid->pagesize_h;
+   if (pagesize_v) *pagesize_v = sid->pagesize_v;
+}
+
+static void
+_elm_scroll_current_page_get(const Evas_Object *obj,
+                             int *pagenumber_h,
+                             int *pagenumber_v)
+{
+   Evas_Coord x, y;
+
+   ELM_SCROLL_IFACE_DATA_GET_OR_RETURN(obj, sid);
+
+   _elm_scroll_content_pos_get(sid->obj, &x, &y);
+   if (pagenumber_h)
+     {
+        if (sid->pagesize_h > 0)
+          *pagenumber_h = (x + sid->pagesize_h - 1) / sid->pagesize_h;
+        else
+          *pagenumber_h = 0;
+     }
+   if (pagenumber_v)
+     {
+        if (sid->pagesize_v > 0)
+          *pagenumber_v = (y + sid->pagesize_v - 1) / sid->pagesize_v;
+        else
+          *pagenumber_v = 0;
+     }
+}
+
+static void
+_elm_scroll_last_page_get(const Evas_Object *obj,
+                          int *pagenumber_h,
+                          int *pagenumber_v)
+{
+   Evas_Coord cw, ch;
+
+   ELM_SCROLL_IFACE_DATA_GET_OR_RETURN(obj, sid);
+
+   if (!sid->pan_obj) return;
+
+   ELM_PAN_DATA_GET(sid->pan_obj, psd);
+
+   psd->api->content_size_get(sid->pan_obj, &cw, &ch);
+   if (pagenumber_h)
+     {
+        if (sid->pagesize_h > 0)
+          *pagenumber_h = cw / sid->pagesize_h + 1;
+        else
+          *pagenumber_h = 0;
+     }
+   if (pagenumber_v)
+     {
+        if (sid->pagesize_v > 0)
+          *pagenumber_v = ch / sid->pagesize_v + 1;
+        else
+          *pagenumber_v = 0;
+     }
+}
+
+static void
+_elm_scroll_page_show(Evas_Object *obj,
+                      int pagenumber_h,
+                      int pagenumber_v)
+{
+   Evas_Coord x, y, w, h;
+
+   ELM_SCROLL_IFACE_DATA_GET_OR_RETURN(obj, sid);
+
+   _elm_scroll_content_viewport_size_get(sid->obj, &w, &h);
+   if (pagenumber_h >= 0) x = sid->pagesize_h * pagenumber_h;
+   if (pagenumber_v >= 0) y = sid->pagesize_v * pagenumber_v;
+   if (_elm_scroll_content_region_show_internal(obj, &x, &y, w, h))
+     _elm_scroll_content_pos_set(obj, x, y);
+}
+
+static void
+_elm_scroll_page_bring_in(Evas_Object *obj,
+                          int pagenumber_h,
+                          int pagenumber_v)
+{
+   Evas_Coord x, y, w, h;
+
+   ELM_SCROLL_IFACE_DATA_GET_OR_RETURN(obj, sid);
+
+   _elm_scroll_content_viewport_size_get(sid->obj, &w, &h);
+   if (pagenumber_h >= 0) x = sid->pagesize_h * pagenumber_h;
+   if (pagenumber_v >= 0) y = sid->pagesize_v * pagenumber_v;
+   if (_elm_scroll_content_region_show_internal(obj, &x, &y, w, h))
+     {
+        _elm_scroll_scroll_to_x(sid, _elm_config->bring_in_scroll_friction, x);
+        _elm_scroll_scroll_to_y(sid, _elm_config->bring_in_scroll_friction, y);
+     }
+}
+
+static void
+_elm_scroll_region_bring_in(Evas_Object *obj,
+                            Evas_Coord x,
+                            Evas_Coord y,
+                            Evas_Coord w,
+                            Evas_Coord h)
+{
+   ELM_SCROLL_IFACE_DATA_GET_OR_RETURN(obj, sid);
+
+   if (_elm_scroll_content_region_show_internal(obj, &x, &y, w, h))
+     {
+        _elm_scroll_scroll_to_x(sid, _elm_config->bring_in_scroll_friction, x);
+        _elm_scroll_scroll_to_y(sid, _elm_config->bring_in_scroll_friction, y);
+     }
+}
+
+static void
+_elm_scroll_gravity_set(Evas_Object *obj,
+                        double x,
+                        double y)
+{
+   ELM_SCROLL_IFACE_DATA_GET_OR_RETURN(obj, sid);
+
+   if (!sid->pan_obj) return;
+
+   ELM_PAN_DATA_GET(sid->pan_obj, psd);
+
+   psd->api->gravity_set(sid->pan_obj, x, y);
+}
+
+static void
+_elm_scroll_gravity_get(const Evas_Object *obj,
+                        double *x,
+                        double *y)
+{
+   ELM_SCROLL_IFACE_DATA_GET_OR_RETURN(obj, sid);
+
+   if (!sid->pan_obj) return;
+
+   ELM_PAN_DATA_GET(sid->pan_obj, psd);
+
+   psd->api->gravity_get(sid->pan_obj, x, y);
+}
+
+static Eina_Bool
+_elm_scroll_interface_add(Evas_Object *obj)
+{
+   ELM_SCROLL_IFACE_DATA_GET(obj, sid);
+
+   memset(sid, 0, sizeof(*sid));
+
+   sid->obj = obj;
+
+   sid->x = 0;
+   sid->y = 0;
+   sid->w = 0;
+   sid->h = 0;
+   sid->step.x = 32;
+   sid->step.y = 32;
+   sid->page.x = -50;
+   sid->page.y = -50;
+   sid->hbar_flags = ELM_SCROLLER_POLICY_AUTO;
+   sid->vbar_flags = ELM_SCROLLER_POLICY_AUTO;
+   sid->hbar_visible = EINA_TRUE;
+   sid->vbar_visible = EINA_TRUE;
+
+   sid->bounce_horiz = EINA_TRUE;
+   sid->bounce_vert = EINA_TRUE;
+
+   sid->one_direction_at_a_time = EINA_TRUE;
+   sid->momentum_animator_disabled = EINA_FALSE;
+   sid->bounce_animator_disabled = EINA_FALSE;
+
+   _elm_scroll_scroll_bar_reset(sid);
+
+   return EINA_TRUE;
+}
+
+static void
+_elm_scroll_interface_del(Evas_Object *obj)
+{
+   ELM_SCROLL_IFACE_DATA_GET(obj, sid);
+
+   _elm_scroll_content_set(obj, NULL);
+   if (!sid->extern_pan) evas_object_del(sid->pan_obj);
+
+   if (sid->down.hold_animator)
+     ecore_animator_del(sid->down.hold_animator);
+   if (sid->down.onhold_animator)
+     ecore_animator_del(sid->down.onhold_animator);
+   if (sid->down.momentum_animator)
+     ecore_animator_del(sid->down.momentum_animator);
+   if (sid->down.bounce_x_animator)
+     ecore_animator_del(sid->down.bounce_x_animator);
+   if (sid->down.bounce_y_animator)
+     ecore_animator_del(sid->down.bounce_y_animator);
+   if (sid->scrollto.x.animator) ecore_animator_del(sid->scrollto.x.animator);
+   if (sid->scrollto.y.animator) ecore_animator_del(sid->scrollto.y.animator);
+}
+
+EAPI const char ELM_SCROLLABLE_IFACE_NAME[] = "elm_interface_scrollable";
+
+EAPI const Elm_Scrollable_Smart_Interface ELM_SCROLLABLE_IFACE =
+{
+   {
+      ELM_SCROLLABLE_IFACE_NAME,
+      sizeof(Elm_Scrollable_Smart_Interface_Data),
+      _elm_scroll_interface_add,
+      _elm_scroll_interface_del
+   },
+
+   _elm_scroll_objects_set,
+   _elm_scroll_content_set,
+   _elm_scroll_extern_pan_set,
+   _elm_scroll_drag_start_cb_set,
+   _elm_scroll_drag_stop_cb_set,
+   _elm_scroll_animate_start_cb_set,
+   _elm_scroll_animate_stop_cb_set,
+   _elm_scroll_scroll_cb_set,
+   _elm_scroll_edge_left_cb_set,
+   _elm_scroll_edge_right_cb_set,
+   _elm_scroll_edge_top_cb_set,
+   _elm_scroll_edge_bottom_cb_set,
+   _elm_content_min_limit_cb_set,
+   _elm_scroll_content_pos_set,
+   _elm_scroll_content_pos_get,
+   _elm_scroll_content_region_show,
+   _elm_scroll_content_region_set,
+   _elm_scroll_content_size_get,
+   _elm_scroll_content_viewport_size_get,
+   _elm_scroll_content_min_limit,
+   _elm_scroll_step_size_set,
+   _elm_scroll_step_size_get,
+   _elm_scroll_page_size_set,
+   _elm_scroll_page_size_get,
+   _elm_scroll_policy_set,
+   _elm_scroll_policy_get,
+   _elm_scroll_single_direction_set,
+   _elm_scroll_single_direction_get,
+   _elm_scroll_mirrored_set,
+   _elm_scroll_hold_set,
+   _elm_scroll_freeze_set,
+   _elm_scroll_bounce_allow_set,
+   _elm_scroll_bounce_allow_get,
+   _elm_scroll_paging_set,
+   _elm_scroll_paging_get,
+   _elm_scroll_current_page_get,
+   _elm_scroll_last_page_get,
+   _elm_scroll_page_show,
+   _elm_scroll_page_bring_in,
+   _elm_scroll_region_bring_in,
+   _elm_scroll_gravity_set,
+   _elm_scroll_gravity_get,
+   _elm_scroll_momentum_animator_disabled_get,
+   _elm_scroll_momentum_animator_disabled_set,
+   _elm_scroll_bounce_animator_disabled_set,
+   _elm_scroll_bounce_animator_disabled_get,
+   _elm_scroll_wheel_disabled_get,
+   _elm_scroll_wheel_disabled_set
+};
diff --git a/src/lib/elm_interface_scrollable.h b/src/lib/elm_interface_scrollable.h
new file mode 100644 (file)
index 0000000..b945a6f
--- /dev/null
@@ -0,0 +1,488 @@
+#ifndef ELM_INTEFARCE_SCROLLER_H
+#define ELM_INTEFARCE_SCROLLER_H
+
+/**
+ * @addtogroup Widget
+ * @{
+ *
+ * @section elm-scrollable-interface The Elementary Scrollable Interface
+ *
+ * This is a common interface for widgets having @b scrollable views.
+ * Widgets using/implementing this must use the
+ * @c EVAS_SMART_SUBCLASS_IFACE_NEW macro (instead of the
+ * @c EVAS_SMART_SUBCLASS_NEW one) when declaring its smart class,
+ * so an interface is also declared.
+ *
+ * The scrollable interface comes built with Elementary and is exposed
+ * as #ELM_SCROLLABLE_IFACE.
+ *
+ * The interface API is explained in details at
+ * #Elm_Scrollable_Smart_Interface.
+ *
+ * An Elementary scrollable interface will handle an internal @b
+ * panning object. It has the function of clipping and moving the
+ * actual scrollable content around, by the command of the scrollable
+ * interface calls. Though it's not the common case, one might
+ * want/have to change some aspects of the internal panning object
+ * behavior.  For that, we have it also exposed here --
+ * #Elm_Pan_Smart_Class. Use elm_pan_smart_class_get() to build your
+ * custom panning object, when creating a scrollable widget (again,
+ * only if you need a custom panning object) and set it with
+ * Elm_Scrollable_Smart_Interface::extern_pan_set.
+ */
+
+/**
+ * @def ELM_PAN_CLASS
+ *
+ * Use this macro to cast whichever subclass of
+ * #Elm_Pan_Smart_Class into it, so to access its fields.
+ *
+ * @ingroup Widget
+ */
+ #define ELM_PAN_CLASS(x) ((Elm_Pan_Smart_Class *)x)
+
+/**
+ * @def ELM_PAN_SMART_CLASS_VERSION
+ *
+ * Current version for Elementary pan @b base smart class, a value
+ * which goes to _Elm_Pan_Smart_Class::version.
+ *
+ * @ingroup Widget
+ */
+#define ELM_PAN_SMART_CLASS_VERSION 1
+
+/**
+ * @def ELM_PAN_SMART_CLASS_INIT
+ *
+ * Initializer for a whole #Elm_Pan_Smart_Class structure, with
+ * @c NULL values on its specific fields.
+ *
+ * @param smart_class_init initializer to use for the "base" field
+ * (#Evas_Smart_Class).
+ *
+ * @see EVAS_SMART_CLASS_INIT_NULL
+ * @see EVAS_SMART_CLASS_INIT_NAME_VERSION
+ * @see ELM_PAN_SMART_CLASS_INIT_NULL
+ * @see ELM_PAN_SMART_CLASS_INIT_NAME_VERSION
+ *
+ * @ingroup Widget
+ */
+#define ELM_PAN_SMART_CLASS_INIT(smart_class_init)                        \
+  {smart_class_init, ELM_PAN_SMART_CLASS_VERSION, NULL, NULL, NULL, NULL, \
+   NULL, NULL, NULL}
+
+/**
+ * @def ELM_PAN_SMART_CLASS_INIT_NULL
+ *
+ * Initializer to zero out a whole #Elm_Pan_Smart_Class structure.
+ *
+ * @see ELM_PAN_SMART_CLASS_INIT_NAME_VERSION
+ * @see ELM_PAN_SMART_CLASS_INIT
+ *
+ * @ingroup Widget
+ */
+#define ELM_PAN_SMART_CLASS_INIT_NULL \
+  ELM_PAN_SMART_CLASS_INIT(EVAS_SMART_CLASS_INIT_NULL)
+
+/**
+ * @def ELM_PAN_SMART_CLASS_INIT_NAME_VERSION
+ *
+ * Initializer to zero out a whole #Elm_Pan_Smart_Class structure and
+ * set its name and version.
+ *
+ * This is similar to #ELM_PAN_SMART_CLASS_INIT_NULL, but it will
+ * also set the version field of #Elm_Pan_Smart_Class (base field)
+ * to the latest #ELM_PAN_SMART_CLASS_VERSION and name it to the
+ * specific value.
+ *
+ * It will keep a reference to the name field as a <c>"const char *"</c>,
+ * i.e., the name must be available while the structure is
+ * used (hint: static or global variable!) and must not be modified.
+ *
+ * @see ELM_PAN_SMART_CLASS_INIT_NULL
+ * @see ELM_PAN_SMART_CLASS_INIT
+ *
+ * @ingroup Widget
+ */
+#define ELM_PAN_SMART_CLASS_INIT_NAME_VERSION(name) \
+  ELM_PAN_SMART_CLASS_INIT(EVAS_SMART_CLASS_INIT_NAME_VERSION(name))
+
+/**
+ * Elementary scroller panning base smart class. This inherits
+ * directly from the Evas smart clipped class (an object clipping
+ * children to its viewport/size). It is exposed here only to build
+ * widgets needing a custom panning behavior.
+ */
+typedef struct _Elm_Pan_Smart_Class Elm_Pan_Smart_Class;
+struct _Elm_Pan_Smart_Class
+{
+   Evas_Smart_Class base; /* it's a clipped smart object */
+
+   int              version; /**< Version of this smart class definition */
+
+   void             (*pos_set)(Evas_Object *obj,
+                               Evas_Coord x,
+                               Evas_Coord y);
+   void             (*pos_get)(const Evas_Object *obj,
+                               Evas_Coord *x,
+                               Evas_Coord *y);
+   void             (*pos_max_get)(const Evas_Object *obj,
+                                   Evas_Coord *x,
+                                   Evas_Coord *y);
+   void             (*pos_min_get)(const Evas_Object *obj,
+                                   Evas_Coord *x,
+                                   Evas_Coord *y);
+   void             (*content_size_get)(const Evas_Object *obj,
+                                        Evas_Coord *x,
+                                        Evas_Coord *y);
+   void             (*gravity_set)(Evas_Object *obj,
+                                   double x,
+                                   double y);
+   void             (*gravity_get)(const Evas_Object *obj,
+                                   double *x,
+                                   double *y);
+};
+
+/**
+ * Elementary scroller panning base smart data.
+ */
+typedef struct _Elm_Pan_Smart_Data Elm_Pan_Smart_Data;
+struct _Elm_Pan_Smart_Data
+{
+   Evas_Object_Smart_Clipped_Data base;
+
+   const Elm_Pan_Smart_Class     *api; /**< This is the pointer to the object's class, from where we can reach/call its class functions */
+
+   Evas_Object                   *self;
+   Evas_Object                   *content;
+   Evas_Coord                     x, y, w, h;
+   Evas_Coord                     content_w, content_h, px, py;
+   double                         gravity_x, gravity_y;
+   Evas_Coord                     prev_cw, prev_ch, delta_posx, delta_posy;
+};
+
+/**
+ * Elementary scrollable interface base data.
+ */
+typedef struct _Elm_Scrollable_Smart_Interface_Data
+  Elm_Scrollable_Smart_Interface_Data;
+struct _Elm_Scrollable_Smart_Interface_Data
+{
+   Evas_Coord          x, y, w, h;
+   Evas_Coord          wx, wy, ww, wh; /**< Last "wanted" geometry */
+
+   Evas_Object        *obj;
+   Evas_Object        *content;
+   Evas_Object        *pan_obj;
+   Evas_Object        *edje_obj;
+   Evas_Object        *event_rect;
+
+   Evas_Object        *parent_widget;
+
+   Elm_Scroller_Policy hbar_flags, vbar_flags;
+
+   struct
+   {
+      Evas_Coord x, y;
+      Evas_Coord sx, sy;
+      Evas_Coord dx, dy;
+      Evas_Coord pdx, pdy;
+      Evas_Coord bx, by;
+      Evas_Coord ax, ay;
+      Evas_Coord bx0, by0;
+      Evas_Coord b0x, b0y;
+      Evas_Coord b2x, b2y;
+
+      struct
+      {
+         Evas_Coord x, y;
+         double     timestamp, localtimestamp;
+      } history[60];
+
+      struct
+      {
+         double tadd, dxsum, dysum;
+         double est_timestamp_diff;
+      } hist;
+
+      double          anim_start;
+      double          anim_start2;
+      double          anim_start3;
+      double          onhold_vx, onhold_vy, onhold_tlast,
+                      onhold_vxe, onhold_vye;
+      double          extra_time;
+
+      Evas_Coord      hold_x, hold_y;
+      Evas_Coord      locked_x, locked_y;
+      int             hdir, vdir;
+
+      Ecore_Animator *hold_animator;
+      Ecore_Animator *onhold_animator;
+      Ecore_Animator *momentum_animator;
+      Ecore_Animator *bounce_x_animator;
+      Ecore_Animator *bounce_y_animator;
+
+      Eina_Bool       bounce_x_hold : 1;
+      Eina_Bool       bounce_y_hold : 1;
+      Eina_Bool       dragged_began : 1;
+      Eina_Bool       want_dragged : 1;
+      Eina_Bool       hold_parent : 1;
+      Eina_Bool       want_reset : 1;
+      Eina_Bool       cancelled : 1;
+      Eina_Bool       dragged : 1;
+      Eina_Bool       locked : 1;
+      Eina_Bool       scroll : 1;
+      Eina_Bool       dir_x : 1;
+      Eina_Bool       dir_y : 1;
+      Eina_Bool       hold : 1;
+      Eina_Bool       now : 1;
+   } down;
+
+   struct
+   {
+      Evas_Coord w, h;
+      Eina_Bool  resized : 1;
+   } content_info;
+
+   struct
+   {
+      Evas_Coord x, y;
+   } step, page;
+
+   struct
+   {
+      void (*drag_start)(Evas_Object *obj,
+                         void *data);
+      void (*drag_stop)(Evas_Object *obj,
+                        void *data);
+      void (*animate_start)(Evas_Object *obj,
+                            void *data);
+      void (*animate_stop)(Evas_Object *obj,
+                           void *data);
+      void (*scroll)(Evas_Object *obj,
+                     void *data);
+      void (*edge_left)(Evas_Object *obj,
+                        void *data);
+      void (*edge_right)(Evas_Object *obj,
+                         void *data);
+      void (*edge_top)(Evas_Object *obj,
+                       void *data);
+      void (*edge_bottom)(Evas_Object *obj,
+                          void *data);
+      void (*content_min_limit)(Evas_Object *obj,
+                                Eina_Bool w,
+                                Eina_Bool h);
+   } cb_func;
+
+   struct
+   {
+      struct
+      {
+         Evas_Coord      start, end;
+         double          t_start, t_end;
+         Ecore_Animator *animator;
+      } x, y;
+   } scrollto;
+
+   double     pagerel_h, pagerel_v;
+   Evas_Coord pagesize_h, pagesize_v;
+
+   Eina_Bool  momentum_animator_disabled : 1;
+   Eina_Bool  bounce_animator_disabled : 1;
+   Eina_Bool  one_direction_at_a_time : 1;
+   Eina_Bool  wheel_disabled : 1;
+   Eina_Bool  hbar_visible : 1;
+   Eina_Bool  vbar_visible : 1;
+   Eina_Bool  bounce_horiz : 1;
+   Eina_Bool  bounce_vert : 1;
+   Eina_Bool  is_mirrored : 1;
+   Eina_Bool  extern_pan : 1;
+   Eina_Bool  bouncemey : 1;
+   Eina_Bool  bouncemex : 1;
+   Eina_Bool  freeze : 1;
+   Eina_Bool  hold : 1;
+};
+
+typedef struct _Elm_Scrollable_Smart_Interface Elm_Scrollable_Smart_Interface;
+struct _Elm_Scrollable_Smart_Interface
+{
+   Evas_Smart_Interface base;
+
+   void       (*objects_set)(Evas_Object *obj,
+                             Evas_Object *edje_obj,
+                             Evas_Object *hit_rectangle);
+   void       (*content_set)(Evas_Object *obj,
+                             Evas_Object *content);
+
+   void       (*extern_pan_set)(Evas_Object *obj,
+                                Evas_Object *pan);
+
+   void       (*drag_start_cb_set)(Evas_Object *obj,
+                                   void (*d_start_cb)(Evas_Object *obj,
+                                                         void *data));
+   void       (*drag_stop_cb_set)(Evas_Object *obj,
+                                  void (*d_stop_cb)(Evas_Object *obj,
+                                                       void *data));
+   void       (*animate_start_cb_set)(Evas_Object *obj,
+                                      void (*a_start_cb)(Evas_Object *obj,
+                                                               void *data));
+   void       (*animate_stop_cb_set)(Evas_Object *obj,
+                                     void (*a_stop_cb)(Evas_Object *obj,
+                                                             void *data));
+   void       (*scroll_cb_set)(Evas_Object *obj,
+                               void (*s_cb)(Evas_Object *obj,
+                                                 void *data));
+   void       (*edge_left_cb_set)(Evas_Object *obj,
+                                  void (*e_left_cb)(Evas_Object *obj,
+                                                       void *data));
+   void       (*edge_right_cb_set)(Evas_Object *obj,
+                                   void (*e_right_cb)(Evas_Object *obj,
+                                                         void *data));
+   void       (*edge_top_cb_set)(Evas_Object *obj,
+                                 void (*e_top_cb)(Evas_Object *obj,
+                                                     void *data));
+   void       (*edge_bottom_cb_set)(Evas_Object *obj,
+                                    void (*e_bottom_cb)(Evas_Object *obj,
+                                                           void *data));
+
+   void       (*content_min_limit_cb_set)(Evas_Object *obj,
+                                          void (*c_limit_cb)(Evas_Object *obj,
+                                                             Eina_Bool w,
+                                                             Eina_Bool h));
+
+   /* set the position of content object inside the scrolling region,
+    * immediately */
+   void       (*content_pos_set)(Evas_Object *obj,
+                                 Evas_Coord x,
+                                 Evas_Coord y);
+   void       (*content_pos_get)(const Evas_Object *obj,
+                                 Evas_Coord *x,
+                                 Evas_Coord *y);
+
+   void       (*content_region_show)(Evas_Object *obj,
+                                     Evas_Coord x,
+                                     Evas_Coord y,
+                                     Evas_Coord w,
+                                     Evas_Coord h);
+   void       (*content_region_set)(Evas_Object *obj,
+                                    Evas_Coord x,
+                                    Evas_Coord y,
+                                    Evas_Coord w,
+                                    Evas_Coord h);
+
+   void       (*content_size_get)(const Evas_Object *obj,
+                                  Evas_Coord *w,
+                                  Evas_Coord *h);
+
+   /* get the size of the actual viewport area (swallowed into
+    * scroller Edje object) */
+   void       (*content_viewport_size_get)(const Evas_Object *obj,
+                                           Evas_Coord *w,
+                                           Evas_Coord *h);
+
+   /* this one issues the respective callback, only */
+   void       (*content_min_limit)(Evas_Object *obj,
+                                   Eina_Bool w,
+                                   Eina_Bool h);
+
+   void       (*step_size_set)(Evas_Object *obj,
+                               Evas_Coord x,
+                               Evas_Coord y);
+   void       (*step_size_get)(const Evas_Object *obj,
+                               Evas_Coord *x,
+                               Evas_Coord *y);
+   void       (*page_size_set)(Evas_Object *obj,
+                               Evas_Coord x,
+                               Evas_Coord y);
+   void       (*page_size_get)(const Evas_Object *obj,
+                               Evas_Coord *x,
+                               Evas_Coord *y);
+   void       (*policy_set)(Evas_Object *obj,
+                            Elm_Scroller_Policy hbar,
+                            Elm_Scroller_Policy vbar);
+   void       (*policy_get)(const Evas_Object *obj,
+                            Elm_Scroller_Policy *hbar,
+                            Elm_Scroller_Policy *vbar);
+
+   void       (*single_direction_set)(Evas_Object *obj,
+                                      Eina_Bool single_dir);
+   Eina_Bool  (*single_direction_get)(const Evas_Object *obj);
+
+   void       (*mirrored_set)(Evas_Object *obj,
+                              Eina_Bool mirrored);
+
+   void       (*hold_set)(Evas_Object *obj,
+                          Eina_Bool hold);
+   void       (*freeze_set)(Evas_Object *obj,
+                            Eina_Bool freeze);
+
+   void       (*bounce_allow_set)(Evas_Object *obj,
+                                  Eina_Bool horiz,
+                                  Eina_Bool vert);
+   void       (*bounce_allow_get)(const Evas_Object *obj,
+                                  Eina_Bool *horiz,
+                                  Eina_Bool *vert);
+
+   void       (*paging_set)(Evas_Object *obj,
+                            double pagerel_h,
+                            double pagerel_v,
+                            Evas_Coord pagesize_h,
+                            Evas_Coord pagesize_v);
+   void       (*paging_get)(const Evas_Object *obj,
+                            double *pagerel_h,
+                            double *pagerel_v,
+                            Evas_Coord *pagesize_h,
+                            Evas_Coord *pagesize_v);
+   void       (*current_page_get)(const Evas_Object *obj,
+                                  int *pagenumber_h,
+                                  int *pagenumber_v);
+   void       (*last_page_get)(const Evas_Object *obj,
+                               int *pagenumber_h,
+                               int *pagenumber_v);
+   void       (*page_show)(Evas_Object *obj,
+                           int pagenumber_h,
+                           int pagenumber_v);
+   void       (*page_bring_in)(Evas_Object *obj,
+                               int pagenumber_h,
+                               int pagenumber_v);
+
+   void       (*region_bring_in)(Evas_Object *obj,
+                                 Evas_Coord x,
+                                 Evas_Coord y,
+                                 Evas_Coord w,
+                                 Evas_Coord h);
+
+   void       (*gravity_set)(Evas_Object *obj,
+                             double x,
+                             double y);
+   void       (*gravity_get)(const Evas_Object *obj,
+                             double *x,
+                             double *y);
+
+   Eina_Bool  (*momentum_animator_disabled_get)(const Evas_Object *obj);
+   void       (*momentum_animator_disabled_set)(Evas_Object *obj,
+                                                Eina_Bool disabled);
+
+   void       (*bounce_animator_disabled_set)(Evas_Object *obj,
+                                              Eina_Bool disabled);
+   Eina_Bool  (*bounce_animator_disabled_get)(const Evas_Object *obj);
+
+   Eina_Bool  (*wheel_disabled_get)(const Evas_Object *obj);
+   void       (*wheel_disabled_set)(Evas_Object *obj,
+                                    Eina_Bool disabled);
+};
+
+EAPI extern const char ELM_SCROLLABLE_IFACE_NAME[];
+EAPI extern const Elm_Scrollable_Smart_Interface ELM_SCROLLABLE_IFACE;
+
+EAPI const Elm_Pan_Smart_Class *elm_pan_smart_class_get(void);
+
+#define ELM_SCROLLABLE_IFACE_GET(obj, iface)    \
+  const Elm_Scrollable_Smart_Interface * iface; \
+  iface = evas_object_smart_interface_get(obj, ELM_SCROLLABLE_IFACE_NAME);
+
+/**
+ * @}
+ */
+
+#endif
index ad17961..d84748c 100644 (file)
@@ -1,6 +1,7 @@
 #include <Elementary.h>
 #include "elm_priv.h"
 #include "elm_widget_container.h"
+#include "elm_interface_scrollable.h"
 
 static const char SMART_NAME[] = "elm_widget";
 static const char SMART_NAME_COMPAT[] = "elm_widget_compat";
@@ -221,6 +222,12 @@ _elm_legacy_is(const Evas_Object *obj)
    return evas_object_smart_type_check_ptr(obj, SMART_NAME_COMPAT);
 }
 
+static inline Eina_Bool
+_elm_scrollable_is(const Evas_Object *obj)
+{
+   return !!evas_object_smart_interface_get(obj, ELM_SCROLLABLE_IFACE_NAME);
+}
+
 /* what follows are both basic (unimplemented) smart class functions
  * and proxies from those to smart data (instance) widget
  * functions. one by one we'll be surpassing the proxies on the
@@ -3038,7 +3045,16 @@ elm_widget_scroll_hold_push(Evas_Object *obj)
    API_ENTRY return;
    sd->scroll_hold++;
    if (sd->scroll_hold == 1)
-     evas_object_smart_callback_call(obj, "scroll-hold-on", obj);
+     {
+        if (_elm_scrollable_is(obj))
+          {
+             ELM_SCROLLABLE_IFACE_GET(obj, s_iface);
+             s_iface->hold_set(obj, EINA_TRUE);
+          }
+        else /* FIXME: this will vanish as soon as we don't have
+              * any legacy widget anymore */
+          evas_object_smart_callback_call(obj, "scroll-hold-on", obj);
+     }
    if (sd->parent_obj) elm_widget_scroll_hold_push(sd->parent_obj);
    // FIXME: on delete/reparent hold pop
 }
@@ -3049,7 +3065,16 @@ elm_widget_scroll_hold_pop(Evas_Object *obj)
    API_ENTRY return;
    sd->scroll_hold--;
    if (!sd->scroll_hold)
-     evas_object_smart_callback_call(obj, "scroll-hold-off", obj);
+     {
+        if (_elm_scrollable_is(obj))
+          {
+             ELM_SCROLLABLE_IFACE_GET(obj, s_iface);
+             s_iface->hold_set(obj, EINA_FALSE);
+          }
+        else /* FIXME: this will vanish as soon as we don't have
+              * any legacy widget anymore */
+          evas_object_smart_callback_call(obj, "scroll-hold-off", obj);
+     }
    if (sd->parent_obj) elm_widget_scroll_hold_pop(sd->parent_obj);
    if (sd->scroll_hold < 0) sd->scroll_hold = 0;
 }
@@ -3067,7 +3092,16 @@ elm_widget_scroll_freeze_push(Evas_Object *obj)
    API_ENTRY return;
    sd->scroll_freeze++;
    if (sd->scroll_freeze == 1)
-     evas_object_smart_callback_call(obj, "scroll-freeze-on", obj);
+     {
+        if (_elm_scrollable_is(obj))
+          {
+             ELM_SCROLLABLE_IFACE_GET(obj, s_iface);
+             s_iface->freeze_set(obj, EINA_TRUE);
+          }
+        else /* FIXME: this will vanish as soon as we don't have
+              * any legacy widget anymore */
+          evas_object_smart_callback_call(obj, "scroll-freeze-on", obj);
+     }
    if (sd->parent_obj) elm_widget_scroll_freeze_push(sd->parent_obj);
    // FIXME: on delete/reparent freeze pop
 }
@@ -3078,7 +3112,16 @@ elm_widget_scroll_freeze_pop(Evas_Object *obj)
    API_ENTRY return;
    sd->scroll_freeze--;
    if (!sd->scroll_freeze)
-     evas_object_smart_callback_call(obj, "scroll-freeze-off", obj);
+     {
+        if (_elm_scrollable_is(obj))
+          {
+             ELM_SCROLLABLE_IFACE_GET(obj, s_iface);
+             s_iface->freeze_set(obj, EINA_FALSE);
+          }
+        else /* FIXME: this will vanish as soon as we don't have
+              * any legacy widget anymore */
+          evas_object_smart_callback_call(obj, "scroll-freeze-off", obj);
+     }
    if (sd->parent_obj) elm_widget_scroll_freeze_pop(sd->parent_obj);
    if (sd->scroll_freeze < 0) sd->scroll_freeze = 0;
 }
index c384407..c4294be 100644 (file)
@@ -1064,6 +1064,10 @@ EAPI void             elm_widget_tree_dot_dump(const Evas_Object *top, FILE *out
 #define ELM_OBJ_ITEM_CHECK_OR_GOTO(it, label)      \
   ELM_WIDGET_ITEM_WIDTYPE_CHECK_OR_GOTO(it, label);
 
+/* to be used by INTERNAL classes on Elementary, so that the widgets
+ * parsing script skips it */
+#define ELM_INTERNAL_SMART_SUBCLASS_NEW EVAS_SMART_SUBCLASS_NEW
+
 /**
  * The drag and drop API.
  * Currently experimental, and will change when it does dynamic type