Update live-viewer.
authorSung-jae Park <nicesj.park@samsung.com>
Sat, 1 Dec 2012 15:32:46 +0000 (00:32 +0900)
committerSung-jae Park <nicesj.park@samsung.com>
Tue, 4 Dec 2012 12:19:11 +0000 (21:19 +0900)
Change-Id: I2fd1132a6afa1e4cd300df6125370de09264b370

27 files changed:
live.viewer/CMakeLists.txt
live.viewer/deprecated/CLiveBox.cpp [moved from live.viewer/src/CLiveBox.cpp with 100% similarity]
live.viewer/deprecated/CLiveBox.h [moved from live.viewer/include/CLiveBox.h with 100% similarity]
live.viewer/deprecated/CLiveBoxMgr.cpp [moved from live.viewer/src/CLiveBoxMgr.cpp with 100% similarity]
live.viewer/deprecated/CLiveBoxMgr.h [moved from live.viewer/include/CLiveBoxMgr.h with 100% similarity]
live.viewer/deprecated/CMain.cpp [moved from live.viewer/src/CMain.cpp with 100% similarity]
live.viewer/deprecated/CMain.h [moved from live.viewer/include/CMain.h with 100% similarity]
live.viewer/deprecated/CResourceMgr.cpp [moved from live.viewer/src/CResourceMgr.cpp with 100% similarity]
live.viewer/deprecated/CResourceMgr.h [moved from live.viewer/include/CResourceMgr.h with 100% similarity]
live.viewer/deprecated/CUtil.cpp [moved from live.viewer/src/CUtil.cpp with 98% similarity]
live.viewer/deprecated/CUtil.h [moved from live.viewer/include/CUtil.h with 100% similarity]
live.viewer/deprecated/dlist.cpp [moved from live.viewer/src/dlist.cpp with 100% similarity]
live.viewer/deprecated/dlist.h [new file with mode: 0644]
live.viewer/include/debug.h [new file with mode: 0644]
live.viewer/include/lb.h [new file with mode: 0644]
live.viewer/include/live_scroller.h [new file with mode: 0644]
live.viewer/include/main.h [new file with mode: 0644]
live.viewer/include/scroller.h [new file with mode: 0644]
live.viewer/include/util.h [new file with mode: 0644]
live.viewer/live.viewer.xml
live.viewer/res/live-viewer.edc
live.viewer/src/dlist.c [new file with mode: 0644]
live.viewer/src/lb.c [new file with mode: 0644]
live.viewer/src/live_scroller.c [new file with mode: 0644]
live.viewer/src/main.c [new file with mode: 0644]
live.viewer/src/scroller.c [new file with mode: 0644]
live.viewer/src/util.c [new file with mode: 0644]

index 60c48a0..9be5a14 100644 (file)
@@ -1,4 +1,4 @@
-PROJECT(live-viewer CXX)
+PROJECT(live-viewer C)
 CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
 
 INCLUDE(FindPkgConfig)
@@ -26,18 +26,19 @@ SET(PKGROOT "/opt/usr/apps/live.viewer")
 ADD_DEFINITIONS("-DNDEBUG")
 ADD_DEFINITIONS("-DPKGROOT=\"${PKGROOT}\"")
 #ADD_DEFINITIONS("-DFLOG")
+ADD_DEFINITIONS("-DLOG_TAG=\"${PROJECT_NAME}\"")
 ADD_DEFINITIONS(${pkgs_CFLAGS})
 ADD_DEFINITIONS(${pkgs_LDFLAGS})
 
 INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/include)
 
 ADD_EXECUTABLE(${PROJECT_NAME}
-       src/CLiveBox.cpp
-       src/CLiveBoxMgr.cpp
-       src/CMain.cpp
-       src/CResourceMgr.cpp
-       src/CUtil.cpp
-       src/dlist.cpp
+       src/main.c
+       src/dlist.c
+       src/live_scroller.c
+       src/util.c
+       src/scroller.c
+       src/lb.c
 )
 
 TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${pkgs_LDFLAGS})
similarity index 98%
rename from live.viewer/src/CUtil.cpp
rename to live.viewer/deprecated/CUtil.cpp
index 79ad646..1895df3 100644 (file)
@@ -59,9 +59,7 @@ Evas_Object *CUtil::CreateCanvasImage(Evas_Object *parent, int w, int h)
 
        evas_object_image_colorspace_set(pCanvasImage, EVAS_COLORSPACE_ARGB8888);
        evas_object_image_alpha_set(pCanvasImage, EINA_TRUE);
-
        evas_object_move(pCanvasImage, 0, 0);
-       evas_object_resize(pCanvasImage, w, h);
 
        return pCanvasImage;
 }
diff --git a/live.viewer/deprecated/dlist.h b/live.viewer/deprecated/dlist.h
new file mode 100644 (file)
index 0000000..5c583e6
--- /dev/null
@@ -0,0 +1,27 @@
+#define dlist_remove_data(list, data) do { \
+       struct dlist *l; \
+       l = dlist_find_data(list, data); \
+       list = dlist_remove(list, l); \
+} while (0)
+
+#define dlist_foreach(list, l, data) \
+       for ((l) = (list); (l) && ((data) = dlist_data(l)); (l) = dlist_next(l))
+
+#define dlist_foreach_safe(list, l, n, data) \
+       for ((l) = (list), (n) = dlist_next(l); \
+               (l) && ((data) = dlist_data(l)); \
+               (l) = (n), (n) = dlist_next(l))
+
+struct dlist;
+
+extern struct dlist *dlist_append(struct dlist *list, void *data);
+extern struct dlist *dlist_prepend(struct dlist *list, void *data);
+extern struct dlist *dlist_remove(struct dlist *list, struct dlist *l);
+extern struct dlist *dlist_find_data(struct dlist *list, void *data);
+extern void *dlist_data(struct dlist *l);
+extern struct dlist *dlist_next(struct dlist *l);
+extern struct dlist *dlist_prev(struct dlist *l);
+extern int dlist_count(struct dlist *l);
+extern struct dlist *dlist_nth(struct dlist *l, int nth);
+
+/* End of a file */
diff --git a/live.viewer/include/debug.h b/live.viewer/include/debug.h
new file mode 100644 (file)
index 0000000..dc6c4a4
--- /dev/null
@@ -0,0 +1,11 @@
+#if !defined(FLOG)
+#define DbgPrint(format, arg...)       LOGD("[\e[32m%s/%s\e[0m:%d] " format, util_basename(__FILE__), __func__, __LINE__, ##arg)
+#define ErrPrint(format, arg...)       LOGE("[\e[32m%s/%s\e[0m:%d] " format, util_basename(__FILE__), __func__, __LINE__, ##arg)
+#else
+extern FILE *__file_log_fp;
+#define DbgPrint(format, arg...) do { fprintf(__file_log_fp, "[LOG] [\e[32m%s/%s\e[0m:%d] " format, util_basename(__FILE__), __func__, __LINE__, ##arg); fflush(__file_log_fp); } while (0)
+
+#define ErrPrint(format, arg...) do { fprintf(__file_log_fp, "[ERR] [\e[32m%s/%s\e[0m:%d] " format, util_basename(__FILE__), __func__, __LINE__, ##arg); fflush(__file_log_fp); } while (0)
+#endif
+
+/* End of a file */
diff --git a/live.viewer/include/lb.h b/live.viewer/include/lb.h
new file mode 100644 (file)
index 0000000..95c4520
--- /dev/null
@@ -0,0 +1,5 @@
+extern int lb_init(void);
+extern int lb_fini(void);
+extern int lb_add(Evas_Object *sc, const char *pkgname);
+
+/* End of a file */
diff --git a/live.viewer/include/live_scroller.h b/live.viewer/include/live_scroller.h
new file mode 100644 (file)
index 0000000..0f911f7
--- /dev/null
@@ -0,0 +1,41 @@
+struct live_sc_event_info {
+       int curidx;
+       int toidx;
+};
+
+struct live_sc_drag_info {
+       int dx;
+       int dy;
+};
+
+struct live_sc_move_info {
+       Evas_Object *item;
+       Evas_Coord x;
+       Evas_Coord y;
+       Evas_Coord w;
+       Evas_Coord h;
+
+       double relx;
+       double rely;
+};
+
+extern Evas_Object *live_scroller_add(Evas_Object *parent);
+extern int live_scroller_append(Evas_Object *scroller, Evas_Object *item);
+extern Evas_Object *live_scroller_remove(Evas_Object *scroller, int idx);
+extern Evas_Object *live_scroller_get_item(Evas_Object *scroller, int idx);
+extern int live_scroller_get_current(Evas_Object *scroller);
+extern int live_scroller_loop_set(Evas_Object *scroller, int is_loop);
+
+extern int live_scroller_freeze(Evas_Object *scroller);
+extern int live_scroller_thaw(Evas_Object *scroller);
+
+extern int live_scroller_anim_to(Evas_Object *scroller, double fps, int offset);
+extern int live_scroller_go_to(Evas_Object *scroller, int idx);
+
+extern int live_scroller_update(Evas_Object *scroller);
+
+extern int live_scroller_remove_by_obj(Evas_Object *scroller, Evas_Object *obj);
+extern int live_scroller_get_item_index(Evas_Object *scroller, Evas_Object *item);
+extern int live_scroller_get_item_count(Evas_Object *scroller);
+
+/* End of a file */
diff --git a/live.viewer/include/main.h b/live.viewer/include/main.h
new file mode 100644 (file)
index 0000000..91ec6e4
--- /dev/null
@@ -0,0 +1 @@
+extern Evas_Object *main_get_window(void);
diff --git a/live.viewer/include/scroller.h b/live.viewer/include/scroller.h
new file mode 100644 (file)
index 0000000..abc5cdb
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * com.samsung.live-magazine
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact: Sung-jae Park <nicesj.park@samsung.com>, Youngjoo Park <yjoo93.park@samsung.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+
+extern Evas_Object *scroller_create(Evas_Object *parent);
+extern Evas_Object *scroller_peek_by_idx(Evas_Object *sc, int idx);
+extern int scroller_peek_by_obj(Evas_Object *sc, Evas_Object *obj);
+extern int scroller_append(Evas_Object *sc, Evas_Object *child);
+extern int scroller_get_current_idx(Evas_Object *sc);
+extern int scroller_peek_by_obj(Evas_Object *sc, Evas_Object *obj);
+extern Evas_Object *scroller_get_page(Evas_Object *sc, int idx);
+extern int scroller_is_scrolling(Evas_Object *sc);
+
+extern int scroller_add_stop_cb(Evas_Object *scroller, int (*cb)(Evas_Object *sc, void *data), void *data);
+extern void scroller_del_stop_cb(Evas_Object *scroller, int (*cb)(Evas_Object *sc, void *data), void *data);
+
+extern int scroller_get_page_index(Evas_Object *sc, Evas_Object *page);
+
+extern void scroller_unlock(Evas_Object *sc);
+extern void scroller_lock(Evas_Object *sc);
+
+extern int scroller_get_page_count(Evas_Object *sc);
+extern int scroller_scroll_to(Evas_Object *sc, int idx);
+extern int scroller_jump_to(Evas_Object *sc, int idx);
+
+extern int scroller_destroy(Evas_Object *sc);
+extern int scroller_update(Evas_Object *sc, void *data);
+extern int scroller_fast_scroll(Evas_Object *sc, int idx);
+extern void scroller_loop_set(Evas_Object *sc, Eina_Bool val);
+extern void scroller_quick_navi(Evas_Object *sc, Eina_Bool val);
+
+/* End of a file */
diff --git a/live.viewer/include/util.h b/live.viewer/include/util.h
new file mode 100644 (file)
index 0000000..a3ebd58
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * com.samsung.live-magazine
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact: Sung-jae Park <nicesj.park@samsung.com>, Youngjoo Park <yjoo93.park@samsung.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+extern const char *util_basename(const char *name);
+
+/* End of a file */
index e011336..7a55872 100644 (file)
@@ -4,8 +4,8 @@
        <author email="nicesj.park@samsung.com" href="www.samsung.com">Sung-jae Park</author>
        <description>Live box simple viewer (native)</description>
 
-       <ui-application appid="live.viewer" exec="/opt/usr/apps/live.viewer/bin/live-viewer" nodisplay="true" multiple="false" type="capp" taskmanage="false">
-               <icon>live.viewer.png</icon>
+       <ui-application appid="live.viewer" exec="/opt/usr/apps/live.viewer/bin/live-viewer" nodisplay="false" multiple="false" type="capp" taskmanage="true">
+               <icon>live-viewer.png</icon>
                <label>Live box simple viewer (native)</label>
                <label xml:lang="en-us">Live box simple viewer (native)</label>
        </ui-application>
index 76266a8..2b83568 100644 (file)
@@ -19,63 +19,66 @@ collections {
                        }
 
                        part {
-                               name: "viewer";
+                               name: "delete,btn";
                                type: SWALLOW;
                                mouse_events: 1;
                                description {
                                        state: "default" 0.0;
-                                       rel1 { relative: 10/720 1.0; to, "indicator"; }
-                                       rel2 { relative: 710/720 800/1280; }
+                                       rel1 { relative: 4/720 1.0; to_y, "viewer"; }
+                                       rel2 { relative: 716/720 850/1280; }
                                }
                        }
 
                        part {
-                               name: "controller";
+                               name: "controller"; /* size list */
                                type: SWALLOW;
                                mouse_events: 1;
                                description {
                                        state: "default" 0.0;
-                                       rel1 { relative: 0.0 1.0; to_y, "viewer"; }
-                                       rel2 { relative: 0.3 1230/1280; }
+                                       rel1 { relative: 0.0 1.0; to_y, "delete,btn"; }
+                                       rel2 { relative: 0.3 1.0; }
                                }
                        }
 
                        part {
-                               name: "pd"; /* button ctrl */
+                               name: "logger";
                                type: SWALLOW;
                                mouse_events: 1;
                                description {
                                        state: "default" 0.0;
-                                       rel1 { relative: 0.0 1.0; to_y, "controller"; }
-                                       rel2 { relative: 1.0 1.0; to_x, "controller"; }
+                                       rel1 { relative: 1.0 1.0; to_x: "controller"; to_y: "delete,btn"; }
+                                       rel2 { relative: 1.0 1.0; }
                                }
                        }
 
                        part {
-                               name: "logger";
-                               type: SWALLOW;
+                               name: "viewer";
+                               type: RECT;
                                mouse_events: 1;
                                description {
                                        state: "default" 0.0;
-                                       rel1 { relative: 1.0 1.0; to_x: "controller"; to_y: "viewer"; }
-                                       rel2 { relative: 1.0 1.0; }
+                                       rel1 { relative: 4/720 1.0; to, "indicator"; }
+                                       rel2 { relative: 716/720 800/1280; }
+                                       color: 255 255 255 255;
                                }
                        }
-               }
-       }
 
-       group {
-               name: "icon,slot";
-               parts {
                        part {
-                               name: "bg";
+                               name: "event,blocker";
                                type: RECT;
                                mouse_events: 1;
                                description {
                                        state: "default" 0.0;
                                        rel1 { relative: 0.0 0.0; }
                                        rel2 { relative: 1.0 1.0; }
-                                       color: 0 0 0 255;
+                                       color: 0 0 0 0;
+                                       visible: 0;
+                               }
+                               description {
+                                       state: "show" 0.0;
+                                       inherit: "default" 0.0;
+                                       color: 50 50 50 50;
+                                       visible: 1;
                                }
                        }
 
@@ -85,51 +88,8 @@ collections {
                                mouse_events: 1;
                                description {
                                        state: "default" 0.0;
-                                       rel1 { relative: 0.0 0.0; }
-                                       rel2 { relative: 1.0 1.0; }
-                                       align: 0.5 1.0;
-                               }
-
-                               description {
-                                       state: "size_172x172" 0.0;
-                                       inherit: "default" 0.0;
-                                       rel1 { relative: ((700-172)/2.0)/700 ((700-172)/2.0)/700; }
-                                       rel2 { relative: ((700+172)/2.0)/700 ((700+172)/2.0)/700; }
-                               }
-
-                               description {
-                                       state: "size_348x172" 0.0;
-                                       inherit: "default" 0.0;
-                                       rel1 { relative: ((700-348)/2.0)/700 ((700-172)/2.0)/700; }
-                                       rel2 { relative: ((700+348)/2.0)/700 ((700+172)/2.0)/700; }
-                               }
-
-                               description {
-                                       state: "size_348x348" 0.0;
-                                       inherit: "default" 0.0;
-                                       rel1 { relative: ((700-348)/2.0)/700 ((700-348)/2.0)/700; }
-                                       rel2 { relative: ((700+348)/2.0)/700 ((700+348)/2.0)/700; }
-                               }
-
-                               description {
-                                       state: "size_700x172" 0.0;
-                                       inherit: "default" 0.0;
-                                       rel1 { relative: 0.0 ((700-172)/2.0)/700; }
-                                       rel2 { relative: 1.0 ((700+172)/2.0)/700; }
-                               }
-
-                               description {
-                                       state: "size_700x348" 0.0;
-                                       inherit: "default" 0.0;
-                                       rel1 { relative: 0.0 ((700-348)/2.0)/700; }
-                                       rel2 { relative: 1.0 ((700+348)/2.0)/700; }
-                               }
-
-                               description {
-                                       state: "size_700x700" 0.0;
-                                       inherit: "default" 0.0;
-                                       rel1 { relative: 0.0 0.0; }
-                                       rel2 { relative: 1.0 1.0; }
+                                       rel1 { relative: 0.4 0.4; to, "viewer"; }
+                                       rel2 { relative: 0.6 0.6; to, "viewer"; }
                                }
                        }
 
@@ -139,91 +99,60 @@ collections {
                                mouse_events: 1;
                                description {
                                        state: "default" 0.0;
-                                       rel1 { relative: 0.0 -1.0; }
-                                       rel2 { relative: 1.0 0.0; }
-                                       align: 0.5 1.0;
+                                       rel1 { relative: 0.0 0.95; to_y, "livebox"; }
+                                       rel2 { relative: 1.0 1.0; to_y, "livebox"; }
+                                       visible: 0;
+                                       align: 0.0 0.0;
                                }
 
                                description {
-                                       state: "open" 0.0;
-                                       rel1 { relative: 0.0 0.0; }
+                                       state: "show" 0.0;
+                                       rel1 { relative: 0.0 1.0; to_y, "livebox"; }
                                        rel2 { relative: 1.0 1.0; }
-                                       align: 0.5 0.0;
+                                       visible: 1;
+                                       align: 0.0 0.0;
                                }
                        }
-
                }
 
                programs {
                        program {
-                               name: "pd,open";
-                               signal: "open";
+                               name: "open,pd";
                                source: "pd";
-                               action: STATE_SET "open" 0.0;
+                               signal: "open";
+                               action: STATE_SET "show" 0.0;
                                target: "pd";
-                               transition: LINEAR 0.3;
+                               target: "event,blocker";
+                               transition: LINEAR 0.2;
                        }
                        program {
-                               name: "pd,close";
-                               signal: "close";
+                               name: "hide,pd";
                                source: "pd";
+                               signal: "close";
                                action: STATE_SET "default" 0.0;
                                target: "pd";
-                               transition: LINEAR 0.3;
-                               after: "pd,closed";
-                       }
-                       program {
-                               name: "pd,closed";
-                               action: SIGNAL_EMIT "closed" "pd";
+                               target: "event,blocker";
+                               transition: LINEAR 0.1;
+                               after: "hide,pd,done";
                        }
 
                        program {
-                               name: "lb,172x172";
-                               signal: "resize,to,172x172";
-                               source: "livebox";
-                               action: STATE_SET "size_172x172" 0.0;
-                               target: "livebox";
-                               transition: LINEAR 0.5;
-                       }
-                       program {
-                               name: "lb,348x172";
-                               signal: "resize,to,348x172";
-                               source: "livebox";
-                               action: STATE_SET "size_348x172" 0.0;
-                               target: "livebox";
-                               transition: LINEAR 0.5;
-                       }
-                       program {
-                               name: "lb,348x348";
-                               signal: "resize,to,348x348";
-                               source: "livebox";
-                               action: STATE_SET "size_348x348" 0.0;
-                               target: "livebox";
-                               transition: LINEAR 0.5;
-                       }
-                       program {
-                               name: "lb,700x172";
-                               signal: "resize,to,700x172";
-                               source: "livebox";
-                               action: STATE_SET "size_700x172" 0.0;
-                               target: "livebox";
-                               transition: LINEAR 0.5;
+                               name: "hide,pd,done";
+                               action: SIGNAL_EMIT "hide,done" "pd";
                        }
+
                        program {
-                               name: "lb,700x348";
-                               signal: "resize,to,700x348";
-                               source: "livebox";
-                               action: STATE_SET "size_700x348" 0.0;
-                               target: "livebox";
-                               transition: LINEAR 0.5;
+                               name: "event,blocker";
+                               source: "event,blocker";
+                               signal: "mouse,clicked,1";
+                               action: SIGNAL_EMIT "close" "pd";
                        }
+
                        program {
-                               name: "lb,700x700";
-                               signal: "resize,to,700x700";
-                               source: "livebox";
-                               action: STATE_SET "size_700x700" 0.0;
-                               target: "livebox";
-                               transition: LINEAR 0.5;
+                               name: "pd,close,pd";
+                               source: "viewer";
+                               signal: "mouse,clicked,1";
+                               action: SIGNAL_EMIT "close" "pd";
                        }
                }
        }
diff --git a/live.viewer/src/dlist.c b/live.viewer/src/dlist.c
new file mode 100644 (file)
index 0000000..5576b1b
--- /dev/null
@@ -0,0 +1,164 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#include "dlist.h"
+
+/*!
+ * \brief
+ * This dlist is called Modified Doubly Linked List.
+ *
+ * Noramlly, The dobule linked list contains address of previous and next element.
+ * This dlist also contains them, but the tail element only contains prev address.
+ *
+ * The head element's prev pointer indicates the last element.
+ * But the last element's next pointer indicates NIL.
+ *
+ * So we can find the last element while crawling this DList
+ * But we have to remember the address of the head element.
+ */
+
+struct dlist {
+       struct dlist *next;
+       struct dlist *prev;
+       void *data;
+};
+
+struct dlist *dlist_append(struct dlist *list, void *data)
+{
+       struct dlist *item;
+
+       item = malloc(sizeof(*item));
+       if (!item)
+               return NULL;
+
+       item->next = NULL;
+       item->data = data;
+
+       if (!list) {
+               item->prev = item;
+
+               list = item;
+       } else {
+               item->prev = list->prev;
+               item->prev->next = item;
+               list->prev = item;
+       }
+
+       assert(!list->prev->next && "item NEXT");
+
+       return list;
+}
+
+struct dlist *dlist_prepend(struct dlist *list, void *data)
+{
+       struct dlist *item;
+
+       item = malloc(sizeof(*item));
+       if (!item)
+               return NULL;
+
+       item->data = data;
+
+       if (!list) {
+               item->prev = item;
+               item->next = NULL;
+       } else {
+               if (list->prev->next)
+                       list->prev->next = item;
+
+               item->prev = list->prev;
+               item->next = list;
+
+               list->prev = item;
+
+       }
+
+       return item;
+}
+
+struct dlist *dlist_remove(struct dlist *list, struct dlist *l)
+{
+       if (!list || !l)
+               return NULL;
+
+       if (l == list)
+               list = l->next;
+       else
+               l->prev->next = l->next;
+
+       if (l->next)
+               l->next->prev = l->prev;
+       /*!
+        * \note
+        * If the removed entry 'l' has no next element, it is the last element.
+        * In this case, check the existence of the list first,
+        * and if the list is not empty, update the 'prev' of the list (which is a head element of the list) 
+        *
+        * If we didn't care about this, the head element(list) can indicates the invalid element.
+        */
+       else if (list)
+               list->prev = l->prev;
+
+       free(l);
+       return list;
+}
+
+struct dlist *dlist_find_data(struct dlist *list, void *data)
+{
+       struct dlist *l;
+       void *_data;
+
+       dlist_foreach(list, l, _data) {
+               if (data == _data)
+                       return l;
+       }
+
+       return NULL;
+}
+
+void *dlist_data(struct dlist *l)
+{
+       return l ? l->data : NULL;
+}
+
+struct dlist *dlist_next(struct dlist *l)
+{
+       return l ? l->next : NULL;
+}
+
+struct dlist *dlist_prev(struct dlist *l)
+{
+       return l ? l->prev : NULL;
+}
+
+int dlist_count(struct dlist *l)
+{
+       register int i;
+       struct dlist *n;
+       void *data;
+
+       i = 0;
+       dlist_foreach(l, n, data) {
+               i++;
+       }
+
+       return i;
+}
+
+struct dlist *dlist_nth(struct dlist *l, int nth)
+{
+       register int i;
+       struct dlist *n;
+
+       i = 0;
+       for (n = l; n; n = n->next) {
+               if (i == nth)
+                       return n;
+               i++;
+       }
+
+       return NULL;
+}
+
+/* End of a file */
diff --git a/live.viewer/src/lb.c b/live.viewer/src/lb.c
new file mode 100644 (file)
index 0000000..4701ec9
--- /dev/null
@@ -0,0 +1,666 @@
+#include <Elementary.h>
+#include <Ecore_X.h>
+
+#include <dlog.h>
+
+#include <livebox.h>
+#include <livebox-service.h>
+
+#include "main.h"
+#include "util.h"
+#include "debug.h"
+#include "lb.h"
+#include "scroller.h"
+
+#define FLICK_COND     100
+
+static Evas_Object *create_canvas(Evas_Object *parent)
+{
+       Evas_Object *canvas;
+
+       canvas = evas_object_image_filled_add(evas_object_evas_get(parent));
+       if (!canvas)
+               return NULL;
+
+       evas_object_image_colorspace_set(canvas, EVAS_COLORSPACE_ARGB8888);
+       evas_object_image_alpha_set(canvas, EINA_TRUE);
+       evas_object_move(canvas, 0, 0);
+       return canvas;
+}
+
+static int update_pd_canvas(struct livebox *handle, Evas_Object *image)
+{
+       int w;
+       int h;
+
+       switch (livebox_pd_type(handle)) {
+       case PD_TYPE_BUFFER:
+       case PD_TYPE_PIXMAP:
+               evas_object_image_colorspace_set(image, EVAS_COLORSPACE_ARGB8888);
+               evas_object_image_alpha_set(image, EINA_TRUE);
+
+               livebox_get_pdsize(handle, &w, &h);
+               if (w > 0 && h > 0) {
+                       void *data;
+                       data = livebox_acquire_pdfb(handle);
+                       if (data) {
+                               evas_object_image_size_set(image, w, h);
+                               evas_object_image_data_copy_set(image, data);
+                               livebox_release_pdfb(data);
+                       }
+                       evas_object_resize(image, w, h);
+                       //evas_object_size_hint_min_set(image, w, h);
+                       evas_object_size_hint_max_set(image, w, h);
+               }
+               break;
+       case PD_TYPE_TEXT:
+       default:
+               break;
+       }
+
+       return 0;
+}
+
+static int update_canvas(struct livebox *handle, Evas_Object *image)
+{
+       const char *filename;
+       int w;
+       int h;
+       int type;
+
+       switch (livebox_lb_type(handle)) {
+       case LB_TYPE_BUFFER:
+       case LB_TYPE_PIXMAP:
+               evas_object_image_colorspace_set(image, EVAS_COLORSPACE_ARGB8888);
+               evas_object_image_alpha_set(image, EINA_TRUE);
+
+               w = h = 0;
+               type = livebox_size(handle);
+               livebox_service_get_size(type, &w, &h);
+               if (w > 0 && h > 0) {
+                       void *data;
+                       data = livebox_acquire_fb(handle);
+                       if (data) {
+                               evas_object_image_size_set(image, w, h);
+                               evas_object_image_data_copy_set(image, data);
+                               livebox_release_fb(data);
+                       }
+                       evas_object_resize(image, w, h);
+                       evas_object_size_hint_min_set(image, w, h);
+                       evas_object_size_hint_max_set(image, w, h);
+               }
+               break;
+       case LB_TYPE_IMAGE:
+               filename = livebox_filename(handle);
+               if (filename) {
+                       const char *old;
+                       evas_object_image_file_get(image, &old, NULL);
+                       if (old && !strcmp(filename, old)) {
+                               evas_object_image_reload(image);
+                       } else {
+                               evas_object_image_file_set(image, filename, NULL);
+                       }
+
+                       w = h = 0;
+                       type = livebox_size(handle);
+                       livebox_service_get_size(type, &w, &h);
+                       if (w > 0 && h > 0) {
+                               evas_object_resize(image, w, h);
+                               evas_object_size_hint_min_set(image, w, h);
+                               evas_object_size_hint_max_set(image, w, h);
+                       }
+               }
+               break;
+       case LB_TYPE_TEXT:
+       default:
+               break;
+       }
+
+       return 0;
+}
+
+static inline void prepend_log(struct livebox *handle, const char *buffer)
+{
+       Evas_Object *layout;
+       Evas_Object *logger;
+
+       layout = livebox_get_data(handle);
+       if (!layout) {
+               ErrPrint("Failed to get layout\n");
+               return;
+       }
+
+       logger = elm_object_part_content_get(layout, "logger");
+       if (logger)
+               elm_list_item_prepend(logger, buffer, NULL, NULL, NULL, NULL);
+}
+
+static int lb_event_cb(struct livebox *handle, enum livebox_event_type event, void *data)
+{
+       Evas_Object *layout;
+       Evas_Object *sc;
+       Evas_Object *pd;
+       Evas_Object *image;
+
+       layout = (Evas_Object *)livebox_get_data(handle);
+       if (!layout)
+               return -EFAULT;
+
+       switch (event) {
+       case LB_EVENT_LB_UPDATED:
+               DbgPrint("Contents: [%s]\n", livebox_content(handle));
+               image = elm_object_part_content_get(layout, "livebox");
+               if (image)
+                       update_canvas(handle, image);
+               break;
+       case LB_EVENT_PD_UPDATED:
+               image = elm_object_part_content_get(layout, "pd");
+               if (image)
+                       update_pd_canvas(handle, image);
+               break;
+       case LB_EVENT_DELETED:
+               sc = evas_object_data_del(layout, "sc");
+               if (sc)
+                       scroller_peek_by_obj(sc, layout);
+
+               evas_object_del(layout);
+               break;
+       case LB_EVENT_PD_DESTROYED:
+               pd = elm_object_part_content_unset(layout, "pd");
+               if (pd)
+                       evas_object_del(pd);
+
+               sc = evas_object_data_del(layout, "sc");
+               if (sc)
+                       scroller_unlock(sc);
+               break;
+       default:
+               break;
+       }
+
+       return 0;
+}
+
+static int lb_fault_cb(enum livebox_fault_type event,
+                                       const char *pkgname, const char *filename,
+                                       const char *funcname, void *data)
+{
+       DbgPrint("pkgname: %s\nfilename: %s\n:funcname: %s\n", pkgname, filename, funcname);
+       return 0;
+}
+
+static void del_cb(struct livebox *handle, int ret, void *data)
+{
+       Evas_Object *layout;
+       Evas_Object *sc;
+
+       layout = livebox_get_data(handle);
+       if (!layout) {
+               ErrPrint("Failed to get layout\n");
+               return;
+       }
+
+       sc = evas_object_data_del(layout, "sc");
+       if (sc) {
+               DbgPrint("Scroller: %p\n", sc);
+               scroller_peek_by_obj(sc, layout);
+       }
+
+       DbgPrint("Delete a layout object\n");
+       evas_object_del(layout);
+}
+
+static void delete_btn_cb(void *handle, Evas_Object *obj, void *event_info)
+{
+       int ret;
+       ret = livebox_del(handle, del_cb, NULL);
+       if (ret < 0) {
+               char buffer[256];
+               snprintf(buffer, sizeof(buffer), "Delete returns: %d\n", ret);
+               prepend_log(handle, buffer);
+       }
+}
+
+static void exit_cb(void *data, Evas_Object *obj, void *event_info)
+{
+       evas_object_del(obj);
+}
+
+static void error_popup(Evas_Object *parent, struct livebox *handle, int ret)
+{
+       Evas_Object *popup;
+       Evas_Object *button;
+       char buffer[256];
+
+       popup = elm_popup_add(parent);
+       if (!popup)
+               return;
+
+       button = elm_button_add(parent);
+       if (!button) {
+               evas_object_del(popup);
+               return;
+       }
+
+       elm_popup_orient_set(popup, ELM_POPUP_ORIENT_CENTER);
+       elm_object_part_text_set(popup, "title,text", "Unable to load a livebox");
+
+       elm_object_text_set(button, "Okay");
+       elm_object_part_content_set(popup, "button2", button);
+       evas_object_smart_callback_add(button, "clicked", exit_cb, popup);
+
+       snprintf(buffer, sizeof(buffer) - 1,
+                       "%s(%s): %d", livebox_pkgname(handle), livebox_content(handle), ret);
+       elm_object_part_text_set(popup, "default", buffer);
+       evas_object_show(popup);
+
+       return;
+}
+
+static void resize_cb(struct livebox *handle, int ret, void *data)
+{
+       Evas_Object *layout;
+       Evas_Object *log_list;
+       char buffer[256];
+
+       layout = livebox_get_data(handle);
+       if (!layout)
+               return;
+
+       log_list = elm_object_part_content_get(layout, "logger");
+       if (!log_list)
+               return;
+
+       snprintf(buffer, sizeof(buffer) - 1, "Resize: %d", ret);
+       elm_list_item_prepend(log_list, buffer, NULL, NULL, NULL, NULL);
+}
+
+static void resize_click_cb(void *handle, Evas_Object *obj, void *event_info)
+{
+       Elm_Object_Item *item;
+       const char *label;
+       int w;
+       int h;
+       int size_type;
+
+       item = elm_list_selected_item_get(obj);
+       if (!item)
+               return;
+
+       label = elm_object_item_part_text_get(item, NULL);
+       if (!label)
+               return;
+
+       sscanf(label, "%dx%d", &w, &h);
+       size_type = livebox_service_size_type(w, h);
+
+       livebox_resize(handle, size_type, resize_cb, NULL);
+}
+
+static void create_resize_controller(struct livebox *handle, Evas_Object *layout)
+{
+       Evas_Object *size_list;
+       char buffer[256];
+       int sizes[7];
+       int cnt;
+       int i;
+       int w;
+       int h;
+
+       size_list = elm_list_add(layout);
+       cnt = 7;
+       livebox_get_supported_sizes(handle, &cnt, sizes);
+       for (i = 0; i < cnt; i++) {
+               livebox_service_get_size(sizes[i], &w, &h);
+               snprintf(buffer, sizeof(buffer) - 1, "%dx%d", w, h);
+
+               elm_list_item_append(size_list, buffer, NULL, NULL, resize_click_cb, handle);
+       }
+       evas_object_show(size_list);
+       elm_list_go(size_list);
+       evas_object_size_hint_weight_set(size_list, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
+       evas_object_size_hint_align_set(size_list, EVAS_HINT_FILL, EVAS_HINT_FILL);
+       elm_object_part_content_set(layout, "controller", size_list);
+}
+
+static void create_logger(struct livebox *handle, Evas_Object *layout)
+{
+       Evas_Object *log_list;
+
+       log_list = elm_list_add(layout);
+       if (!log_list)
+               return;
+
+       elm_list_item_prepend(log_list, "Created", NULL, NULL, NULL, NULL);
+       evas_object_show(log_list);
+       elm_list_go(log_list);
+
+       evas_object_size_hint_weight_set(log_list, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
+       evas_object_size_hint_align_set(log_list, EVAS_HINT_FILL, EVAS_HINT_FILL);
+       elm_object_part_content_set(layout, "logger", log_list);
+}
+
+struct event_data {
+       Evas_Coord x;
+       Evas_Coord y;
+
+       enum flick {
+               FLICK_UNKNOWN = 0x00,
+               FLICK_DOWN = 0x01,
+               FLICK_UP = 0x02,
+       } flick;
+};
+
+static void pd_closed_cb(struct livebox *handle, int ret, void *data)
+{
+       evas_object_del(data);
+}
+
+static void pd_hide_done_cb(void *data, Evas_Object *obj, const char *emission, const char *source) 
+{
+       Evas_Object *pd;
+       Evas_Object *sc;
+
+       sc = evas_object_data_get(obj, "sc");
+       scroller_unlock(sc);
+
+       elm_object_signal_callback_del(obj, emission, source, pd_hide_done_cb);
+       pd = elm_object_part_content_unset(obj, "pd");
+       livebox_destroy_pd(data, pd_closed_cb, pd);
+}
+
+static void pd_mouse_up_cb(void *handle, Evas *e, Evas_Object *obj, void *event_info)
+{
+       Evas_Event_Mouse_Up *up = event_info;
+       Evas_Coord x, y, w, h;
+       double rx, ry;
+
+       evas_object_geometry_get(obj, &x, &y, &w, &h);
+
+       rx = (double)(up->canvas.x - x) / (double)w;
+       ry = (double)(up->canvas.y - y) / (double)h;
+       livebox_content_event(handle, PD_MOUSE_UP, rx, ry);
+}
+
+static void pd_mouse_down_cb(void *handle, Evas *e, Evas_Object *obj, void *event_info)
+{
+       Evas_Event_Mouse_Down *down = event_info;
+       Evas_Coord x, y, w, h;
+       double rx, ry;
+
+       evas_object_geometry_get(obj, &x, &y, &w, &h);
+       rx = (double)(down->canvas.x - x) / (double)w;
+       ry = (double)(down->canvas.y - y) / (double)h;
+       livebox_content_event(handle, PD_MOUSE_DOWN, rx, ry);
+}
+
+static void pd_mouse_move_cb(void *handle, Evas *e, Evas_Object *obj, void *event_info)
+{
+       Evas_Event_Mouse_Move *move = event_info;
+       Evas_Coord x, y, w, h;
+       double rx, ry;
+
+       evas_object_geometry_get(obj, &x, &y, &w, &h);
+       rx = (double)(move->cur.canvas.x - x) / (double)w;
+       ry = (double)(move->cur.canvas.y - y) / (double)h;
+       livebox_content_event(handle, PD_MOUSE_MOVE, rx, ry);
+}
+
+static void pd_created_cb(struct livebox *handle, int ret, void *data)
+{
+       Evas_Object *layout;
+       Evas_Object *pd_image;
+       Evas_Object *sc;
+
+       layout = (Evas_Object *)livebox_get_data(handle);
+       if (!layout)
+               return;
+
+       sc = evas_object_data_get(layout, "sc");
+
+       pd_image = create_canvas(layout);
+       if (!pd_image)
+               return;
+
+       evas_object_event_callback_add(pd_image, EVAS_CALLBACK_MOUSE_UP, pd_mouse_up_cb, handle);
+       evas_object_event_callback_add(pd_image, EVAS_CALLBACK_MOUSE_DOWN, pd_mouse_down_cb, handle);
+       evas_object_event_callback_add(pd_image, EVAS_CALLBACK_MOUSE_MOVE, pd_mouse_move_cb, handle);
+
+       update_pd_canvas(handle, pd_image);
+
+       elm_object_signal_callback_add(layout, "hide,done", "pd", pd_hide_done_cb, handle);
+       elm_object_part_content_set(layout, "pd", pd_image);
+       elm_object_signal_emit(layout, "open", "pd");
+       scroller_lock(sc);
+}
+
+static void lb_mouse_up_cb(void *handle, Evas *e, Evas_Object *obj, void *event_info)
+{
+       Evas_Event_Mouse_Up *up = event_info;
+       struct event_data *evt;
+
+       evt = evas_object_data_del(obj, "evt");
+       if (!evt)
+               return;
+
+       if (livebox_lb_type(handle) == LB_TYPE_PIXMAP || livebox_lb_type(handle) == LB_TYPE_BUFFER) {
+               Evas_Coord x, y, w, h;
+               double rx, ry;
+
+               evas_object_geometry_get(obj, &x, &y, &w, &h);
+               rx = (double)(up->canvas.x - x) / (double)w;
+               ry = (double)(up->canvas.y - y) / (double)h;
+               livebox_content_event(handle, LB_MOUSE_UP, rx, ry);
+       } else {
+               Evas_Coord x, y, w, h;
+               evas_object_geometry_get(obj, &x, &y, &w, &h);
+
+               if (x < up->canvas.x && up->canvas.x < x + w) {
+                       if (y < up->canvas.y && up->canvas.y < y + h) {
+                               livebox_click(handle, (double)x / (double)w, (double)y / (double)h);
+                       }
+               }
+       }
+
+       if (evt->flick == FLICK_DOWN && (up->canvas.y - evt->y) > (FLICK_COND>>1)) {
+               int ret;
+               /* Open PD */
+               ret = livebox_create_pd_with_position(handle, 0.5, 0.0, pd_created_cb, NULL);
+       }
+
+       free(evt);
+}
+
+static void lb_mouse_down_cb(void *handle, Evas *e, Evas_Object *obj, void *event_info)
+{
+       struct event_data *evt;
+       Evas_Event_Mouse_Down *down = event_info;
+       Evas_Object *layout;
+       Evas_Object *sc;
+
+       layout = livebox_get_data(handle);
+       if (!layout)
+               return;
+
+       sc = evas_object_data_get(layout, "sc");
+       if (!sc)
+               return;
+
+       if (scroller_is_scrolling(sc))
+               return;
+
+       if (livebox_lb_type(handle) == LB_TYPE_PIXMAP || livebox_lb_type(handle) == LB_TYPE_BUFFER) {
+               Evas_Coord x, y, w, h;
+               double rx, ry;
+
+               evas_object_geometry_get(obj, &x, &y, &w, &h);
+               rx = (double)(down->canvas.x - x) / (double)w;
+               ry = (double)(down->canvas.y - y) / (double)h;
+               livebox_content_event(handle, LB_MOUSE_DOWN, rx, ry);
+       }
+
+       evt = evas_object_data_get(obj, "evt");
+       if (evt) {
+               ErrPrint("Huh?");
+       } else {
+               evt = malloc(sizeof(*evt));
+               if (!evt) {
+                       ErrPrint("Heap: %s\n", strerror(errno));
+                       return;
+               }
+       }
+
+       evas_object_data_set(obj, "evt", evt);
+
+       evt->x = down->canvas.x;
+       evt->y = down->canvas.y;
+       evt->flick = FLICK_UNKNOWN;
+}
+
+static void lb_mouse_move_cb(void *handle, Evas *e, Evas_Object *obj, void *event_info)
+{
+       Evas_Event_Mouse_Move *move = event_info;
+       struct event_data *evt;
+
+       evt = evas_object_data_get(obj, "evt");
+       if (!evt)
+               return;
+
+       if (livebox_lb_type(handle) == LB_TYPE_PIXMAP || livebox_lb_type(handle) == LB_TYPE_BUFFER) {
+               Evas_Coord x, y, w, h;
+               double rx, ry;
+
+               evas_object_geometry_get(obj, &x, &y, &w, &h);
+               rx = (double)(move->cur.canvas.x - x) / (double)w;
+               ry = (double)(move->cur.canvas.y - y) / (double)h;
+               livebox_content_event(handle, LB_MOUSE_MOVE, rx, ry);
+       }
+
+       if ((move->cur.canvas.x - move->prev.canvas.x) > FLICK_COND) {
+               evt->flick = FLICK_UNKNOWN;
+               return;
+       } else if ((move->cur.canvas.x - move->prev.canvas.x) < -FLICK_COND) {
+               evt->flick = FLICK_UNKNOWN;
+               return;
+       }
+
+       if ((move->cur.canvas.y - move->prev.canvas.y) > 0) {
+               if (evt->flick != FLICK_DOWN)
+                       evt->flick = FLICK_DOWN;
+       } else if ((move->cur.canvas.y - move->prev.canvas.y) < 0) {
+               if (evt->flick != FLICK_UP)
+                       evt->flick = FLICK_UP;
+       }
+}
+
+static void livebox_added_cb(struct livebox *handle, int ret, void *data)
+{
+       Evas_Object *layout;
+       Evas_Object *lb_image;
+       Evas_Object *btn;
+       int w;
+       int h;
+       int type;
+       int idx;
+
+       DbgPrint("%s - %d\n", livebox_pkgname(handle), ret);
+
+       if (ret != 0) {
+               error_popup(data, handle, ret);
+               return;
+       }
+
+       layout = elm_layout_add(main_get_window());
+       if (!layout) {
+               ErrPrint("Failed to add a layout\n");
+               return;
+       }
+
+       if (elm_layout_file_set(layout, PKGROOT "/res/edje/live-viewer.edj", "layout") == EINA_FALSE) {
+               DbgPrint("Failed to add a layout\n");
+               evas_object_del(layout);
+               return;
+       }
+
+       lb_image = create_canvas(layout);
+       if (!lb_image) {
+               ErrPrint("Failed to create a canvas\n");
+               evas_object_del(layout);
+               return;
+       }
+
+       evas_object_event_callback_add(lb_image, EVAS_CALLBACK_MOUSE_UP, lb_mouse_up_cb, handle);
+       evas_object_event_callback_add(lb_image, EVAS_CALLBACK_MOUSE_DOWN, lb_mouse_down_cb, handle);
+       evas_object_event_callback_add(lb_image, EVAS_CALLBACK_MOUSE_MOVE, lb_mouse_move_cb, handle);
+
+       w = 0;
+       h = 0;
+       type = livebox_size(handle);
+       if (type != LB_SIZE_TYPE_UNKNOWN) {
+               livebox_service_get_size(type, &w, &h);
+               DbgPrint("%dx%d\n", w, h);
+       }
+       evas_object_resize(lb_image, w, h);
+       evas_object_show(lb_image);
+
+       update_canvas(handle, lb_image);
+
+       btn = elm_button_add(main_get_window());
+       if (btn) {
+               elm_object_text_set(btn, "Delete");
+               evas_object_smart_callback_add(btn, "clicked", delete_btn_cb, handle);
+               elm_object_part_content_set(layout, "delete,btn", btn);
+       }
+
+       elm_object_part_content_set(layout, "livebox", lb_image);
+       evas_object_resize(layout, 720, 1280);
+       evas_object_show(layout);
+
+       create_resize_controller(handle, layout);
+       create_logger(handle, layout);
+
+       livebox_set_data(handle, layout);
+       evas_object_data_set(layout, "sc", data);
+
+       scroller_append(data, layout);
+
+       idx = scroller_get_page_index(data, layout);
+       DbgPrint("Scroll to %d\n", idx);
+       scroller_scroll_to(data, idx);
+}
+
+int lb_add(Evas_Object *sc, const char *pkgname)
+{
+       int w, h;
+       struct livebox *handle;
+
+       evas_object_geometry_get(sc, NULL, NULL, &w, &h);
+
+       DbgPrint("sc: %dx%d, package: %s\n", w, h, pkgname);
+       livebox_activate(pkgname, NULL, NULL);
+
+       handle = livebox_add(pkgname, "default", "user,created", "default",
+                                               DEFAULT_PERIOD, livebox_added_cb, sc);
+       if (!handle) {
+               ErrPrint("Failed to add a new livebox\n");
+               return -EFAULT;
+       }
+
+       return 0;
+}
+
+int lb_init(void)
+{
+       livebox_init(ecore_x_display_get());
+       livebox_set_event_handler(lb_event_cb, NULL);
+       livebox_set_fault_handler(lb_fault_cb, NULL);
+       return 0;
+}
+
+int lb_fini(void)
+{
+       livebox_fini();
+       return 0;
+}
+
+/* End of a file */
diff --git a/live.viewer/src/live_scroller.c b/live.viewer/src/live_scroller.c
new file mode 100644 (file)
index 0000000..c55865f
--- /dev/null
@@ -0,0 +1,1333 @@
+#include <Elementary.h>
+
+#include <dlog.h>
+
+#include "live_scroller.h"
+#include "util.h"
+#include "debug.h"
+
+#define SAMPLE_MAX 10
+#define EVT_PERIOD 0.016 /* 60 fps */
+#define EVT_SAMPLE_PERIOD 9
+#define DRAG_SENS 100
+#define ANIM_MIN 40
+#define ANIM_UNIT 15
+
+struct item_list_entry {
+       struct item_list_entry *prev;
+       struct item_list_entry *next;
+
+       Evas_Object *data;
+       Evas_Coord x;
+       Evas_Coord y;
+       Evas_Coord w;
+       Evas_Coord h;
+};
+
+struct evt_info {
+       Evas_Coord x;
+       unsigned int timestamp;
+};
+
+struct evt_queue {
+       struct evt_info ei[SAMPLE_MAX];
+       int front;
+       int rear;
+       int cnt;
+       unsigned int old_timestamp;
+};
+
+struct widget_data {
+       Eina_Bool is_loop;
+       Eina_Bool is_freezed;
+
+       struct item_list_entry *item_list;
+
+       int item_cnt;
+       struct item_list_entry *curlist;
+       struct item_list_entry *tolist;
+
+       Eina_Bool drag_started;
+       Eina_Bool is_pressed;
+       Evas_Coord press_x;
+       Evas_Coord press_y;
+
+       Ecore_Timer *sc_anim_timer;
+       int sc_anim_dx;
+
+       Evas_Object *clip;
+       Evas_Object *evt_layer;
+       Evas_Object *scroller;
+
+       Evas_Coord clip_bx;
+       Evas_Coord clip_bw;
+
+       struct evt_queue evtq;
+       Ecore_Timer *evt_emulator;
+       Evas_Coord old_x;
+       unsigned int prev_timestamp;
+};
+
+#ifdef PROFILE
+#define PROFILE_START() \
+static int _exec_cnt = 0; \
+struct timeval _stv, _etv; \
+long _elapsed; \
+gettimeofday(&_stv, NULL);
+
+#define PROFILE_END() \
+do { \
+       _exec_cnt++; \
+       gettimeofday(&_etv, NULL); \
+       _elapsed = (_etv.tv_sec - _stv.tv_sec) * 1000000l + (_etv.tv_usec - _stv.tv_usec); \
+       DbgPrint("[%d] Elapsed time: %lu\n", _exec_cnt, _elapsed); \
+} while (0)
+#else
+#define PROFILE_START()
+#define PROFILE_END()
+#endif
+
+#define LIST_NEXT(list)        ((list)->next)
+#define LIST_PREV(list)        ((list)->prev)
+#define LIST_DATA(list)        ((list) ? (list)->data : NULL)
+
+static inline void LIST_ITEM_GEO_SET(struct item_list_entry *list, int x, int y, int w, int h)
+{
+       list->x = x;
+       list->y = y;
+       list->w = w;
+       list->h = h;
+}
+
+static inline void LIST_ITEM_GEO_GET(struct item_list_entry *list, int *x, int *y, int *w, int *h)
+{
+       if (x)
+               *x = list->x;
+       if (y)
+               *y = list->y;
+       if (w)
+               *w = list->w;
+       if (h)
+               *h = list->h;
+}
+
+static inline struct item_list_entry *list_item_append(struct item_list_entry *list, void *obj)
+{
+       struct item_list_entry *item;
+
+       item = malloc(sizeof(*item));
+       if (!item)
+               return NULL;
+
+       item->data = obj;
+
+       if (list) {
+               list->prev->next = item;
+               item->prev = list->prev;
+
+               item->next = list;
+               list->prev = item;
+       } else{
+               item->prev = item;
+               item->next = item;
+               list = item;
+       }
+
+       return list;
+}
+
+static inline void *list_item_nth(struct item_list_entry *list, int idx)
+{
+       if (!list)
+               return NULL;
+
+       while (--idx >= 0)
+               list = list->next;
+
+       return list->data;
+}
+
+static inline struct item_list_entry *list_item_nth_list(struct item_list_entry *list, int idx)
+{
+       if (!list)
+               return NULL;
+
+       while (--idx >= 0)
+               list = list->next;
+
+       return list;
+}
+
+static inline struct item_list_entry *list_item_find(struct item_list_entry *list, void *data)
+{
+       struct item_list_entry *item;
+
+       if (!list)
+               return NULL;
+
+       item = list;
+       do {
+               if (LIST_DATA(item) == data)
+                       return item;
+
+               item = LIST_NEXT(item);
+       } while (item != list);
+
+       return NULL;
+}
+
+static inline struct item_list_entry *list_item_remove(struct item_list_entry *list, void *data)
+{
+       struct item_list_entry *item;
+
+       if (!list)
+               return NULL;
+
+       item = list;
+       do {
+               if (LIST_DATA(item) == data) {
+                       if (item == list) {
+                               if (list == list->next)
+                                       list = NULL;
+                               else
+                                       list = list->next;
+                       }
+
+                       item->prev->next = item->next;
+                       item->next->prev = item->prev;
+                       free(item);
+                       break;
+               }
+       } while (item != list);
+
+       return list;
+}
+
+static inline void *list_item_last(struct item_list_entry *list)
+{
+       if (!list)
+               return NULL;
+
+       return list->prev->data;
+}
+
+static inline struct item_list_entry *list_item_last_list(struct item_list_entry *list)
+{
+       if (!list)
+               return NULL;
+
+       return list->prev;
+}
+
+static inline int list_item_count(struct item_list_entry *list)
+{
+       struct item_list_entry *n;
+       int cnt;
+
+       if (!list)
+               return 0;
+
+       cnt = 0;
+       n = list;
+       do {
+               cnt++;
+               n = LIST_NEXT(n);
+       } while (n != list);
+
+       return cnt;
+}
+
+static inline int list_item_idx(struct widget_data *sc_data, struct item_list_entry *ilist)
+{
+       int idx;
+       idx = 0;
+       while (ilist != sc_data->item_list) {
+               idx++;
+               ilist = LIST_PREV(ilist);
+       }
+
+       return idx;
+}
+
+static inline void init_evtq(struct evt_queue *evtq)
+{
+       evtq->front = 0;
+       evtq->rear = 0;
+       evtq->cnt = 0;
+       evtq->old_timestamp = 0;
+}
+
+static inline void dq_evt(struct evt_queue *evtq)
+{
+       if (evtq->cnt <= 0)
+               return;
+       evtq->front++;
+       if (evtq->front >= SAMPLE_MAX)
+               evtq->front -= SAMPLE_MAX;
+       evtq->cnt--;
+}
+
+static inline void enq_evt(struct evt_queue *evtq, Evas_Coord x, unsigned int timestamp)
+{
+       unsigned int t_diff;
+       int replace;
+
+       replace = 0;
+       t_diff = timestamp - evtq->old_timestamp;
+
+       if (evtq->cnt <= 0)
+               evtq->old_timestamp = timestamp;
+       else if (t_diff > EVT_SAMPLE_PERIOD)
+               evtq->old_timestamp += EVT_SAMPLE_PERIOD * (t_diff / EVT_SAMPLE_PERIOD);
+       else
+               replace = 1;
+
+       if (!replace) {
+               if (evtq->cnt >= SAMPLE_MAX)
+                       dq_evt(evtq);
+               evtq->rear++;
+               if (evtq->rear >= SAMPLE_MAX)
+                       evtq->rear -= SAMPLE_MAX;
+               evtq->cnt++;
+       }
+
+       evtq->ei[evtq->rear].x = x;
+       evtq->ei[evtq->rear].timestamp = evtq->old_timestamp;
+}
+
+static inline Evas_Coord get_evt_avg(struct evt_queue *evtq)
+{
+       int i;
+       int rear;
+       Evas_Coord x;
+       int weight;
+       int t;
+
+       t = (int)(ecore_time_get() * 1000);
+       rear = evtq->rear;
+
+       x = 0;
+       for (i = 0; i < evtq->cnt; i += weight) {
+               weight = (t - evtq->ei[rear].timestamp) / EVT_SAMPLE_PERIOD;
+               if (weight > (evtq->cnt - i))
+                       weight = evtq->cnt - i;
+               else
+                       weight = 1;
+
+               x += evtq->ei[rear].x * weight;
+               t = evtq->ei[rear].timestamp;
+               rear--;
+               if (rear < 0)
+                       rear += SAMPLE_MAX;
+       }
+
+       x /= evtq->cnt;
+       return x;
+}
+
+/* Move the item to given direction to fit its coordinates to border */
+static inline int calc_anim_dx_with_dir(struct widget_data *sc_data, int *dir)
+{
+       Evas_Coord x, w;
+
+       LIST_ITEM_GEO_GET(sc_data->curlist, &x, NULL, &w, NULL);
+       sc_data->sc_anim_dx = 0;
+
+       if (*dir < 0) {
+               /* MOVE to LEFT */
+               if (x < sc_data->clip_bx) {
+                       (*dir)++;
+
+                       if (sc_data->tolist == sc_data->item_list) {
+                               if (!sc_data->is_loop) {
+                                       *dir = 0;
+                                       return -EINVAL;
+                               }
+                       }
+
+                       sc_data->tolist = LIST_PREV(sc_data->tolist);
+                       sc_data->sc_anim_dx = sc_data->clip_bx - x /*- w*/;
+               } else {
+                       sc_data->sc_anim_dx = sc_data->clip_bx - x;
+               }
+       } else if (*dir > 0) {
+               /* MOVE to RIGHT */
+               if (x < sc_data->clip_bx) {
+                       sc_data->sc_anim_dx = sc_data->clip_bx - x;
+               } else if (x > sc_data->clip_bx) {
+                       struct item_list_entry *newlist;
+
+                       (*dir)--;
+                       newlist = LIST_NEXT(sc_data->tolist);
+                       if (newlist == sc_data->item_list) {
+                               if (!sc_data->is_loop) {
+                                       *dir = 0;
+                                       return -EINVAL;
+                               }
+                       }
+                       sc_data->tolist = newlist;
+                       sc_data->sc_anim_dx = sc_data->clip_bx - x; /*(sc_data->clip_bx + sc_data->clip_bw) - x;*/
+               }
+       }
+
+       return 0;
+}
+
+static inline void move_item(struct widget_data *sc_data, struct item_list_entry *ilist, int x, int y, int w, int h)
+{
+       struct live_sc_move_info info;
+
+       info.item = LIST_DATA(ilist);
+
+       info.relx = ((double)x - (double)sc_data->clip_bx) / (double)sc_data->clip_bw;
+
+       LIST_ITEM_GEO_SET(ilist, x, y, w, h);
+       info.x = x;
+       info.y = y;
+       info.w = w;
+       info.h = h;
+
+       evas_object_smart_callback_call(sc_data->scroller, "item,moved", &info);
+}
+
+static struct item_list_entry *update_items_geo(struct widget_data *sc_data, int dx)
+{
+       Evas_Coord sx, sw;
+       Evas_Coord y, w, h;
+       struct item_list_entry *ilist;
+       struct item_list_entry *newlist;
+       struct item_list_entry *boundary;
+       int bx_bw;
+       register int x;
+
+       LIST_ITEM_GEO_GET(sc_data->curlist, &sx, &y, &sw, &h);
+
+       bx_bw = sc_data->clip_bx + sc_data->clip_bw;
+
+       sx += dx;
+       move_item(sc_data, sc_data->curlist, sx, y, sw, h);
+
+       newlist = NULL;
+
+       if (sc_data->item_cnt < 3) {
+               ilist = LIST_NEXT(sc_data->curlist);
+               LIST_ITEM_GEO_GET(ilist, NULL, &y, &w, &h);
+
+               if (sx + sw < bx_bw) {
+                       x = sx + sw;
+                       move_item(sc_data, ilist, x, y, w, h);
+                       if (x == sc_data->clip_bx || (x < sc_data->clip_bx && (x + w) > sc_data->clip_bx))
+                               newlist = ilist;
+               } else if (sx > 0) {
+                       x = sx - w;
+                       move_item(sc_data, ilist, x, y, w, h);
+                       if (x == sc_data->clip_bx || (x > sc_data->clip_bx && x < bx_bw)) {
+                               newlist = ilist;
+                       }
+               }
+
+               goto out;
+       }
+
+       x = sx;
+       boundary = NULL;
+       ilist = sc_data->curlist;
+       do {
+               if (!sc_data->is_loop && ilist == sc_data->item_list)
+                       break;
+
+               ilist = LIST_PREV(ilist);
+               if (!ilist)
+                       break;
+
+               LIST_ITEM_GEO_GET(ilist, NULL, &y, &w, &h);
+               x -= w;
+               move_item(sc_data, ilist, x, y, w, h);
+
+               if (dx > 0 && !newlist) {
+                       if ((x == sc_data->clip_bx) || (x > sc_data->clip_bx && x < bx_bw))
+                               newlist = ilist;
+               }
+
+               boundary = ilist;
+       } while (x > sc_data->clip_bx);
+
+       x = sx;
+       w = sw;
+       ilist = sc_data->curlist;
+       do {
+               ilist = LIST_NEXT(ilist);
+               if (!ilist || (!sc_data->is_loop && ilist == sc_data->item_list) || ilist == boundary)
+                       break;
+
+               x += w;
+               LIST_ITEM_GEO_GET(ilist, NULL, &y, &w, &h);
+               move_item(sc_data, ilist, x, y, w, h);
+
+               if (dx < 0 && !newlist) {
+                       if ((x == sc_data->clip_bx) || (x < sc_data->clip_bx && (x + w) > sc_data->clip_bx))
+                               newlist = ilist;
+               }
+       } while (x < bx_bw);
+
+out:
+       if (newlist)
+               sc_data->curlist = newlist;
+               
+       return newlist;
+}
+
+static Eina_Bool emulate_evt(void *data)
+{
+       PROFILE_START();
+       struct widget_data *sc_data;
+       Evas_Coord x;
+       Evas_Coord dx;
+       struct item_list_entry *newlist;
+
+       sc_data = data;
+
+       x = get_evt_avg(&sc_data->evtq);
+       if (x == sc_data->old_x) {
+               PROFILE_END();
+               return ECORE_CALLBACK_RENEW;
+       }
+       
+       dx = x - sc_data->old_x;
+       sc_data->old_x = x;
+
+       newlist = update_items_geo(sc_data, dx);
+       if (newlist) {
+               int idx;
+
+               idx = list_item_idx(sc_data, newlist);
+               evas_object_smart_callback_call(
+                               sc_data->scroller, "page,changed", (void *)idx);
+       }
+       PROFILE_END();
+       return ECORE_CALLBACK_RENEW;
+}
+
+static void evt_mouse_down_cb(void *data, Evas *e, Evas_Object *evt_layer, void *event_info)
+{
+       Evas_Event_Mouse_Down *down;
+       struct widget_data *sc_data;
+
+       sc_data = data;
+       down = event_info;
+
+       sc_data->is_pressed = EINA_TRUE;
+       sc_data->press_x = down->canvas.x;
+       sc_data->press_y = down->canvas.y;
+       sc_data->old_x = down->canvas.x;
+
+       if (!sc_data->is_freezed) {
+               init_evtq(&sc_data->evtq);
+               enq_evt(&sc_data->evtq, down->canvas.x, down->timestamp);
+       }
+}
+
+static void evt_mouse_up_cb(void *data, Evas *e, Evas_Object *evt_layer, void *event_info)
+{
+       Evas_Event_Mouse_Up *up;
+       struct widget_data *sc_data;
+       struct live_sc_drag_info info;
+
+       sc_data = data;
+
+       if (!sc_data->is_pressed)
+               return;
+
+       sc_data->is_pressed = EINA_FALSE;
+
+       if (sc_data->evt_emulator) {
+               ecore_timer_del(sc_data->evt_emulator);
+               sc_data->evt_emulator = NULL;
+       }
+
+       if (sc_data->drag_started == EINA_FALSE) {
+               DbgPrint("drag is not started\n");
+               return;
+       }
+
+       up = event_info;
+
+       info.dx = up->canvas.x - sc_data->press_x;
+       info.dy = up->canvas.y - sc_data->press_y;
+
+       sc_data->drag_started = EINA_FALSE;
+
+       evas_object_smart_callback_call(sc_data->scroller, "drag,stop", &info);
+}
+
+static void evt_mouse_move_cb(void *data, Evas *e, Evas_Object *evt_layer, void *event_info)
+{
+       struct widget_data *sc_data;
+       Evas_Event_Mouse_Move *move;
+
+       sc_data = data;
+       
+       if (sc_data->is_pressed == EINA_FALSE)
+               return;
+
+       if (sc_data->item_cnt <= 1)
+               return;
+
+       if (sc_data->is_freezed)
+               return;
+
+       move = event_info;
+
+       if (sc_data->drag_started == EINA_FALSE) {
+               if (abs(move->cur.canvas.x - sc_data->press_x) < DRAG_SENS)
+                       return;
+
+               if (sc_data->sc_anim_timer) {
+                       ecore_timer_del(sc_data->sc_anim_timer);
+                       sc_data->sc_anim_timer = NULL;
+               }
+
+               evas_object_smart_callback_call(sc_data->scroller, "drag,start", NULL);
+               sc_data->drag_started = EINA_TRUE;
+       }
+
+       sc_data->prev_timestamp = move->timestamp;
+       enq_evt(&sc_data->evtq, move->cur.canvas.x, move->timestamp);
+       if (!sc_data->evt_emulator) {
+               if (!sc_data->curlist)
+                       sc_data->curlist = sc_data->item_list;
+
+               sc_data->evt_emulator = ecore_timer_add(EVT_PERIOD, emulate_evt, sc_data);
+       }
+}
+
+static inline int prepare_evt_layer(struct widget_data *sc_data)
+{
+       Evas *e;
+
+       e = evas_object_evas_get(sc_data->scroller);
+       if (!e)
+               return -EFAULT;
+
+       sc_data->evt_layer = evas_object_rectangle_add(e);
+       if (!sc_data->evt_layer)
+               return -EFAULT;
+
+       evas_object_smart_member_add(sc_data->evt_layer, sc_data->scroller);
+
+       evas_object_color_set(sc_data->evt_layer, 255, 255, 255, 0);
+       evas_object_show(sc_data->evt_layer);
+       evas_object_repeat_events_set(sc_data->evt_layer, EINA_TRUE);
+
+       evas_object_event_callback_add(sc_data->evt_layer,
+                       EVAS_CALLBACK_MOUSE_DOWN, evt_mouse_down_cb, sc_data);
+
+       evas_object_event_callback_add(sc_data->evt_layer,
+                       EVAS_CALLBACK_MOUSE_UP, evt_mouse_up_cb, sc_data);
+
+       evas_object_event_callback_add(sc_data->evt_layer,
+                       EVAS_CALLBACK_MOUSE_MOVE, evt_mouse_move_cb, sc_data);
+
+       evas_object_clip_set(sc_data->evt_layer, sc_data->clip);
+       return 0;
+}
+
+static void live_add(Evas_Object *scroller)
+{
+       struct widget_data *sc_data;
+       Evas *e;
+       int ret;
+
+       sc_data = calloc(1, sizeof(*sc_data));
+       if (!sc_data)
+               return;
+
+       e = evas_object_evas_get(scroller);
+       if (!e) {
+               free(sc_data);
+               return;
+       }
+
+       evas_object_smart_data_set(scroller, sc_data);
+
+       sc_data->clip = evas_object_rectangle_add(e);
+       if (!sc_data->clip) {
+               free(sc_data);
+               return;
+       }
+
+       sc_data->is_pressed = EINA_FALSE;
+       sc_data->drag_started = EINA_FALSE;
+       sc_data->tolist = NULL;
+       sc_data->curlist = NULL;
+       sc_data->item_list = NULL;
+       sc_data->scroller = scroller;
+
+       evas_object_smart_member_add(sc_data->clip, sc_data->scroller);
+
+       ret = prepare_evt_layer(sc_data);
+       if (ret < 0) {
+               evas_object_del(sc_data->clip);
+               free(sc_data);
+       }
+
+       return;
+}
+
+static void live_del(Evas_Object *scroller)
+{
+       struct widget_data *sc_data;
+       Evas_Object *item;
+       struct item_list_entry *ilist;
+       struct item_list_entry *next;
+
+       sc_data = evas_object_smart_data_get(scroller);
+       if (!sc_data)
+               return;
+
+       ilist = sc_data->item_list;
+       if (ilist) {
+               do {
+                       next = LIST_NEXT(ilist);
+                       item = LIST_DATA(ilist);
+                       evas_object_clip_unset(item);
+                       evas_object_smart_member_del(item);
+                       free(ilist);
+                       ilist = next;
+               } while (ilist != sc_data->item_list);
+       }
+
+       evas_object_del(sc_data->evt_layer);
+       evas_object_del(sc_data->clip);
+       free(sc_data);
+}
+
+static void live_move(Evas_Object *scroller, Evas_Coord bx, Evas_Coord by)
+{
+       struct widget_data *sc_data;
+       Evas_Coord x, y, w, h;
+       Evas_Coord bw;
+
+       Evas_Coord dx;
+       Evas_Coord dy;
+
+       struct item_list_entry *n;
+
+       sc_data = evas_object_smart_data_get(scroller);
+       if (!sc_data)
+               return;
+
+       evas_object_geometry_get(sc_data->clip, &x, &y, &bw, NULL);
+
+       evas_object_move(sc_data->evt_layer, bx, by);
+       evas_object_move(sc_data->clip, bx, by);
+       sc_data->clip_bx = bx;
+       sc_data->clip_bw = bw;
+
+       dx = bx - x;
+       dy = by - y;
+
+       if (sc_data->item_list) {
+               n = sc_data->item_list;
+               do {
+                       evas_object_move(LIST_DATA(n), bx, by);
+
+                       LIST_ITEM_GEO_GET(n, &x, &y, &w, &h);
+                       x += dx;
+                       y += dy;
+                       move_item(sc_data, n, x, y, w, h);
+                       n = LIST_NEXT(n);
+               } while (n != sc_data->item_list);
+       }
+}
+
+static void live_resize(Evas_Object *scroller, Evas_Coord w, Evas_Coord h)
+{
+       struct widget_data *sc_data;
+
+       sc_data = evas_object_smart_data_get(scroller);
+       if (!sc_data)
+               return;
+
+       evas_object_resize(sc_data->clip, w, h);
+       evas_object_resize(sc_data->evt_layer, w, h);
+
+       sc_data->clip_bw = w;
+}
+
+static void live_show(Evas_Object *scroller)
+{
+       struct widget_data *sc_data;
+
+       sc_data = evas_object_smart_data_get(scroller);
+       if (!sc_data)
+               return;
+
+       evas_object_show(sc_data->clip);
+}
+
+static void live_hide(Evas_Object *scroller)
+{
+       struct widget_data *sc_data;
+
+       sc_data = evas_object_smart_data_get(scroller);
+       if (!sc_data)
+               return;
+
+       evas_object_hide(sc_data->clip);
+}
+
+static void live_set_color(Evas_Object *scroller, int r, int g, int b, int a)
+{
+       struct widget_data *sc_data;
+
+       sc_data = evas_object_smart_data_get(scroller);
+       if (!sc_data)
+               return;
+
+       evas_object_color_set(sc_data->clip, r, g, b, a);
+}
+
+static void live_set_clip(Evas_Object *scroller, Evas_Object *clip)
+{
+       struct widget_data *sc_data;
+
+       sc_data = evas_object_smart_data_get(scroller);
+       if (!sc_data)
+               return;
+
+       evas_object_clip_set(sc_data->clip, clip);
+}
+
+static void live_unset_clip(Evas_Object *scroller)
+{
+       struct widget_data *sc_data;
+
+       sc_data = evas_object_smart_data_get(scroller);
+       if (!sc_data)
+               return;
+
+       evas_object_clip_unset(sc_data->clip);
+}
+
+static inline void rearrange_items(struct widget_data *sc_data)
+{
+       struct item_list_entry *ilist;
+       Evas_Coord x, y, w, h;
+       Evas_Coord sw;
+
+       LIST_ITEM_GEO_GET(sc_data->curlist, NULL, &y, &sw, &h);
+       move_item(sc_data, sc_data->curlist, sc_data->clip_bx, y, sw, h);
+
+       x = sc_data->clip_bx;
+       ilist = sc_data->curlist;
+       while (ilist != sc_data->item_list) {
+               ilist = LIST_PREV(ilist);
+               LIST_ITEM_GEO_GET(ilist, NULL, &y, &w, &h);
+               x -= w;
+               move_item(sc_data, ilist, x, y, w, h);
+       }
+
+       w = sw;
+       x = sc_data->clip_bx;
+       ilist = LIST_NEXT(sc_data->curlist);
+       while (ilist != sc_data->item_list) {
+               x += w;
+               LIST_ITEM_GEO_GET(ilist, NULL, &y, &w, &h);
+               move_item(sc_data, ilist, x, y, w, h);
+               ilist = LIST_NEXT(ilist);
+       }
+}
+
+static Eina_Bool sc_anim_cb(void *data)
+{
+       PROFILE_START();
+       struct widget_data *sc_data;
+       Evas_Coord sx, sw;
+       Evas_Coord y;
+       Evas_Coord dx;
+       struct item_list_entry *ilist;
+
+       sc_data = data;
+
+       if (!sc_data->curlist || !sc_data->tolist) {
+               DbgPrint("cur_list: %p, tolist: %p\n", sc_data->curlist, sc_data->tolist);
+               goto clean_out;
+       }
+
+       ilist = sc_data->curlist;
+       if (sc_data->curlist != sc_data->tolist) {
+               if (sc_data->sc_anim_dx > 0)
+                       ilist = LIST_PREV(ilist);
+               else
+                       ilist = LIST_NEXT(ilist);
+       }
+
+       LIST_ITEM_GEO_GET(ilist, &sx, &y, &sw, NULL);
+       if (ilist == sc_data->tolist) {
+               dx = abs(sx - sc_data->clip_bx);
+               if (dx < abs(sc_data->sc_anim_dx)) {
+                       if (sc_data->sc_anim_dx < 0)
+                               dx = -dx;
+               } else {
+                       dx = sc_data->sc_anim_dx;
+               }
+       } else {
+               dx = sc_data->sc_anim_dx;
+       }
+
+       if (!dx) {
+               DbgPrint("dx is 0\n");
+               goto clean_out;
+       }
+
+       ilist = update_items_geo(sc_data, dx);
+       if (ilist) {
+               int idx;
+
+               idx = list_item_idx(sc_data, ilist);
+               evas_object_smart_callback_call(sc_data->scroller,
+                                               "page,changed", (void *)idx);
+       }
+       PROFILE_END();
+       return ECORE_CALLBACK_RENEW;
+
+clean_out:
+       PROFILE_END();
+       evas_object_smart_callback_call(sc_data->scroller, "anim,stop", NULL);
+       sc_data->sc_anim_timer = NULL;
+       return ECORE_CALLBACK_CANCEL;
+}
+
+Evas_Object *live_scroller_add(Evas_Object *parent)
+{
+       static Evas_Smart_Class sc = EVAS_SMART_CLASS_INIT_NAME_VERSION("live,scroller");
+       static Evas_Smart *smart = NULL;
+       Evas_Object *scroller;
+       Evas *e;
+
+       if (!parent)
+               return NULL;
+
+       e = evas_object_evas_get(parent);
+       if (!e)
+               return NULL;
+
+       if (!smart) {
+               sc.add = live_add;
+               sc.del = live_del;
+               sc.move = live_move;
+               sc.resize = live_resize;
+               sc.show = live_show;
+               sc.hide = live_hide;
+               sc.color_set = live_set_color;
+               sc.clip_set = live_set_clip;
+               sc.clip_unset = live_unset_clip;
+
+               smart = evas_smart_class_new(&sc);
+       }
+
+       scroller = evas_object_smart_add(e, smart);
+
+       return scroller;
+}
+
+int live_scroller_append(Evas_Object *scroller, Evas_Object *item)
+{
+       Evas_Coord x, y, w, h;
+       Evas_Coord bx, by, bw, bh;
+       struct widget_data *sc_data;
+       struct item_list_entry *tmplist;
+
+       sc_data = evas_object_smart_data_get(scroller);
+       if (!sc_data)
+               return -EINVAL;
+
+       evas_object_geometry_get(sc_data->clip, &bx, &by, &bw, &bh);
+
+       tmplist = list_item_last_list(sc_data->item_list);
+       if (tmplist) {
+               LIST_ITEM_GEO_GET(tmplist, &x, NULL, &w, NULL);
+               x += w;
+       } else {
+               x = bx;
+       }
+       
+       evas_object_geometry_get(item, NULL, NULL, &w, &h);
+       evas_object_smart_member_add(item, sc_data->scroller);
+
+       y = by + ((bh - h) >> 1);
+
+       tmplist = list_item_append(sc_data->item_list, item);
+       if (sc_data->item_list == sc_data->curlist)
+               sc_data->curlist = tmplist;
+       if (sc_data->item_list == sc_data->tolist)
+               sc_data->tolist = tmplist;
+       sc_data->item_list = tmplist;
+
+       sc_data->item_cnt++;
+       evas_object_clip_set(item, sc_data->clip);
+       evas_object_stack_below(item, sc_data->clip);
+
+       evas_object_move(item, bx, by);
+       move_item(sc_data, list_item_find(sc_data->item_list, item), x, y, w, h);
+
+       return 0;
+}
+
+int live_scroller_remove_by_obj(Evas_Object *scroller, Evas_Object *obj)
+{
+       struct widget_data *sc_data;
+       struct item_list_entry *tmplist;
+       Evas_Object *item;
+
+       sc_data = evas_object_smart_data_get(scroller);
+       if (!sc_data)
+               return -EINVAL;
+
+       tmplist = list_item_remove(sc_data->item_list, obj);
+       if (sc_data->item_list == sc_data->curlist)
+               sc_data->curlist = tmplist;
+       if (sc_data->item_list == sc_data->tolist)
+               sc_data->tolist = tmplist;
+       sc_data->item_list = tmplist;
+
+       sc_data->item_cnt--;
+       evas_object_clip_unset(obj);
+       evas_object_smart_member_del(obj);
+
+       item = LIST_DATA(sc_data->curlist);
+       if (item) {
+               Evas_Coord y, w, h;
+               int idx;
+
+               LIST_ITEM_GEO_GET(sc_data->curlist, NULL, &y, &w, &h);
+               LIST_ITEM_GEO_SET(sc_data->curlist, sc_data->clip_bx, y, w, h);
+               update_items_geo(sc_data, 0);
+
+               idx = list_item_idx(sc_data, sc_data->curlist);
+               evas_object_smart_callback_call(sc_data->scroller,
+                                               "page,changed", (void *)idx);
+       }
+
+       return 0;
+}
+
+Evas_Object *live_scroller_remove(Evas_Object *scroller, int idx)
+{
+       struct widget_data *sc_data;
+       struct item_list_entry *tmplist;
+       Evas_Object *ret;
+       Evas_Object *item;
+
+       sc_data = evas_object_smart_data_get(scroller);
+       if (!sc_data)
+               return NULL;
+
+       if (idx < 0 || idx >= sc_data->item_cnt)
+               return NULL;
+
+       ret = list_item_nth(sc_data->item_list, idx);
+       if (!ret)
+               return NULL;
+
+       tmplist = list_item_remove(sc_data->item_list, ret);
+       if (sc_data->item_list == sc_data->curlist)
+               sc_data->curlist = tmplist;
+       if (sc_data->item_list == sc_data->tolist)
+               sc_data->tolist = tmplist;
+       sc_data->item_list = tmplist;
+
+       sc_data->item_cnt--;
+       evas_object_clip_unset(ret);
+       evas_object_smart_member_del(ret);
+
+       item = LIST_DATA(sc_data->curlist);
+       if (item) {
+               Evas_Coord y, w, h;
+               int idx;
+               LIST_ITEM_GEO_GET(sc_data->curlist, NULL, &y, &w, &h);
+               LIST_ITEM_GEO_SET(sc_data->curlist, sc_data->clip_bx, y, w, h);
+               update_items_geo(sc_data, 0);
+               idx = list_item_idx(sc_data, sc_data->curlist);
+               evas_object_smart_callback_call(
+                               sc_data->scroller, "page,changed", (void *)idx);
+       }
+       return ret;
+}
+
+Evas_Object *live_scroller_get_item(Evas_Object *scroller, int idx)
+{
+       struct widget_data *sc_data;
+
+       sc_data = evas_object_smart_data_get(scroller);
+       if (!sc_data)
+               return NULL;
+
+       if (idx < 0 || idx >= sc_data->item_cnt)
+               return NULL;
+
+       return list_item_nth(sc_data->item_list, idx);
+}
+
+int live_scroller_get_current(Evas_Object *scroller)
+{
+       struct widget_data *sc_data;
+       struct item_list_entry *ilist;
+       int idx;
+
+       sc_data = evas_object_smart_data_get(scroller);
+       if (!sc_data)
+               return -EINVAL;
+
+       ilist = sc_data->curlist;
+       idx = 0;
+       if (ilist) {
+               while (ilist != sc_data->item_list) {
+                       idx++;
+                       ilist = LIST_PREV(ilist);
+               }
+       }
+
+       return idx;
+}
+
+int live_scroller_loop_set(Evas_Object *scroller, int is_loop)
+{
+       struct widget_data *sc_data;
+
+       sc_data = evas_object_smart_data_get(scroller);
+       if (!sc_data)
+               return -EINVAL;
+
+       if (is_loop == EINA_FALSE && sc_data->is_loop == EINA_TRUE)
+               rearrange_items(sc_data);
+
+       sc_data->is_loop = is_loop;
+       return 0;
+}
+
+int live_scroller_freeze(Evas_Object *scroller)
+{
+       struct widget_data *sc_data;
+
+       sc_data = evas_object_smart_data_get(scroller);
+       if (!sc_data)
+               return -EINVAL;
+
+       sc_data->is_freezed = EINA_TRUE;
+       return 0;
+}
+
+int live_scroller_thaw(Evas_Object *scroller)
+{
+       struct widget_data *sc_data;
+
+       sc_data = evas_object_smart_data_get(scroller);
+       if (!sc_data)
+               return -EINVAL;
+
+       sc_data->is_freezed = EINA_FALSE;
+       return 0;
+}
+
+int live_scroller_anim_to(Evas_Object *scroller, double sec, int offset)
+{
+       PROFILE_START();
+       struct widget_data *sc_data;
+       Evas_Coord sx, sw;
+       Evas_Coord y;
+       struct live_sc_event_info info;
+       struct item_list_entry *ilist;
+       double ftmp;
+       int ret;
+
+       sc_data = evas_object_smart_data_get(scroller);
+       if (!sc_data) {
+               ret = -EINVAL;
+               goto out;
+       }
+       
+       if (sc_data->is_freezed) {
+               ret = -EBUSY;
+               goto out;
+       }
+
+       if (!sc_data->curlist)
+               sc_data->curlist = sc_data->item_list;
+
+       if (!sc_data->curlist) {
+               ret = -ENOENT;
+               goto out;
+       }
+
+       if (sc_data->sc_anim_timer) {
+               ecore_timer_del(sc_data->sc_anim_timer);
+               sc_data->sc_anim_timer = NULL;
+               evas_object_smart_callback_call(sc_data->scroller, "anim,stop", NULL);
+       } else {
+               sc_data->tolist = sc_data->curlist;
+       }
+
+       LIST_ITEM_GEO_GET(sc_data->curlist, &sx, &y, &sw, NULL);
+
+       if (!offset) {
+               sc_data->sc_anim_dx = sc_data->clip_bx - sx;
+       } else {
+               Evas_Coord tw;
+               struct item_list_entry *tmplist;
+
+               calc_anim_dx_with_dir(sc_data, &offset);
+
+               ilist = sc_data->curlist;
+               while (offset < 0) {
+                       if (!sc_data->is_loop && ilist == sc_data->item_list)
+                               break;
+
+                       LIST_ITEM_GEO_GET(ilist, NULL, NULL, &tw, NULL);
+                       ilist = LIST_PREV(ilist);
+
+                       sc_data->sc_anim_dx += tw;
+
+                       offset++;
+                       if (sc_data->tolist == sc_data->item_list) {
+                               if (!sc_data->is_loop)
+                                       break;
+                       }
+                       sc_data->tolist = LIST_PREV(sc_data->tolist);
+               }
+               
+               while (offset > 0) {
+                       LIST_ITEM_GEO_GET(ilist, NULL, NULL, &tw, NULL);
+                       ilist = LIST_NEXT(ilist);
+
+                       sc_data->sc_anim_dx -= tw;
+
+                       offset--;
+                       tmplist = LIST_NEXT(sc_data->tolist);
+                       if (tmplist == sc_data->item_list) {
+                               if (!sc_data->is_loop)
+                                       break;
+                       }
+                       sc_data->tolist = tmplist;
+
+                       if (!sc_data->is_loop && ilist == sc_data->item_list)
+                               break;
+               }
+       }
+
+       if (abs(sc_data->sc_anim_dx) > ANIM_MIN) {
+               ftmp = (double)sc_data->sc_anim_dx / ANIM_UNIT;
+               if (fabs(ftmp) < ANIM_MIN || fabs(ftmp) > abs(sc_data->sc_anim_dx))
+                       sc_data->sc_anim_dx = ftmp < 0 ? -ANIM_MIN : ANIM_MIN;
+               else
+                       sc_data->sc_anim_dx = ftmp;
+       }
+
+       sc_data->sc_anim_timer = ecore_timer_add(sec, sc_anim_cb, sc_data);
+       if (!sc_data->sc_anim_timer) {
+               ret = -EFAULT;
+               goto out;
+       }
+
+       info.curidx = list_item_idx(sc_data, sc_data->curlist);
+       info.toidx = list_item_idx(sc_data, sc_data->tolist);
+
+       evas_object_smart_callback_call(sc_data->scroller, "anim,start", &info);
+       ret = 0;
+
+out:
+       PROFILE_END();
+       return ret;
+}
+
+int live_scroller_go_to(Evas_Object *scroller, int idx)
+{
+       struct widget_data *sc_data;
+
+       sc_data = evas_object_smart_data_get(scroller);
+       if (!sc_data)
+               return -EINVAL;
+
+       if (sc_data->is_freezed)
+               return -EBUSY;
+
+       if (idx < 0 || idx >= sc_data->item_cnt)
+               return -EINVAL;
+
+       sc_data->curlist = list_item_nth_list(sc_data->item_list, idx);
+       if (!sc_data->curlist)
+               return -EFAULT;
+
+       rearrange_items(sc_data);
+       evas_object_smart_callback_call(sc_data->scroller,
+                                       "page,changed", (void *)idx);
+
+       return 0;
+}
+
+int live_scroller_update(Evas_Object *scroller)
+{
+       struct widget_data *sc_data;
+       struct item_list_entry *n;
+       Evas_Object *item;
+       Evas_Coord x, y, w, h;
+
+       sc_data = evas_object_smart_data_get(scroller);
+       if (!sc_data)
+               return -EINVAL;
+
+       if (sc_data->item_list) {
+               n = sc_data->item_list;
+               do {
+                       item = LIST_DATA(n);
+                       LIST_ITEM_GEO_GET(n, &x, &y, &w, &h);
+                       move_item(sc_data, n, x, y, w, h);
+                       n = LIST_NEXT(n);
+               } while (n != sc_data->item_list);
+       }
+
+       return 0;
+}
+
+int live_scroller_get_item_count(Evas_Object *scroller)
+{
+       struct widget_data *sc_data;
+
+       sc_data = evas_object_smart_data_get(scroller);
+       if (!sc_data)
+               return 0;
+
+       return list_item_count(sc_data->item_list);
+}
+
+int live_scroller_get_item_index(Evas_Object *scroller, Evas_Object *item)
+{
+       struct widget_data *sc_data;
+       struct item_list_entry *n;
+       Evas_Object *tmp;
+       int idx;
+
+       sc_data = evas_object_smart_data_get(scroller);
+       if (!sc_data)
+               return -EINVAL;
+
+       if (!sc_data->item_list)
+               return -ENOENT;
+
+       idx = 0;
+       n = sc_data->item_list;
+       do {
+               tmp = LIST_DATA(n);
+               n = LIST_NEXT(n);
+
+               if (tmp == item)
+                       return idx;
+
+               idx++;
+       } while (n != sc_data->item_list);
+
+       return -ENOENT;
+}
+
+/* End of a file */
diff --git a/live.viewer/src/main.c b/live.viewer/src/main.c
new file mode 100644 (file)
index 0000000..3ddcfc7
--- /dev/null
@@ -0,0 +1,160 @@
+#include <Elementary.h>
+
+#include <dlog.h>
+#include <ail.h>
+#include <app.h>
+#include <bundle.h>
+
+#include <livebox.h>
+#include <livebox-service.h>
+
+#include "main.h"
+#include "util.h"
+#include "debug.h"
+#include "scroller.h"
+#include "lb.h"
+
+static struct info {
+       Evas_Object *window;
+       Evas_Object *scroller;
+} s_info = {
+       .window = NULL,
+       .scroller = NULL,
+};
+
+Evas_Object *main_get_window(void)
+{
+       return s_info.window;
+}
+
+static void click_cb(void *data, Evas_Object *obj, void *event_info)
+{
+       Elm_Object_Item *item;
+       const char *label;
+
+       item = elm_list_selected_item_get(obj);
+       if (!item)
+               return;
+
+       label = elm_object_item_part_text_get(item, NULL);
+       if (!label)
+               return;
+
+       DbgPrint("Label: %s (%s)\n", label, data);
+       if (lb_add(s_info.scroller, data) < 0)
+               ErrPrint("Failed to add a new livebox\n");
+}
+
+static int append_livebox_cb(const char *appid, const char *lbid, int is_prime, void *data)
+{
+       char *name;
+
+       DbgPrint("%s - %s\n", appid, lbid);
+
+       name = livebox_service_i18n_name(lbid, NULL);
+       if (!name) {
+               name = strdup(lbid);
+               if (!name) {
+                       ErrPrint("Heap: %s\n", strerror(errno));
+                       return 0;
+               }
+       }
+
+       DbgPrint("Name: %s\n", name);
+       elm_list_item_append(data, name, NULL, NULL, click_cb, strdup(lbid));
+       free(name);
+       return 0;
+}
+
+static inline void livebox_list_create(void)
+{
+       Evas_Object *list;
+
+       list = elm_list_add(s_info.window);
+       evas_object_resize(list, 720, 1280);
+       evas_object_show(list);
+
+       DbgPrint("Get Package list\n");
+       livebox_service_get_pkglist(append_livebox_cb, list);
+       scroller_append(s_info.scroller, list);
+       elm_list_go(list);
+}
+
+static bool app_create(void *data)
+{
+       DbgPrint("create");
+       lb_init();
+
+       s_info.window = elm_win_add(NULL, "Box viewer", ELM_WIN_BASIC);
+       if (!s_info.window) {
+               ErrPrint("Failed to create a window\n");
+               return false;
+       }
+
+       evas_object_resize(s_info.window, 720, 1280);
+       evas_object_show(s_info.window);
+
+       s_info.scroller = scroller_create(s_info.window);
+       if (!s_info.scroller) {
+               evas_object_del(s_info.window);
+               s_info.window = NULL;
+               ErrPrint("Failed to create a scroller\n");
+               return false;
+       }
+
+       evas_object_resize(s_info.scroller, 720, 1280);
+       evas_object_show(s_info.scroller);
+
+       livebox_list_create();
+
+       return true;
+}
+
+static void app_terminate(void *data)
+{
+       DbgPrint("terminate");
+       lb_fini();
+       /*!
+        * \TODO
+        * Delete all objects from the scroller.
+        */
+
+       scroller_destroy(s_info.scroller);
+       evas_object_del(s_info.window);
+       s_info.window = NULL;
+}
+
+static void app_pause(void *data)
+{
+       DbgPrint("pause");
+}
+
+static void app_resume(void *data)
+{
+       DbgPrint("resume");
+}
+
+static void app_reset(service_h service, void *data)
+{
+       DbgPrint("reset");
+}
+
+int main(int argc, char *argv[])
+{
+       app_event_callback_s event_callback;
+
+       setenv("ELM_ENGINE", "gl", 0);
+       event_callback.create = app_create;
+       event_callback.terminate = app_terminate;
+       event_callback.pause = app_pause;
+       event_callback.resume = app_resume;
+       event_callback.service = app_reset;
+       event_callback.low_memory = NULL;
+       event_callback.low_battery = NULL;
+       event_callback.device_orientation = NULL;
+       event_callback.language_changed = NULL;
+
+       return app_efl_main(&argc, &argv, &event_callback, NULL);
+}
+
+/* End of a file */
diff --git a/live.viewer/src/scroller.c b/live.viewer/src/scroller.c
new file mode 100644 (file)
index 0000000..8dd531e
--- /dev/null
@@ -0,0 +1,487 @@
+#include <Elementary.h>
+
+#include <dlog.h>
+
+#include "util.h"
+#include "live_scroller.h"
+#include "scroller.h"
+#include "debug.h"
+
+#define FOCAL_DIST 800
+#define FLICK_COND 100
+
+struct cb_item {
+       int (*cb)(Evas_Object *sc, void *data);
+       void *data;
+};
+
+struct scroll_info {
+       int locked;
+       Eina_Bool scrolling;
+       int focal;
+       Eina_Bool quick;
+
+       Evas_Map *map;
+
+       Ecore_Idler *bg_changer;
+       Eina_List *cb_list;
+};
+
+void scroller_lock(Evas_Object *sc)
+{
+       struct scroll_info *scinfo;
+
+       scinfo = evas_object_data_get(sc, "scinfo");
+       if (!scinfo)
+               return;
+
+       if (!scinfo->locked)
+               live_scroller_freeze(sc);
+
+       scinfo->locked++;
+}
+
+void scroller_unlock(Evas_Object *sc)
+{
+       struct scroll_info *scinfo;
+
+       scinfo = evas_object_data_get(sc, "scinfo");
+       if (!scinfo)
+               return;
+
+       if (scinfo->locked == 0)
+               return;
+
+       scinfo->locked--;
+
+       if (scinfo->locked == 0)
+               live_scroller_thaw(sc);
+}
+
+static void sc_anim_stop(void *data, Evas_Object *obj, void *event_info)
+{
+       Eina_List *l;
+       Eina_List *tmp;
+       struct cb_item *item;
+       struct scroll_info *scinfo;
+
+       scinfo = evas_object_data_get(obj, "scinfo");
+       if (!scinfo)
+               return;
+       /*!
+        * \TODO
+        * Do what you want at here when the scroller is stopped
+        */
+
+       scinfo->scrolling = EINA_FALSE;
+       EINA_LIST_FOREACH_SAFE(scinfo->cb_list, l, tmp, item) {
+               if (item->cb(obj, item->data) == ECORE_CALLBACK_CANCEL) {
+                       if (eina_list_data_find(scinfo->cb_list, item)) {
+                               scinfo->cb_list = eina_list_remove(scinfo->cb_list, item);
+                               free(item);
+                       }
+               }
+       }
+}
+
+static inline void sc_drag_start(void *data, Evas_Object *obj, void *event_info)
+{
+       struct scroll_info *scinfo;
+
+       scinfo = evas_object_data_get(obj, "scinfo");
+       if (!scinfo)
+               return;
+
+       scinfo->scrolling = EINA_TRUE;
+}
+
+static inline void sc_drag_stop(void *data, Evas_Object *scroller, void *event_info)
+{
+       struct live_sc_drag_info *info;
+       int offset = 0;
+       int ret;
+
+       info = event_info;
+
+       if (info->dx > FLICK_COND)
+               offset = -1;
+       else if (info->dx < -FLICK_COND)
+               offset = 1;
+
+       ret = live_scroller_anim_to(scroller, 0.016f, offset);
+       if (ret < 0) {
+               struct scroll_info *scinfo;
+               scinfo = evas_object_data_get(scroller, "scinfo");
+               if (scinfo)
+                       scinfo->scrolling = EINA_FALSE;
+       }
+}
+
+static Eina_Bool bg_change_cb(void *data)
+{
+       Evas_Object *sc = data;
+       struct scroll_info *scinfo;
+
+       scinfo = evas_object_data_get(sc, "scinfo");
+       if (scinfo)
+               scinfo->bg_changer = NULL;
+
+       /*
+        * NOTE:
+        *  Here,
+        *  Filename of background image handling code is only
+        *  used to demonstrates UX concept and estimates its perfomance.
+        *  So, I'll change this if it should be appled to
+        *  main branch.
+        */
+       DbgPrint("Change the background image (%p)\n", sc);
+       return ECORE_CALLBACK_CANCEL;
+}
+
+static void sc_anim_start(void *data, Evas_Object *obj, void *event_info)
+{
+       struct scroll_info *scinfo;
+
+       scinfo = evas_object_data_get(obj, "scinfo");
+       if (!scinfo)
+               return;
+
+       /* \note
+        * without drag,start
+        * anim start can be invoked by the scroller_anim_to
+        */
+       scinfo->scrolling = EINA_TRUE;
+
+       if (scinfo->bg_changer)
+               ecore_idler_del(scinfo->bg_changer);
+
+       scinfo->bg_changer = ecore_idler_add(bg_change_cb, obj);
+       if (!scinfo->bg_changer)
+               DbgPrint("Failed to add an idler\n");
+}
+
+static void sc_item_moved(void *data, Evas_Object *obj, void *event_info)
+{
+       struct live_sc_move_info *evt = event_info;
+       int color;
+       int focal;
+       Evas_Coord y, sx, sw;
+       double ftmp;
+       struct scroll_info *scinfo;
+
+       scinfo = evas_object_data_get(obj, "scinfo");
+       if (!scinfo) {
+               ErrPrint("Has no scinfo\n");
+               return;
+       }
+
+       ftmp = fabsl(evt->relx);
+       if (ftmp >= 1.0f) {
+               evas_object_map_enable_set(evt->item, EINA_FALSE);
+               evas_object_hide(evt->item);
+               return;
+       }
+
+       color = 255 * (1.0f - ftmp);
+       if (scinfo->quick) {
+               if (color < 100)
+                       color = 100;
+
+               focal = scinfo->focal;
+       } else {
+               if (color == 0) {
+                       evas_object_map_enable_set(evt->item, EINA_FALSE);
+                       evas_object_hide(evt->item);
+                       return;
+               }
+
+               focal = -ftmp * 200.0f + scinfo->focal;
+       }
+
+       evas_object_geometry_get(data, &sx, NULL, &sw, NULL);
+       
+       /* LEFT */
+       evas_map_point_coord_set(scinfo->map, 0, evt->x, evt->y, 0);
+       evas_map_point_image_uv_set(scinfo->map, 0, 0, 0);
+       evas_map_point_color_set(scinfo->map, 0, color, color, color, color);
+
+       /* RIGHT */
+       evas_map_point_coord_set(scinfo->map, 1, evt->x + evt->w, evt->y, 0);
+       evas_map_point_image_uv_set(scinfo->map, 1, evt->w, 0);
+       evas_map_point_color_set(scinfo->map, 1, color, color, color, color);
+
+       /* BOTTOM-RIGHT */
+       evas_map_point_coord_set(scinfo->map, 2, evt->x + evt->w, evt->y + evt->h, 0);
+       evas_map_point_image_uv_set(scinfo->map, 2, evt->w, evt->h);
+       evas_map_point_color_set(scinfo->map, 2, color, color, color, color);
+
+       /* BOTTOM-LEFT */
+       evas_map_point_coord_set(scinfo->map, 3, evt->x, evt->y + evt->h, 0);
+       evas_map_point_image_uv_set(scinfo->map, 3, 0, evt->h);
+       evas_map_point_color_set(scinfo->map, 3, color, color, color, color);
+
+       y = evt->y + (evt->h >> 1);
+       evas_map_util_3d_rotate(scinfo->map, 0.0f, -30.0f * evt->relx, 0.0f, evt->x + (evt->w >> 1), y, 0);
+       evas_map_util_3d_perspective(scinfo->map, sx + (sw >> 1), y, focal, FOCAL_DIST);
+       evas_object_map_set(evt->item, scinfo->map);
+       evas_object_map_enable_set(evt->item, EINA_TRUE);
+       evas_object_show(evt->item);
+       return;
+}
+
+static void sc_page_changed(void *data, Evas_Object *obj, void *event_info)
+{
+       DbgPrint("Page is changed %d\n", (int)event_info);
+}
+
+int scroller_add_stop_cb(Evas_Object *scroller,
+                       int (*cb)(Evas_Object *sc, void *data), void *data)
+{
+       struct cb_item *item;
+       struct scroll_info *scinfo;
+
+       scinfo = evas_object_data_get(scroller, "scinfo");
+       if (!scinfo)
+               return -EINVAL;
+
+       item = calloc(1, sizeof(*item));
+       if (!item) {
+               ErrPrint("Error: %s\n", strerror(errno));
+               return EXIT_FAILURE;
+       }
+
+       item->cb = cb;
+       item->data = data;
+
+       scinfo->cb_list = eina_list_append(scinfo->cb_list, item);
+       return EXIT_SUCCESS;
+}
+
+void scroller_del_stop_cb(Evas_Object *scroller,
+                       int (*cb)(Evas_Object *sc, void *data), void *data)
+{
+       struct cb_item *item;
+       Eina_List *l;
+       Eina_List *tmp;
+       struct scroll_info *scinfo;
+
+       scinfo = evas_object_data_get(scroller, "scinfo");
+       if (!scinfo) {
+               ErrPrint("Failed to get scinfo\n");
+               return;
+       }
+
+       EINA_LIST_FOREACH_SAFE(scinfo->cb_list, l, tmp, item) {
+               if (item->cb == cb && item->data == data) {
+                       scinfo->cb_list = eina_list_remove(scinfo->cb_list, item);
+                       free(item);
+                       break;
+               }
+       }
+}
+
+Evas_Object *scroller_create(Evas_Object *ctrl)
+{
+       Evas_Object *sc;
+       struct scroll_info *scinfo;
+
+       scinfo = calloc(1, sizeof(*scinfo));
+       if (!scinfo) {
+               ErrPrint("Heap: %s\n", strerror(errno));
+               return NULL;
+       }
+
+       sc = live_scroller_add(ctrl);
+       if (!sc) {
+               DbgPrint("Failed to create flip object\n");
+               free(scinfo);
+               return NULL;
+       }
+
+       evas_object_data_set(sc, "scinfo", scinfo);
+
+       scinfo->map = evas_map_new(4);
+       if (!scinfo->map) {
+               evas_object_del(sc);
+               free(scinfo);
+               return NULL;
+       }
+
+       evas_map_smooth_set(scinfo->map, EINA_TRUE);
+       evas_map_alpha_set(scinfo->map, EINA_TRUE);
+
+       evas_object_size_hint_weight_set(sc,
+                                       EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
+       evas_object_smart_callback_add(sc, "drag,start", sc_drag_start, NULL);
+       evas_object_smart_callback_add(sc, "drag,stop", sc_drag_stop, NULL);
+       evas_object_smart_callback_add(sc, "anim,stop", sc_anim_stop, NULL);
+       evas_object_smart_callback_add(sc, "anim,start", sc_anim_start, NULL);
+       evas_object_smart_callback_add(sc, "page,changed", sc_page_changed, NULL);
+       evas_object_smart_callback_add(sc, "item,moved", sc_item_moved, NULL);
+       live_scroller_loop_set(sc, EINA_TRUE);
+       evas_object_show(sc);
+
+       return sc;
+}
+
+int scroller_append(Evas_Object *sc, Evas_Object *child)
+{
+       return live_scroller_append(sc, child);
+}
+
+int scroller_get_page_index(Evas_Object *sc, Evas_Object *page)
+{
+       return live_scroller_get_item_index(sc, page);
+}
+
+Evas_Object *scroller_get_page(Evas_Object *sc, int idx)
+{
+       return live_scroller_get_item(sc, idx);
+}
+
+Evas_Object *scroller_peek_by_idx(Evas_Object *sc, int idx)
+{
+       return live_scroller_remove(sc, idx);
+}
+
+int scroller_peek_by_obj(Evas_Object *sc, Evas_Object *page)
+{
+       return live_scroller_remove_by_obj(sc, page);
+}
+
+int scroller_get_current_idx(Evas_Object *sc)
+{
+       return live_scroller_get_current(sc);
+}
+
+int scroller_is_scrolling(Evas_Object *sc)
+{
+       struct scroll_info *scinfo;
+
+       scinfo = evas_object_data_get(sc, "scinfo");
+       if (!scinfo)
+               return -EINVAL;
+
+       return scinfo->scrolling;
+}
+
+int scroller_get_page_count(Evas_Object *sc)
+{
+       return live_scroller_get_item_count(sc);
+}
+
+int scroller_scroll_to(Evas_Object *sc, int idx)
+{
+       int curidx;
+       int cnt;
+       register int i;
+       int next_offset;
+       int prev_offset;
+       struct scroll_info *scinfo;
+
+       scinfo = evas_object_data_get(sc, "scinfo");
+       if (!scinfo)
+               return -EINVAL;
+
+       if (scinfo->scrolling) {
+               DbgPrint("Scroller is scrolling\n");
+               return -EINVAL;
+       }
+
+       curidx = live_scroller_get_current(sc);
+       cnt = live_scroller_get_item_count(sc);
+
+       i = curidx;
+       next_offset = 0;
+       while (i != idx/* && i >= 0 && i < cnt*/) {
+               i++;
+               if (i >= cnt)
+                       i = 0;
+
+               next_offset++;
+       }
+
+       i = curidx;
+       prev_offset = 0;
+       while (i != idx/* && i >= 0 && i < cnt*/) {
+               i--;
+               if (i < 0)
+                       i = cnt - 1;
+
+               prev_offset--;
+       }
+
+       idx = next_offset < -prev_offset ? next_offset : prev_offset;
+       live_scroller_anim_to(sc, 0.016f, idx);
+       return 0;
+}
+
+int scroller_jump_to(Evas_Object *sc, int idx)
+{
+       live_scroller_go_to(sc, idx);
+       return 0;
+}
+
+int scroller_destroy(Evas_Object *sc)
+{
+       int cnt;
+       struct scroll_info *scinfo;
+       struct cb_item *item;
+
+       scinfo = evas_object_data_del(sc, "scinfo");
+       if (!scinfo)
+               return -EFAULT;
+
+       if (scinfo->bg_changer)
+               ecore_idler_del(scinfo->bg_changer);
+
+       EINA_LIST_FREE(scinfo->cb_list, item) {
+               free(item);
+       }
+
+       cnt = live_scroller_get_item_count(sc);
+       if (cnt)
+               DbgPrint("Children is not cleared (%d)\n", cnt);
+
+       evas_object_del(sc);
+       evas_map_free(scinfo->map);
+       free(scinfo);
+       return 0;
+}
+
+int scroller_update(Evas_Object *sc, void *data)
+{
+       struct scroll_info *scinfo;
+
+       scinfo = evas_object_data_get(sc, "scinfo");
+       if (!scinfo)
+               return -EFAULT;
+
+       scinfo->focal = (int)data;
+       live_scroller_update(sc);
+       return EXIT_SUCCESS;
+}
+
+int scroller_fast_scroll(Evas_Object *sc, int idx)
+{
+       idx -= scroller_get_current_idx(sc);
+       live_scroller_anim_to(sc, 0.016f, idx);
+       return 0;
+}
+
+void scroller_loop_set(Evas_Object *sc, Eina_Bool val)
+{
+       live_scroller_loop_set(sc, val);
+}
+
+void scroller_quick_navi(Evas_Object *sc, Eina_Bool val)
+{
+       struct scroll_info *scinfo;
+       scinfo = evas_object_data_get(sc, "scinfo");
+       if (!scinfo)
+               return;
+
+       scinfo->quick = val;
+}
+
+/* End of a file */
diff --git a/live.viewer/src/util.c b/live.viewer/src/util.c
new file mode 100644 (file)
index 0000000..013f629
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * com.samsung.live-magazine
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact: Sung-jae Park <nicesj.park@samsung.com>, Youngjoo Park <yjoo93.park@samsung.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include <dlog.h>
+
+#include "debug.h"
+#include "util.h"
+
+int errno;
+
+const char *util_basename(const char *name)
+{
+       int length;
+       length = name ? strlen(name) : 0;
+       if (!length)
+               return ".";
+
+       while (--length > 0 && name[length] != '/');
+
+       return length <= 0 ? name : name + length + (name[length] == '/');
+}
+
+/* End of a file */