[els_touch] commit initial els_touch file which will be used by elm_webview
authorEunmi Lee <eunmi15.lee@samsung.com>
Thu, 22 Jul 2010 10:45:14 +0000 (19:45 +0900)
committerEunmi Lee <eunmi15.lee@samsung.com>
Thu, 22 Jul 2010 10:45:14 +0000 (19:45 +0900)
src/lib/Makefile.am
src/lib/els_touch.c [new file with mode: 0644]
src/lib/els_touch.h [new file with mode: 0644]

index 7bf8549..695165d 100644 (file)
@@ -140,7 +140,9 @@ els_hor_scroller.h \
 els_box.c \
 els_box.h \
 els_icon.c \
-els_icon.h
+els_icon.h \
+els_touch.c \
+els_touch.h
 
 
 libelementary_la_CFLAGS =
diff --git a/src/lib/els_touch.c b/src/lib/els_touch.c
new file mode 100644 (file)
index 0000000..5545201
--- /dev/null
@@ -0,0 +1,1571 @@
+/*
+ *
+ * vim:ts=8:sw=3:sts=8:noexpandtab:cino=>5n-3f0^-2{2
+ */
+#include <Elementary.h>
+#include <math.h>
+#include "elm_priv.h"
+
+#define SMART_NAME "elm_touch"
+#define API_ENTRY Smart_Data *sd; sd = evas_object_smart_data_get(obj); if ((!obj) || (!sd) || (evas_object_type_get(obj) && strcmp(evas_object_type_get(obj), SMART_NAME)))
+#define INTERNAL_ENTRY Smart_Data *sd; sd = evas_object_smart_data_get(obj); if (!sd) return;
+
+#define PRESS_TIME          250   // ms
+#define RELEASE_TIME         50   // ms
+#define LONG_HOLD_TIME      500   // ms
+
+#define N_FINGER             3
+#define DEFAULT_FRAMERATE   60
+#define DOUBLE_ERROR        0.00001
+
+// for one finger
+#define DBL_TAP_DISTANCE    30    // pixel
+#define DRAG_THRESHOLD       3    // pixel
+#define INIT_DRAG_THRESHOLD 15    // pixel
+#define MOVE_HISTORY_SIZE    5
+#define MAX_MOVE_DISTANCE   15
+#define FLICK_THRESHOLD      5
+
+// for two finger
+#define MOVE_THRESHOLD      15
+#define FINGER_DISTANCE     10
+
+typedef struct _Mouse_Data Mouse_Data;
+
+struct _Mouse_Data
+{
+   Evas_Coord x;
+   Evas_Coord y;
+   int time;
+   int device;
+};
+
+typedef enum _Two_Drag_Mode
+{
+   TWO_DRAG_NONE,
+   TWO_DRAG_PINCH,
+   TWO_DRAG_VERTICAL,
+   TWO_DRAG_HORIZONTAL,
+} Two_Drag_Mode;
+
+typedef struct _Two_Mouse_Data Two_Mouse_Data;
+
+struct _Two_Mouse_Data
+{
+   Evas_Point first;
+   Evas_Point second;
+   Two_Drag_Mode mode;
+};
+
+typedef struct _Three_Mouse_Data Three_Mouse_Data;
+
+struct _Three_Mouse_Data
+{
+   Evas_Point first;
+   Evas_Point second;
+   Evas_Point third;
+};
+
+typedef enum _Touch_State
+{
+   TOUCH_STATE_NONE,
+   TOUCH_STATE_DOWN,
+   TOUCH_STATE_DOWN_UP,
+   TOUCH_STATE_DOWN_UP_DOWN,
+   TOUCH_STATE_HOLD,
+   TOUCH_STATE_DRAG,
+   TOUCH_STATE_TWO_DOWN,
+   TOUCH_STATE_TWO_DRAG,
+   TOUCH_STATE_THREE_DOWN
+} Touch_State;
+
+typedef enum _One_Drag_Mode
+{
+   ONE_DRAG_NONE,
+   ONE_DRAG_VERTICAL,
+   ONE_DRAG_HORIZONTAL
+} One_Drag_Mode;
+
+typedef struct _Flick_Data Flick_Data;
+
+struct _Flick_Data
+{
+   int flick_index;
+   Evas_Coord_Point last;
+   Evas_Coord_Point avg_distance;
+};
+
+typedef struct _Mouse_Diff_Data Mouse_Diff_Data;
+
+struct _Mouse_Diff_Data
+{
+   Evas_Coord dx, dy;
+   double time;
+};
+
+typedef struct _Smart_Data Smart_Data;
+
+struct _Smart_Data
+{
+   Evas_Object *smart_obj;
+   Evas_Object *child_obj;
+
+   Eina_Bool running;
+
+   int screen_angle;
+   Touch_State state;
+   One_Drag_Mode one_drag_mode;
+   Eina_Bool is_one_drag_mode;
+   Two_Drag_Mode two_drag_mode;
+   int numOfTouch;
+
+   // for flick
+   int last_move_history_index;
+   int move_history_count;
+   Flick_Data flick_data;
+   Mouse_Diff_Data move_history[MOVE_HISTORY_SIZE];
+
+   Mouse_Data first_down[N_FINGER];
+   Mouse_Data last_down[N_FINGER];
+   Mouse_Data last_drag[N_FINGER];
+
+   Ecore_Animator *animator_move;
+   Ecore_Animator *animator_flick;
+   Ecore_Animator *animator_two_move;
+
+   // timers
+   Ecore_Timer *press_timer;
+   Ecore_Timer *long_press_timer;
+   Ecore_Timer *release_timer;
+   Ecore_Timer *press_release_timer;
+};
+
+/* local subsystem functions */
+// mouse callbacks
+static float _smart_velocity_easeinoutcubic(int index);
+static void _smart_mouse_down(void *data, Evas *e, Evas_Object *obj, void *ev);
+static void _smart_mouse_up(void *data, Evas *e, Evas_Object *obj, void *ev);
+static void _smart_mouse_move(void *data, Evas *e, Evas_Object *obj, void *ev);
+static void _smart_multi_down(void *data, Evas *e, Evas_Object *obj, void *ev);
+static void _smart_multi_up(void *data, Evas *e, Evas_Object *obj, void *ev);
+static void _smart_multi_move(void *data, Evas *e, Evas_Object *obj, void *ev);
+// animator callbacks
+static int _smart_animation_move(void *data);
+static int _smart_animation_flick(void *data);
+static int _smart_animation_two_move(void *data);
+// enter mode functions
+static void _smart_enter_none(Smart_Data *sd);
+static void _smart_enter_down(Smart_Data *sd);
+static void _smart_enter_down_up(Smart_Data *sd, int downTime, int time);
+static void _smart_enter_down_up_down(Smart_Data *sd);
+static void _smart_enter_hold(Smart_Data *sd);
+static void _smart_enter_drag(Smart_Data *sd);
+static void _smart_enter_two_down(Smart_Data *sd);
+static void _smart_enter_two_drag(Smart_Data *sd);
+static void _smart_enter_three_down(Smart_Data *sd);
+// emit functions
+static void _smart_emit_press(Smart_Data *sd);
+static void _smart_emit_tap(Smart_Data *sd);
+static void _smart_emit_double_tap(Smart_Data *sd);
+static void _smart_emit_long_hold(Smart_Data *sd);
+static void _smart_emit_release(Smart_Data *sd);
+static void _smart_emit_two_press(Smart_Data *sd);
+static void _smart_emit_two_tap(Smart_Data *sd);
+static void _smart_emit_two_move_start(Smart_Data *sd);
+static void _smart_emit_two_move(Smart_Data *sd);
+static void _smart_emit_two_move_end(Smart_Data *sd);
+static void _smart_emit_three_press(Smart_Data *sd);
+static void _smart_emit_three_tap(Smart_Data *sd);
+// timer handlers
+static int _smart_press_timer_handler(void *data);
+static int _smart_long_press_timer_handler(void *data);
+static int _smart_release_timer_handler(void *data);
+static int _smart_press_release_timer_handler(void *data);
+
+static void _smart_save_move_history(Smart_Data *sd, int x, int y, int dx, int dy);
+static void _smart_start_flick(Smart_Data *sd);
+static void _smart_stop_animator_move(Smart_Data *sd);
+static void _smart_stop_animator_flick(Smart_Data *sd);
+static void _smart_stop_animator_two_move(Smart_Data *sd);
+static Two_Drag_Mode _smart_check_two_drag_mode(Smart_Data *sd);
+static void _smart_set_first_down(Smart_Data *sd, int index, Mouse_Data *data);
+static void _smart_set_last_down(Smart_Data *sd, int index, Mouse_Data *data);
+static void _smart_set_last_drag(Smart_Data *sd, int index, Mouse_Data *data);
+static void _smart_stop_all_timers(Smart_Data *sd);
+static void _smart_init(void);
+static void _smart_del(Evas_Object *obj);
+static void _smart_add(Evas_Object *obj);
+
+/* local subsystem globals */
+static Evas_Smart *_smart = NULL;
+
+/* externally accessible functions */
+   Evas_Object *
+_elm_smart_touch_add(Evas *evas)
+{
+   _smart_init();
+   return evas_object_smart_add(evas, _smart);
+}
+
+   void
+_elm_smart_touch_child_set(Evas_Object *obj, Evas_Object *child)
+{
+   API_ENTRY return;
+   if (child == sd->child_obj) return;
+
+   if (sd->child_obj) // delete callbacks of old object
+     {
+       evas_object_event_callback_del(sd->child_obj, EVAS_CALLBACK_MOUSE_DOWN, _smart_mouse_down);
+       evas_object_event_callback_del(sd->child_obj, EVAS_CALLBACK_MOUSE_UP, _smart_mouse_up);
+       evas_object_event_callback_del(sd->child_obj, EVAS_CALLBACK_MOUSE_MOVE, _smart_mouse_move);
+       evas_object_event_callback_del(sd->child_obj, EVAS_CALLBACK_MULTI_DOWN, _smart_multi_down);
+       evas_object_event_callback_del(sd->child_obj, EVAS_CALLBACK_MULTI_UP, _smart_multi_up);
+       evas_object_event_callback_del(sd->child_obj, EVAS_CALLBACK_MULTI_MOVE, _smart_multi_move);
+       _smart_stop_all_timers(sd);
+       _smart_stop_animator_move(sd);
+       _smart_stop_animator_flick(sd);
+       _smart_stop_animator_two_move(sd);
+
+       sd->child_obj = NULL;
+     }
+
+   if (child)
+     {
+       sd->child_obj = child;
+
+       // add callbacks
+       evas_object_event_callback_add(child, EVAS_CALLBACK_MOUSE_DOWN, _smart_mouse_down, sd);
+       evas_object_event_callback_add(child, EVAS_CALLBACK_MOUSE_UP, _smart_mouse_up, sd);
+       evas_object_event_callback_add(child, EVAS_CALLBACK_MOUSE_MOVE, _smart_mouse_move, sd);
+       evas_object_event_callback_add(child, EVAS_CALLBACK_MULTI_DOWN, _smart_multi_down, sd);
+       evas_object_event_callback_add(child, EVAS_CALLBACK_MULTI_UP, _smart_multi_up, sd);
+       evas_object_event_callback_add(child, EVAS_CALLBACK_MULTI_MOVE, _smart_multi_move, sd);
+
+       _smart_enter_none(sd);
+
+       sd->is_one_drag_mode = EINA_TRUE;
+     }
+
+   evas_object_smart_callback_call(sd->smart_obj, "changed", NULL);
+}
+
+   void
+_elm_smart_touch_start(Evas_Object *obj)
+{
+   API_ENTRY return;
+   if (sd->running) return;
+
+   sd->running = EINA_TRUE;
+   _smart_enter_none(sd);
+}
+
+   void
+_elm_smart_touch_stop(Evas_Object *obj)
+{
+   API_ENTRY return;
+   sd->running = EINA_FALSE;
+   _smart_stop_all_timers(sd);
+   _smart_stop_animator_move(sd);
+   _smart_stop_animator_flick(sd);
+   _smart_stop_animator_two_move(sd);
+   _smart_enter_none(sd);
+}
+
+   void
+_elm_smart_touch_reset(Evas_Object *obj)
+{
+   API_ENTRY return;
+   _smart_stop_all_timers(sd);
+   _smart_stop_animator_move(sd);
+   _smart_stop_animator_flick(sd);
+   _smart_stop_animator_two_move(sd);
+   _smart_enter_none(sd);
+}
+
+   void
+_elm_smart_touch_screen_angle_update(Evas_Object *obj, int screen_angle)
+{
+   API_ENTRY return;
+   sd->screen_angle = screen_angle;
+}
+
+   void
+_elm_smart_touch_is_one_drag_mode_enable(Evas_Object *obj, Eina_Bool is_one_drag_mode)
+{
+   API_ENTRY return;
+   sd->is_one_drag_mode = is_one_drag_mode;
+}
+
+/* local subsystem functions */
+/** reference from htsd://hosted.zeh.com.br/tweener/docs/en-us/misc/transitions.html
+ * Easing equation function for a cubic (t^3) easing in/out: acceleration until halfway, then deceleration
+ * @param                t                                        Number                Current time (in frames or seconds)
+ * @param                b                                        Number                Starting value
+ * @param                c                                        Number                Change needed in value
+ * @param                d                                        Number                Expected easing duration (in frames or seconds)
+ * @return                                                        Number                The correct value
+ public static function easeInOutCubic (t:Number, b:Number, c:Number, d:Number, p_params:Object):Number {
+ if ((t/=d/2) < 1) return c/2*t*t*t + b;
+ return c/2*((t-=2)*t*t + 2) + b;
+ }
+ */
+static float
+_smart_velocity_easeinoutcubic(int index)
+{
+   float d = 40.0f;
+   float t = d - index; // we want to get reversed value
+   float c = 1.0f;
+   if ((t /= (d / 2)) < 1)
+     {
+       return (c / 2) * t * t * t;
+     } else {
+         t -= 2;
+         return (c / 2) * (t * t * t + 2);
+     }
+}
+
+/* mouse callbacks */
+static void
+_smart_mouse_down(void *data, Evas *e, Evas_Object *obj, void *ev)
+{
+   Smart_Data *sd;
+   Evas_Event_Mouse_Down *event;
+   Mouse_Data mouse_data;
+
+   sd = data;
+   if (!sd || sd->running == EINA_FALSE) return;
+
+   event = (Evas_Event_Mouse_Down*)ev;
+
+   switch (sd->state)
+     {
+      case TOUCH_STATE_NONE:
+      case TOUCH_STATE_DRAG:
+        _smart_stop_animator_move(sd);
+        _smart_stop_animator_flick(sd);
+        mouse_data.x = event->canvas.x;
+        mouse_data.y = event->canvas.y;
+        mouse_data.time = event->timestamp;
+        mouse_data.device = -1;
+        _smart_set_first_down(sd, 0, &mouse_data);
+        _smart_set_last_down(sd, 0, &mouse_data);
+        _smart_set_last_drag(sd, 0, &mouse_data);
+        _smart_enter_down(sd);
+        break;
+
+      case TOUCH_STATE_DOWN_UP:
+        if (event->flags == EVAS_BUTTON_DOUBLE_CLICK) {
+             mouse_data.x = event->canvas.x;
+             mouse_data.y = event->canvas.y;
+             mouse_data.time = event->timestamp;
+             mouse_data.device = -1;
+             _smart_set_last_down(sd, 0, &mouse_data);
+             _smart_set_last_drag(sd, 0, &mouse_data);
+             _smart_enter_down_up_down(sd);
+        }
+        break;
+
+      default:
+        break;
+     }
+}
+
+static void
+_smart_mouse_up(void *data, Evas *e, Evas_Object *obj, void *ev)
+{
+   Smart_Data *sd;
+   sd = data;
+   Evas_Event_Mouse_Up *event;
+
+   if (!sd || sd->running == EINA_FALSE) return;
+
+   event = (Evas_Event_Mouse_Up*)ev;
+
+   switch (sd->state)
+     {
+      case TOUCH_STATE_DOWN:
+        _smart_enter_down_up(sd, (event->timestamp - sd->last_down[0].time), event->timestamp);
+        break;
+
+      case TOUCH_STATE_DOWN_UP_DOWN:
+          {
+             int dx = sd->last_down[0].x - sd->first_down[0].x;
+             int dy = sd->last_down[0].y - sd->first_down[0].y;
+             if ((dx * dx + dy * dy) <= (DBL_TAP_DISTANCE * DBL_TAP_DISTANCE))
+               _smart_emit_double_tap(sd);
+             _smart_stop_all_timers(sd);
+             _smart_enter_none(sd);
+          }        break;
+
+      case TOUCH_STATE_HOLD:
+        _smart_emit_release(sd);
+        _smart_stop_all_timers(sd);
+        _smart_enter_none(sd);
+        break;
+
+      case TOUCH_STATE_DRAG:
+        _smart_emit_release(sd);
+        _smart_start_flick(sd);
+        break;
+
+      case TOUCH_STATE_TWO_DOWN:
+        _smart_emit_two_tap(sd);
+        _smart_stop_all_timers(sd);
+        _smart_enter_none(sd);
+        break;
+
+      case TOUCH_STATE_TWO_DRAG:
+        _smart_stop_animator_two_move(sd);
+        _smart_stop_all_timers(sd);
+        _smart_enter_none(sd);
+        break;
+
+      case TOUCH_STATE_THREE_DOWN:
+        _smart_emit_three_tap(sd);
+        _smart_stop_all_timers(sd);
+        _smart_enter_none(sd);
+        break;
+
+      default:
+        _smart_emit_release(sd);
+        _smart_stop_all_timers(sd);
+        _smart_enter_none(sd);
+        DBG("\nERROR: wrong state in mouse_up\n\n");
+        break;
+     }
+
+}
+
+static void
+_smart_mouse_move(void *data, Evas *e, Evas_Object *obj, void *ev)
+{
+   Smart_Data *sd;
+   sd = data;
+   if (!sd || sd->running == EINA_FALSE) return;
+
+   Evas_Event_Mouse_Move *event = (Evas_Event_Mouse_Move*)ev;
+   int dx = 0;
+   int dy = 0;
+
+   Mouse_Data mouse_data;
+   mouse_data.x = event->cur.canvas.x;
+   mouse_data.y = event->cur.canvas.y;
+   mouse_data.time = event->timestamp;
+   mouse_data.device = -1;
+
+   switch (sd->state)
+     {
+      case TOUCH_STATE_DOWN:
+        dx = mouse_data.x - sd->last_drag[0].x;
+        dy = mouse_data.y - sd->last_drag[0].y;
+
+        if ((abs(dx) > INIT_DRAG_THRESHOLD) || (abs(dy) > INIT_DRAG_THRESHOLD))
+          {
+             _smart_set_last_drag(sd, 0, &mouse_data);
+             // Note:
+             // last_down - location where the drag starts
+             // (which is different than fisrtDown)
+             _smart_set_last_down(sd, 0, &mouse_data);
+             _smart_enter_drag(sd);
+          }
+        break;
+
+      case TOUCH_STATE_DRAG:
+        dx = mouse_data.x - sd->last_drag[0].x;
+        dy = mouse_data.y - sd->last_drag[0].y;
+
+        if ((abs(dx) > DRAG_THRESHOLD) || (abs(dy) > DRAG_THRESHOLD))
+          {
+             _smart_set_last_drag(sd, 0, &mouse_data);
+             _smart_save_move_history(sd, mouse_data.x, mouse_data.y, dx, dy);
+          }
+        break;
+
+      case TOUCH_STATE_TWO_DOWN:
+        _smart_set_last_drag(sd, 0, &mouse_data);
+
+        sd->two_drag_mode = _smart_check_two_drag_mode(sd);
+        if (sd->two_drag_mode != TWO_DRAG_NONE)
+          {
+             DBG("<< sd->two_drag_mode [%d] >>\n", sd->two_drag_mode);
+             _smart_enter_two_drag(sd);
+          }
+        break;
+
+      case TOUCH_STATE_TWO_DRAG:
+        _smart_set_last_drag(sd, 0, &mouse_data);
+        break;
+
+      case TOUCH_STATE_THREE_DOWN:
+        _smart_set_last_drag(sd, 0, &mouse_data);
+        break;
+
+      default:
+        break;
+     }
+}
+
+static void
+_smart_multi_down(void *data, Evas *e, Evas_Object *obj, void *ev)
+{
+   Smart_Data *sd;
+
+   sd = data;
+   if (!sd || sd->running == EINA_FALSE) return;
+
+   Evas_Event_Multi_Down *event = (Evas_Event_Multi_Down*)ev;
+   Mouse_Data mouse_data;
+
+   switch (sd->state)
+     {
+      case TOUCH_STATE_DOWN:
+      case TOUCH_STATE_DRAG:
+        sd->numOfTouch++;
+        if (sd->numOfTouch == 1)
+          {
+             mouse_data.x = event->output.x;
+             mouse_data.y = event->output.y;
+             mouse_data.time = event->timestamp;
+             mouse_data.device = event->device;
+             _smart_set_first_down(sd, 1, &mouse_data);
+             _smart_set_last_down(sd, 1, &mouse_data);
+             _smart_set_last_drag(sd, 1, &mouse_data);
+             _smart_stop_animator_move(sd);
+             _smart_stop_animator_flick(sd);
+             _smart_stop_animator_two_move(sd);
+             _smart_enter_two_down(sd);
+          }
+        break;
+
+      case TOUCH_STATE_TWO_DOWN:
+      case TOUCH_STATE_TWO_DRAG:
+        sd->numOfTouch++;
+        if (sd->numOfTouch == 2)
+          {
+             mouse_data.x = event->output.x;
+             mouse_data.y = event->output.y;
+             mouse_data.time = event->timestamp;
+             mouse_data.device = event->device;
+             _smart_set_first_down(sd, 2, &mouse_data);
+             _smart_set_last_down(sd, 2, &mouse_data);
+             _smart_set_last_drag(sd, 2, &mouse_data);
+             _smart_stop_animator_move(sd);
+             _smart_stop_animator_flick(sd);
+             _smart_stop_animator_two_move(sd);
+             _smart_enter_three_down(sd);
+          }
+        break;
+
+      default:
+        break;
+     }
+}
+
+static void
+_smart_multi_up(void *data, Evas *e, Evas_Object *obj, void *ev)
+{
+   Smart_Data *sd;
+   Evas_Event_Multi_Up *event;
+
+   sd = data;
+   if (!sd || sd->running == EINA_FALSE) return;
+
+   event = (Evas_Event_Multi_Up*)ev;
+
+   switch (sd->state)
+     {
+      case TOUCH_STATE_TWO_DOWN:
+        _smart_emit_two_tap(sd);
+        _smart_stop_all_timers(sd);
+        _smart_enter_none(sd);
+        break;
+
+      case TOUCH_STATE_TWO_DRAG:
+        _smart_stop_animator_two_move(sd);
+        _smart_stop_all_timers(sd);
+        _smart_enter_none(sd);
+        break;
+
+      case TOUCH_STATE_THREE_DOWN:
+        _smart_emit_three_tap(sd);
+        _smart_stop_all_timers(sd);
+        _smart_enter_none(sd);
+        break;
+
+      default:
+        _smart_stop_all_timers(sd);
+        _smart_enter_none(sd);
+        break;
+     }
+}
+
+static void
+_smart_multi_move(void *data, Evas *e, Evas_Object *obj, void *ev)
+{
+   Smart_Data *sd;
+   Evas_Event_Multi_Move *event;
+   Mouse_Data mouse_data;
+
+   sd = data;
+   if (!sd || sd->running == EINA_FALSE) return;
+
+   event = (Evas_Event_Multi_Move*)ev;
+   mouse_data.x = event->cur.output.x;
+   mouse_data.y = event->cur.output.y;
+   mouse_data.time = event->timestamp;
+   mouse_data.device = event->device;
+
+   switch (sd->state)
+     {
+      case TOUCH_STATE_TWO_DOWN:
+        if (sd->first_down[1].device == event->device)
+          {
+             _smart_set_last_drag(sd, 1, &mouse_data);
+             sd->two_drag_mode = _smart_check_two_drag_mode(sd);
+             if (sd->two_drag_mode != TWO_DRAG_NONE)
+               {
+                  DBG("<< sd->two_drag_mode [%d] >>\n", sd->two_drag_mode);
+                  _smart_enter_two_drag(sd);
+               }
+          }
+        break;
+
+      case TOUCH_STATE_TWO_DRAG:
+        if (sd->first_down[1].device == event->device)
+          {
+             _smart_set_last_drag(sd, 1, &mouse_data);
+          }
+        break;
+
+      case TOUCH_STATE_THREE_DOWN:
+        if (sd->first_down[1].device == event->device)
+          {
+             _smart_set_last_drag(sd, 1, &mouse_data);
+          }
+        else if (sd->first_down[2].device == event->device)
+          {
+             _smart_set_last_drag(sd, 2, &mouse_data);
+          }
+        break;
+
+      default:
+        break;
+     }
+}
+
+/* animators */
+static int
+_smart_animation_move(void *data)
+{
+   Smart_Data *sd;
+
+   sd = data;
+   if (sd->child_obj)
+     {
+       DBG("<< animation_move >>\n");
+       // get the position here instead of mouse_move event
+       Evas *evas = evas_object_evas_get(sd->child_obj);
+       Evas_Point point;
+       evas_pointer_canvas_xy_get(evas, &point.x, &point.y);
+       if (sd->is_one_drag_mode)
+         {
+            if (sd->one_drag_mode == ONE_DRAG_VERTICAL)
+              {
+                 // Note:
+                 // first_down - location of mouse down
+                 // last_down - location where the drag started
+                 point.x = sd->last_down[0].x;
+              }
+            else if (sd->one_drag_mode == ONE_DRAG_HORIZONTAL)
+              {
+                 point.y = sd->last_down[0].y;
+              }
+         }
+       evas_object_smart_callback_call(sd->child_obj, "one,move", &point);
+       return ECORE_CALLBACK_RENEW;
+     }
+   else
+     {
+       _smart_stop_animator_move(sd);
+       _smart_enter_none(sd);
+       return ECORE_CALLBACK_CANCEL;
+     }
+}
+
+static int
+_smart_animation_flick(void *data)
+{
+   Smart_Data *sd;
+   Flick_Data *flick_data;
+
+   sd = data;
+   flick_data = &(sd->flick_data);
+
+   if (flick_data && sd->child_obj)
+     {
+       // calculate dx, dy
+       float velocity = _smart_velocity_easeinoutcubic(flick_data->flick_index);
+       Evas_Coord dx = flick_data->avg_distance.x * velocity;
+       Evas_Coord dy = flick_data->avg_distance.y * velocity;
+       flick_data->flick_index++;
+       flick_data->last.x += dx;
+       flick_data->last.y += dy;
+       DBG("<< animation_flick |%d|%d|%f| >>\n", dx, dy, ecore_loop_time_get());
+
+       // stop flick animator
+       if (dx == 0 && dy == 0)
+         {
+            _smart_stop_animator_flick(sd);
+            _smart_enter_none(sd);
+            return ECORE_CALLBACK_CANCEL;
+         }
+       else
+         {
+            Evas_Coord_Point point;
+            point = flick_data->last;
+            if (sd->is_one_drag_mode)
+              {
+                 if (sd->one_drag_mode == ONE_DRAG_VERTICAL)
+                   {
+                      point.x = sd->first_down[0].x;
+                   }
+                 else if (sd->one_drag_mode == ONE_DRAG_HORIZONTAL)
+                   {
+                      point.y = sd->first_down[0].y;
+                   }
+              }
+            evas_object_smart_callback_call(sd->child_obj, "one,move", &point);
+            return ECORE_CALLBACK_RENEW;
+         }
+     }
+   else
+     {
+       _smart_stop_animator_flick(sd);
+       _smart_enter_none(sd);
+       return ECORE_CALLBACK_CANCEL;
+     }
+}
+
+static int
+_smart_animation_two_move(void *data)
+{
+   Smart_Data *sd;
+
+   sd = data;
+
+   if (sd->child_obj)
+     {
+       _smart_emit_two_move(sd);
+       return ECORE_CALLBACK_RENEW;
+     }
+   else
+     {
+       _smart_stop_animator_two_move(sd);
+       _smart_enter_none(sd);
+       return ECORE_CALLBACK_CANCEL;
+     }
+
+}
+
+/* state switching */
+static void
+_smart_enter_none(Smart_Data *sd)
+{
+   sd->numOfTouch = 0;
+   sd->two_drag_mode = TWO_DRAG_NONE;
+   sd->state = TOUCH_STATE_NONE;
+   DBG("\nTOUCH_STATE_NONE\n");
+}
+
+static void
+_smart_enter_down(Smart_Data *sd)
+{
+   // set press timer
+   sd->press_timer = ecore_timer_add(((double)PRESS_TIME)/1000.0, _smart_press_timer_handler, sd);
+
+   // set long press timer
+   sd->long_press_timer = ecore_timer_add(((double)LONG_HOLD_TIME)/1000.0, _smart_long_press_timer_handler, sd);
+
+   sd->state = TOUCH_STATE_DOWN;
+   DBG("\nTOUCH_STATE_DOWN\n");
+}
+
+static void
+_smart_enter_down_up(Smart_Data *sd, int downTime, int time)
+{
+   // remove sd->press_timer and set new timer
+   int timerTime = RELEASE_TIME - (downTime - PRESS_TIME);
+   if (sd->press_timer)
+     {
+       ecore_timer_del(sd->press_timer);
+       sd->press_timer = NULL;
+       sd->press_release_timer = ecore_timer_add(((double)timerTime)/1000.0, _smart_press_release_timer_handler, sd);
+
+     }
+   else
+     {
+       sd->release_timer = ecore_timer_add(((double)timerTime)/1000.0, _smart_release_timer_handler, sd);
+     }
+
+   if (sd->long_press_timer) // remove long press timer
+     {
+       ecore_timer_del(sd->long_press_timer);
+       sd->long_press_timer = NULL;
+     }
+
+   sd->state = TOUCH_STATE_DOWN_UP;
+   DBG("\nTOUCH_STATE_DOWN_UP\n");
+}
+
+static void
+_smart_enter_down_up_down(Smart_Data *sd)
+{
+   if (sd->press_release_timer) // remove press_release_timer
+     {
+       ecore_timer_del(sd->press_release_timer);
+       sd->press_release_timer = NULL;
+     }
+
+   if (sd->release_timer) // remove ReleaseTimer
+     {
+       ecore_timer_del(sd->release_timer);
+       sd->release_timer = NULL;
+     }
+
+   sd->state = TOUCH_STATE_DOWN_UP_DOWN;
+   DBG("\nTOUCH_STATE_DOWN_UP_DOWN\n");
+}
+
+static void
+_smart_enter_hold(Smart_Data *sd)
+{
+   sd->state = TOUCH_STATE_HOLD;
+   DBG("\nTOUCH_STATE_HOLD\n");
+}
+
+static void
+_smart_enter_drag(Smart_Data *sd)
+{
+   if (sd->press_timer) // remove press_timer
+     {
+       ecore_timer_del(sd->press_timer);
+       sd->press_timer = NULL;
+     }
+
+   if (sd->press_release_timer) // remove press_release_timer
+     {
+       ecore_timer_del(sd->press_release_timer);
+       sd->press_release_timer = NULL;
+     }
+
+   if (sd->release_timer) // remove ReleaseTimer
+     {
+       ecore_timer_del(sd->release_timer);
+       sd->release_timer = NULL;
+     }
+
+   if (sd->long_press_timer) // remove long press timer
+     {
+       ecore_timer_del(sd->long_press_timer);
+       sd->long_press_timer = NULL;
+     }
+
+   if (sd->child_obj)
+     {
+       if (sd->is_one_drag_mode)
+         {
+            sd->one_drag_mode = ONE_DRAG_NONE;
+            int abs_dx = abs(sd->first_down[0].x - sd->last_drag[0].x);
+            int abs_dy = abs(sd->first_down[0].y - sd->last_drag[0].y);
+            abs_dx = (abs_dx == 0) ? 1 : abs_dx;
+            DBG("<< abs_dx[%d], abs_dy[%d] >>\n\n", abs_dx, abs_dy);
+            float degree = (float)abs_dy / (float)abs_dx;
+            // more than 70 degree
+            if (degree > tan(70 * M_PI / 180))
+              {
+                 sd->one_drag_mode = ONE_DRAG_VERTICAL;
+              }
+            // less than 20 degree
+            else if (degree < tan(20 * M_PI / 180))
+              {
+                 sd->one_drag_mode = ONE_DRAG_HORIZONTAL;
+              }
+         }
+       Evas_Point point;
+       point.x = sd->last_down[0].x;
+       point.y = sd->last_down[0].y;
+       evas_object_smart_callback_call(sd->child_obj, "one,move,start", &point);
+
+       // initialize flick variables
+       sd->last_move_history_index = -1;
+       sd->move_history_count = 0;
+
+       sd->animator_move = ecore_animator_add(_smart_animation_move, sd);
+       DBG("<< sd->animator_move >>\n");
+       sd->state = TOUCH_STATE_DRAG;
+       DBG("\nTOUCH_STATE_DRAG\n");
+     }
+   else
+     {
+       sd->state = TOUCH_STATE_NONE;
+     }
+}
+
+static void
+_smart_enter_two_down(Smart_Data *sd)
+{
+   _smart_stop_all_timers(sd);
+
+   if (sd->child_obj)
+     {
+       DBG("<< enter two down >>\n");
+       sd->state = TOUCH_STATE_TWO_DOWN;
+       _smart_emit_two_press(sd);
+     }
+}
+
+static void
+_smart_enter_two_drag(Smart_Data *sd)
+{
+   if (sd->child_obj)
+     {
+       DBG("<< sd->animator_two_move >>\n");
+       sd->state = TOUCH_STATE_TWO_DRAG;
+       _smart_emit_two_move_start(sd);
+       sd->animator_two_move = ecore_animator_add(_smart_animation_two_move, sd);
+     }
+   else
+     {
+       sd->state = TOUCH_STATE_NONE;
+     }
+}
+
+static void
+_smart_enter_three_down(Smart_Data *sd)
+{
+   if (sd->child_obj)
+     {
+       sd->state = TOUCH_STATE_THREE_DOWN;
+       _smart_emit_three_press(sd);
+     }
+}
+
+/* producing output events */
+static void
+_smart_emit_press(Smart_Data *sd)
+{
+   if (sd->child_obj)
+     {
+       DBG("<< emit_press >>\n");
+       Evas_Point point;
+       point.x = sd->last_down[0].x;
+       point.y = sd->last_down[0].y;
+       evas_object_smart_callback_call(sd->child_obj, "one,press", &point);
+     }
+}
+
+static void
+_smart_emit_tap(Smart_Data *sd)
+{
+   if (sd->child_obj)
+     {
+       DBG("<< emit_tap >>\n");
+       Evas_Point point;
+       point.x = sd->last_down[0].x;
+       point.y = sd->last_down[0].y;
+       evas_object_smart_callback_call(sd->child_obj, "one,single,tap", &point);
+     }
+}
+
+static void
+_smart_emit_double_tap(Smart_Data *sd)
+{
+   if (sd->child_obj)
+     {
+       DBG("<< emit_double_tap >>\n");
+       Evas_Point point;
+       point.x = sd->last_down[0].x;
+       point.y = sd->last_down[0].y;
+       evas_object_smart_callback_call(sd->child_obj, "one,double,tap", &point);
+     }
+}
+
+static void
+_smart_emit_long_hold(Smart_Data *sd)
+{
+   if (sd->child_obj)
+     {
+       DBG("<< emit_long_hold >>\n");
+       Evas_Point point;
+       point.x = sd->last_down[0].x;
+       point.y = sd->last_down[0].y;
+       evas_object_smart_callback_call(sd->child_obj, "one,long,press", &point);
+     }
+}
+
+static void
+_smart_emit_release(Smart_Data *sd)
+{
+   if (sd->child_obj)
+     {
+       DBG("<< emit_release >>\n");
+       Evas_Point point;
+       point.x = sd->last_down[0].x;
+       point.y = sd->last_down[0].y;
+       evas_object_smart_callback_call(sd->child_obj, "one,release", &point);
+     }
+}
+
+static void
+_smart_emit_two_press(Smart_Data *sd)
+{
+   if (sd->child_obj)
+     {
+       DBG("<< emit_two_press >>\n");
+       Two_Mouse_Data two_mouse_data;
+       two_mouse_data.first.x = sd->last_down[0].x;
+       two_mouse_data.first.y = sd->last_down[0].y;
+       two_mouse_data.second.x = sd->last_down[1].x;
+       two_mouse_data.second.y = sd->last_down[1].y;
+       two_mouse_data.mode = sd->two_drag_mode;
+       evas_object_smart_callback_call(sd->child_obj, "two,press", &two_mouse_data);
+     }
+}
+
+static void
+_smart_emit_two_tap(Smart_Data *sd)
+{
+   if (sd->child_obj)
+     {
+       DBG("<< emit_two_tap >>\n");
+       Two_Mouse_Data two_mouse_data;
+       two_mouse_data.first.x = sd->last_down[0].x;
+       two_mouse_data.first.y = sd->last_down[0].y;
+       two_mouse_data.second.x = sd->last_down[1].x;
+       two_mouse_data.second.y = sd->last_down[1].y;
+       two_mouse_data.mode = sd->two_drag_mode;
+       evas_object_smart_callback_call(sd->child_obj, "two,tap", &two_mouse_data);
+     }
+}
+
+static void
+_smart_emit_two_move_start(Smart_Data *sd)
+{
+   if (sd->child_obj)
+     {
+       DBG("<< emit_two_move_start >>\n");
+       Two_Mouse_Data two_mouse_data;
+       two_mouse_data.first.x = sd->last_drag[0].x;
+       two_mouse_data.first.y = sd->last_drag[0].y;
+       two_mouse_data.second.x = sd->last_drag[1].x;
+       two_mouse_data.second.y = sd->last_drag[1].y;
+       two_mouse_data.mode = sd->two_drag_mode;
+       evas_object_smart_callback_call(sd->child_obj, "two,move,start", &two_mouse_data);
+     }
+}
+
+static void
+_smart_emit_two_move(Smart_Data *sd)
+{
+   if (sd->child_obj)
+     {
+       Two_Mouse_Data two_mouse_data;
+       two_mouse_data.first.x = sd->last_drag[0].x;
+       two_mouse_data.first.y = sd->last_drag[0].y;
+       two_mouse_data.second.x = sd->last_drag[1].x;
+       two_mouse_data.second.y = sd->last_drag[1].y;
+       two_mouse_data.mode = sd->two_drag_mode;
+       evas_object_smart_callback_call(sd->child_obj, "two,move", &two_mouse_data);
+     }
+}
+
+static void
+_smart_emit_two_move_end(Smart_Data *sd)
+{
+   if (sd->child_obj)
+     {
+       DBG("<< emit_two_move_end >>\n");
+       Two_Mouse_Data two_mouse_data;
+       two_mouse_data.first.x = sd->last_drag[0].x;
+       two_mouse_data.first.y = sd->last_drag[0].y;
+       two_mouse_data.second.x = sd->last_drag[1].x;
+       two_mouse_data.second.y = sd->last_drag[1].y;
+       two_mouse_data.mode = sd->two_drag_mode;
+       evas_object_smart_callback_call(sd->child_obj, "two,move,end", &two_mouse_data);
+     }
+}
+
+static void
+_smart_emit_three_press(Smart_Data *sd)
+{
+   if (sd->child_obj)
+     {
+       DBG("<< emit_three_press >>\n");
+       Three_Mouse_Data three_mouse_data;
+       three_mouse_data.first.x = sd->last_drag[0].x;
+       three_mouse_data.first.y = sd->last_drag[0].y;
+       three_mouse_data.second.x = sd->last_drag[1].x;
+       three_mouse_data.second.y = sd->last_drag[1].y;
+       three_mouse_data.third.x = sd->last_drag[2].x;
+       three_mouse_data.third.y = sd->last_drag[2].y;
+       evas_object_smart_callback_call(sd->child_obj, "three,press", &three_mouse_data);
+     }
+}
+
+static void
+_smart_emit_three_tap(Smart_Data *sd)
+{
+   if (sd->child_obj)
+     {
+       DBG("<< emit_three_tap >>\n");
+       Three_Mouse_Data three_mouse_data;
+       three_mouse_data.first.x = sd->last_drag[0].x;
+       three_mouse_data.first.y = sd->last_drag[0].y;
+       three_mouse_data.second.x = sd->last_drag[1].x;
+       three_mouse_data.second.y = sd->last_drag[1].y;
+       three_mouse_data.third.x = sd->last_drag[2].x;
+       three_mouse_data.third.y = sd->last_drag[2].y;
+       evas_object_smart_callback_call(sd->child_obj, "three,tap", &three_mouse_data);
+     }
+}
+
+/* timer event handling */
+static int
+_smart_press_timer_handler(void *data)
+{
+   Smart_Data *sd;
+
+   sd = data;
+   _smart_emit_press(sd);
+   sd->press_timer = NULL;
+   return ECORE_CALLBACK_CANCEL;
+}
+
+static int
+_smart_long_press_timer_handler(void *data)
+{
+   Smart_Data *sd;
+
+   sd = data;
+   _smart_emit_long_hold(sd);
+   _smart_enter_hold(sd);
+   sd->long_press_timer = NULL;
+   return ECORE_CALLBACK_CANCEL;
+}
+
+static int
+_smart_release_timer_handler(void *data)
+{
+   Smart_Data *sd;
+
+   sd = data;
+   _smart_emit_tap(sd);
+   _smart_stop_all_timers(sd);
+   _smart_enter_none(sd);
+   sd->release_timer = NULL;
+   return ECORE_CALLBACK_CANCEL;
+}
+
+static int
+_smart_press_release_timer_handler(void *data)
+{
+   Smart_Data *sd;
+
+   sd = data;
+   _smart_emit_press(sd);
+   _smart_emit_tap(sd);
+   _smart_stop_all_timers(sd);
+   _smart_enter_none(sd);
+   sd->press_release_timer = NULL;
+   return ECORE_CALLBACK_CANCEL;
+}
+
+/* other functions */
+static void
+_smart_save_move_history(Smart_Data *sd, int x, int y, int dx, int dy)
+{
+   // save pan information to the pan history
+   int index = (sd->last_move_history_index + 1) % MOVE_HISTORY_SIZE;
+   sd->last_move_history_index = index;
+   sd->move_history[index].dx = dx;
+   sd->move_history[index].dy = dy;
+   sd->move_history[index].time = ecore_time_get();
+   sd->move_history_count++;
+}
+
+static void
+_smart_start_flick(Smart_Data *sd)
+{
+   if (sd->animator_move)
+     {
+       ecore_animator_del(sd->animator_move);
+       DBG("<< stop_animator_move >>\n");
+       sd->animator_move = NULL;
+
+       // start flick
+       // accumulate sd->move_history data
+       int nSamples = 0;
+       int totalDx = 0;
+       int totalDy = 0;
+       int index = sd->last_move_history_index;
+       int todo = sd->move_history_count > MOVE_HISTORY_SIZE ? MOVE_HISTORY_SIZE : sd->move_history_count;
+       Mouse_Diff_Data *p;
+       double endTime = (sd->move_history + index)->time;
+       double startTime = endTime;
+       for( ; todo > 0; todo--) {
+            p = sd->move_history + index; // get one sd->move_history
+
+            // get values
+            startTime = p->time;
+            totalDx += p->dx;
+            totalDy += p->dy;
+            nSamples++;
+
+            if ((endTime - startTime) > 0.2 && nSamples > 0)
+              break;
+
+            index = (index > 0) ? (index - 1) : (MOVE_HISTORY_SIZE - 1); // set index
+       }
+       double totalTime = endTime - startTime;
+       if (totalTime < DOUBLE_ERROR)
+         totalTime = 0.001;
+
+       // calculate average pan_dx and pan_dy (per 1 / DEFAULT_FRAMERATE ms)
+       double temp = totalTime * DEFAULT_FRAMERATE;
+       if (temp <= 0)
+         temp = 1;
+
+       Flick_Data *flick_data = &sd->flick_data;
+       flick_data->avg_distance.x = totalDx / temp;
+       flick_data->avg_distance.y = totalDy / temp;
+
+       // set max value for pan_dx and pan_dy
+       int abs_pan_dx = abs(flick_data->avg_distance.x);
+       int abs_pan_dy = abs(flick_data->avg_distance.y);
+       if ((abs_pan_dx > MAX_MOVE_DISTANCE) && (abs_pan_dx > abs_pan_dy))
+         {
+            flick_data->avg_distance.x = (flick_data->avg_distance.x > 0) ? MAX_MOVE_DISTANCE : -MAX_MOVE_DISTANCE;
+            flick_data->avg_distance.y = flick_data->avg_distance.y * MAX_MOVE_DISTANCE / abs_pan_dx;
+
+         }
+       else if ((abs_pan_dy > MAX_MOVE_DISTANCE) && (abs_pan_dy > abs_pan_dx))
+         {
+            flick_data->avg_distance.y = (flick_data->avg_distance.y > 0) ? MAX_MOVE_DISTANCE : -MAX_MOVE_DISTANCE;
+            flick_data->avg_distance.x = flick_data->avg_distance.x * MAX_MOVE_DISTANCE / abs_pan_dy;
+         }
+
+       if (abs_pan_dx > FLICK_THRESHOLD || abs_pan_dy > FLICK_THRESHOLD)
+         {
+            // set flick_data and start flick
+            flick_data->last.x = sd->last_drag[0].x;
+            flick_data->last.y = sd->last_drag[0].y;
+            flick_data->flick_index = 0;
+            sd->animator_flick = ecore_animator_add(_smart_animation_flick, sd);
+            DBG("<< sd->animator_flick >>\n");
+         }
+       else
+         {
+            Evas_Point point;
+            point.x = sd->last_drag[0].x;
+            point.y = sd->last_drag[0].y;
+            evas_object_smart_callback_call(sd->child_obj, "one,move,end", &point);
+            _smart_enter_none(sd);
+         }
+     }
+   else
+     {
+       _smart_emit_release(sd);
+       _smart_stop_all_timers(sd);
+       _smart_enter_none(sd);
+     }
+}
+
+static void
+_smart_stop_animator_move(Smart_Data *sd)
+{
+   if (sd->animator_move)
+     {
+       ecore_animator_del(sd->animator_move);
+       DBG("<< stop_animator_move >>\n");
+       sd->animator_move = NULL;
+       Evas_Point point;
+       point.x = sd->last_drag[0].x;
+       point.y = sd->last_drag[0].y;
+       evas_object_smart_callback_call(sd->child_obj, "one,move,end", &point);
+     }
+}
+
+static void
+_smart_stop_animator_flick(Smart_Data *sd)
+{
+   if (sd->animator_flick)
+     {
+       ecore_animator_del(sd->animator_flick);
+       DBG("<< stop_animator_flick >>\n");
+       sd->animator_flick = NULL;
+       Evas_Coord_Point point;
+       point = sd->flick_data.last;
+       evas_object_smart_callback_call(sd->child_obj, "one,move,end", &point);
+     }
+}
+
+static void
+_smart_stop_animator_two_move(Smart_Data *sd)
+{
+   if (sd->animator_two_move)
+     {
+       ecore_animator_del(sd->animator_two_move);
+       DBG("<< stop_animator_two_move >>\n");
+       sd->animator_two_move = NULL;
+       _smart_emit_two_move_end(sd);
+     }
+}
+
+static Two_Drag_Mode
+_smart_check_two_drag_mode(Smart_Data *sd)
+{
+   // get distance from press to current position
+   int dx0 = sd->last_drag[0].x - sd->first_down[0].x;
+   int dy0 = sd->last_drag[0].y - sd->first_down[0].y;
+   int dx1 = sd->last_drag[1].x - sd->first_down[1].x;
+   int dy1 = sd->last_drag[1].y - sd->first_down[1].y;
+   int dx = 0;
+   int dy = 0;
+
+   // select dx and dy
+   if ((abs(dx1) >= MOVE_THRESHOLD) || (abs(dy1) >= MOVE_THRESHOLD))
+     {
+       dx = dx1;
+       dy = dy1;
+     }
+   else if ((abs(dx0) >= MOVE_THRESHOLD) || (abs(dy0) >= MOVE_THRESHOLD))
+     {
+       dx = dx0;
+       dy = dy0;
+     }
+   else
+     {
+       return TWO_DRAG_NONE;
+     }
+
+   // same x direction
+   if ((abs(dx) > abs(dy)) && ((dx0 > 0 && dx1 > 0) || (dx0 < 0 && dx1 < 0)))
+     {
+       dy = (dy == 0) ? 1 : dy;
+       // less than 30 degree (1024/root(3) = 591)
+       if (((abs(dy) << 10) / abs(dx)) < 591)
+         {
+            return TWO_DRAG_HORIZONTAL;
+         }
+     }
+
+   // same y direction
+   if ((abs(dy) > abs(dx)) && ((dy0 > 0 && dy1 > 0) || (dy0 < 0 && dy1 < 0)))
+     {
+       dx = (dx == 0) ? 1 : dx;
+       // more than 60 degree (1024 * root(3)/1 = 1773)
+       if (((abs(dy) << 10) / abs(dx)) > 1773)
+         {
+            return TWO_DRAG_VERTICAL;
+         }
+     }
+
+   // pinch direction
+   int distanceX = abs(abs(sd->first_down[0].x - sd->first_down[1].x)
+        - abs(sd->last_drag[0].x - sd->last_drag[1].x));
+   int distanceY = abs(abs(sd->first_down[0].y - sd->first_down[1].y)
+        - abs(sd->last_drag[0].y - sd->last_drag[1].y));
+   if ((distanceX > FINGER_DISTANCE) || (distanceY > FINGER_DISTANCE))
+     {
+       return TWO_DRAG_PINCH;
+     }
+
+   return TWO_DRAG_NONE;
+}
+
+static void
+_smart_set_first_down(Smart_Data *sd, int index, Mouse_Data *data)
+{
+   if (index > N_FINGER)
+     {
+       return;
+     }
+
+   if ((sd->screen_angle == 270) && (index > 0))
+     {
+       sd->first_down[index].x = data->y;
+       sd->first_down[index].y = data->x;
+       sd->first_down[index].time = data->time;
+       sd->first_down[index].device = data->device;
+     }
+   else
+     {
+       sd->first_down[index].x = data->x;
+       sd->first_down[index].y = data->y;
+       sd->first_down[index].time = data->time;
+       sd->first_down[index].device = data->device;
+     }
+}
+
+static void
+_smart_set_last_down(Smart_Data *sd, int index, Mouse_Data *data)
+{
+   if (index > N_FINGER)
+     {
+       return;
+     }
+
+   if ((sd->screen_angle == 270) && (index > 0))
+     {
+       sd->last_down[index].x = data->y;
+       sd->last_down[index].y = data->x;
+       sd->last_down[index].time = data->time;
+       sd->last_down[index].device = data->device;
+     }
+   else
+     {
+       sd->last_down[index].x = data->x;
+       sd->last_down[index].y = data->y;
+       sd->last_down[index].time = data->time;
+       sd->last_down[index].device = data->device;
+     }
+}
+
+static void
+_smart_set_last_drag(Smart_Data *sd, int index, Mouse_Data *data)
+{
+   if (index > N_FINGER)
+     {
+       return;
+     }
+
+   if ((sd->screen_angle == 270) && (index > 0))
+     {
+       sd->last_drag[index].x = data->y;
+       sd->last_drag[index].y = data->x;
+       sd->last_drag[index].time = data->time;
+       sd->last_drag[index].device = data->device;
+     }
+   else
+     {
+       sd->last_drag[index].x = data->x;
+       sd->last_drag[index].y = data->y;
+       sd->last_drag[index].time = data->time;
+       sd->last_drag[index].device = data->device;
+     }
+}
+
+static void
+_smart_stop_all_timers(Smart_Data *sd)
+{
+   if (sd->press_timer) // remove sd->press_timer
+     {
+       ecore_timer_del(sd->press_timer);
+       sd->press_timer = NULL;
+     }
+
+   if (sd->long_press_timer) // remove long press timer
+     {
+       ecore_timer_del(sd->long_press_timer);
+       sd->long_press_timer = NULL;
+     }
+
+   if (sd->release_timer) // remove release timer
+     {
+       ecore_timer_del(sd->release_timer);
+       sd->release_timer = NULL;
+     }
+
+   if (sd->press_release_timer) // remove pressRelease timer
+     {
+       ecore_timer_del(sd->press_release_timer);
+       sd->press_release_timer = NULL;
+     }
+}
+
+static void
+_smart_add(Evas_Object *obj)
+{
+   Smart_Data *sd;
+
+   sd = calloc(1, sizeof(Smart_Data));
+   if (!sd) return;
+   memset((void *)sd, 0x00, sizeof(Smart_Data));
+
+   sd->smart_obj = obj;
+
+   // set default framerate
+   ecore_animator_frametime_set(1.0 / DEFAULT_FRAMERATE);
+
+   evas_object_smart_data_set(obj, sd);
+}
+
+static void
+_smart_del(Evas_Object *obj)
+{
+   INTERNAL_ENTRY;
+   if (sd)
+     {
+       if (sd->press_timer)
+         ecore_timer_del(sd->press_timer);
+
+       if (sd->long_press_timer)
+         ecore_timer_del(sd->long_press_timer);
+
+       if (sd->release_timer)
+         ecore_timer_del(sd->release_timer);
+
+       if (sd->press_release_timer)
+         ecore_timer_del(sd->press_release_timer);
+
+       if (sd->animator_move)
+         ecore_animator_del(sd->animator_move);
+
+       if (sd->animator_flick)
+         ecore_animator_del(sd->animator_flick);
+
+       if (sd->animator_two_move)
+         ecore_animator_del(sd->animator_two_move);
+
+       if (sd->child_obj)
+         {
+            evas_object_event_callback_del(sd->child_obj, EVAS_CALLBACK_MOUSE_DOWN, _smart_mouse_down);
+            evas_object_event_callback_del(sd->child_obj, EVAS_CALLBACK_MOUSE_UP, _smart_mouse_up);
+            evas_object_event_callback_del(sd->child_obj, EVAS_CALLBACK_MOUSE_MOVE, _smart_mouse_move);
+            evas_object_event_callback_del(sd->child_obj, EVAS_CALLBACK_MULTI_DOWN, _smart_multi_down);
+            evas_object_event_callback_del(sd->child_obj, EVAS_CALLBACK_MULTI_UP, _smart_multi_up);
+            evas_object_event_callback_del(sd->child_obj, EVAS_CALLBACK_MULTI_MOVE, _smart_multi_move);
+         }
+
+       free(sd);
+     }
+}
+
+static void
+_smart_init(void)
+{
+   if (_smart) return;
+
+   static const Evas_Smart_Class sc = 
+     {
+       SMART_NAME,
+       EVAS_SMART_CLASS_VERSION,
+       _smart_add,
+       _smart_del,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL,
+       NULL
+     };
+   _smart = evas_smart_class_new(&sc);
+}
diff --git a/src/lib/els_touch.h b/src/lib/els_touch.h
new file mode 100644 (file)
index 0000000..902be70
--- /dev/null
@@ -0,0 +1,7 @@
+Evas_Object *_elm_smart_touch_add                     (Evas *evas);
+void         _elm_smart_touch_child_set               (Evas_Object *obj, Evas_Object *child);
+void         _elm_smart_touch_start                   (Evas_Object *obj);
+void         _elm_smart_touch_stop                    (Evas_Object *obj);
+void         _elm_smart_touch_reset                   (Evas_Object *obj);
+void         _elm_smart_touch_screen_angle_update     (Evas_Object *obj, int screen_angle);
+void         _elm_smart_touch_is_one_drag_mode_enable (Evas_Object *obj, Eina_Bool is_one_drag_mode);