7d794d6d5b9c8715595df71a0af7ac14399bb870
[platform/framework/web/livebox-viewer.git] / live.viewer / src / live_scroller.c
1 /*
2  * Copyright 2013  Samsung Electronics Co., Ltd
3  *
4  * Licensed under the Flora License, Version 1.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://floralicense.org
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include <Elementary.h>
18
19 #include <dlog.h>
20
21 #include "live_scroller.h"
22 #include "util.h"
23 #include "debug.h"
24
25 #define SAMPLE_MAX 10
26 #define EVT_PERIOD 0.016 /* 60 fps */
27 #define EVT_SAMPLE_PERIOD 9
28 #define DRAG_SENS 100
29 #define ANIM_MIN 40
30 #define ANIM_UNIT 15
31
32 struct item_list_entry {
33         struct item_list_entry *prev;
34         struct item_list_entry *next;
35
36         Evas_Object *data;
37         Evas_Coord x;
38         Evas_Coord y;
39         Evas_Coord w;
40         Evas_Coord h;
41 };
42
43 struct evt_info {
44         Evas_Coord x;
45         unsigned int timestamp;
46 };
47
48 struct evt_queue {
49         struct evt_info ei[SAMPLE_MAX];
50         int front;
51         int rear;
52         int cnt;
53         unsigned int old_timestamp;
54 };
55
56 struct widget_data {
57         Eina_Bool is_loop;
58         Eina_Bool is_freezed;
59
60         struct item_list_entry *item_list;
61
62         int item_cnt;
63         struct item_list_entry *curlist;
64         struct item_list_entry *tolist;
65
66         Eina_Bool drag_started;
67         Eina_Bool is_pressed;
68         Evas_Coord press_x;
69         Evas_Coord press_y;
70
71         Ecore_Timer *sc_anim_timer;
72         int sc_anim_dx;
73
74         Evas_Object *clip;
75         Evas_Object *evt_layer;
76         Evas_Object *scroller;
77
78         Evas_Coord clip_bx;
79         Evas_Coord clip_bw;
80
81         struct evt_queue evtq;
82         Ecore_Timer *evt_emulator;
83         Evas_Coord old_x;
84         unsigned int prev_timestamp;
85 };
86
87 #ifdef PROFILE
88 #define PROFILE_START() \
89 static int _exec_cnt = 0; \
90 struct timeval _stv, _etv; \
91 long _elapsed; \
92 gettimeofday(&_stv, NULL);
93
94 #define PROFILE_END() \
95 do { \
96         _exec_cnt++; \
97         gettimeofday(&_etv, NULL); \
98         _elapsed = (_etv.tv_sec - _stv.tv_sec) * 1000000l + (_etv.tv_usec - _stv.tv_usec); \
99         DbgPrint("[%d] Elapsed time: %lu\n", _exec_cnt, _elapsed); \
100 } while (0)
101 #else
102 #define PROFILE_START()
103 #define PROFILE_END()
104 #endif
105
106 #define LIST_NEXT(list) ((list)->next)
107 #define LIST_PREV(list) ((list)->prev)
108 #define LIST_DATA(list) ((list) ? (list)->data : NULL)
109
110 static inline void LIST_ITEM_GEO_SET(struct item_list_entry *list, int x, int y, int w, int h)
111 {
112         list->x = x;
113         list->y = y;
114         list->w = w;
115         list->h = h;
116 }
117
118 static inline void LIST_ITEM_GEO_GET(struct item_list_entry *list, int *x, int *y, int *w, int *h)
119 {
120         if (x)
121                 *x = list->x;
122         if (y)
123                 *y = list->y;
124         if (w)
125                 *w = list->w;
126         if (h)
127                 *h = list->h;
128 }
129
130 static inline struct item_list_entry *list_item_append(struct item_list_entry *list, void *obj)
131 {
132         struct item_list_entry *item;
133
134         item = malloc(sizeof(*item));
135         if (!item)
136                 return NULL;
137
138         item->data = obj;
139
140         if (list) {
141                 list->prev->next = item;
142                 item->prev = list->prev;
143
144                 item->next = list;
145                 list->prev = item;
146         } else{
147                 item->prev = item;
148                 item->next = item;
149                 list = item;
150         }
151
152         return list;
153 }
154
155 static inline void *list_item_nth(struct item_list_entry *list, int idx)
156 {
157         if (!list)
158                 return NULL;
159
160         while (--idx >= 0)
161                 list = list->next;
162
163         return list->data;
164 }
165
166 static inline struct item_list_entry *list_item_nth_list(struct item_list_entry *list, int idx)
167 {
168         if (!list)
169                 return NULL;
170
171         while (--idx >= 0)
172                 list = list->next;
173
174         return list;
175 }
176
177 static inline struct item_list_entry *list_item_find(struct item_list_entry *list, void *data)
178 {
179         struct item_list_entry *item;
180
181         if (!list)
182                 return NULL;
183
184         item = list;
185         do {
186                 if (LIST_DATA(item) == data)
187                         return item;
188
189                 item = LIST_NEXT(item);
190         } while (item != list);
191
192         return NULL;
193 }
194
195 static inline struct item_list_entry *list_item_remove(struct item_list_entry *list, void *data)
196 {
197         struct item_list_entry *item;
198
199         if (!list) {
200                 ErrPrint("List is not valid\n");
201                 return NULL;
202         }
203
204         DbgPrint("Start\n");
205         item = list;
206         do {
207                 if (LIST_DATA(item) == data) {
208                         DbgPrint("ITEM is removed\n");
209                         if (item == list) {
210                                 if (list == LIST_NEXT(list))
211                                         list = NULL;
212                                 else
213                                         list = LIST_NEXT(list);
214                         }
215
216                         item->prev->next = item->next;
217                         item->next->prev = item->prev;
218                         free(item);
219                         break;
220                 }
221
222                 item = LIST_NEXT(item);
223         } while (item != list);
224         DbgPrint("End\n");
225
226         return list;
227 }
228
229 static inline void *list_item_last(struct item_list_entry *list)
230 {
231         if (!list)
232                 return NULL;
233
234         return list->prev->data;
235 }
236
237 static inline struct item_list_entry *list_item_last_list(struct item_list_entry *list)
238 {
239         if (!list)
240                 return NULL;
241
242         return list->prev;
243 }
244
245 static inline int list_item_count(struct item_list_entry *list)
246 {
247         struct item_list_entry *n;
248         int cnt;
249
250         if (!list)
251                 return 0;
252
253         cnt = 0;
254         n = list;
255         do {
256                 cnt++;
257                 n = LIST_NEXT(n);
258         } while (n != list);
259
260         return cnt;
261 }
262
263 static inline int list_item_idx(struct widget_data *sc_data, struct item_list_entry *ilist)
264 {
265         int idx;
266         idx = 0;
267
268         while (ilist != sc_data->item_list) {
269                 idx++;
270                 ilist = LIST_PREV(ilist);
271         }
272
273         return idx;
274 }
275
276 static inline void init_evtq(struct evt_queue *evtq)
277 {
278         evtq->front = 0;
279         evtq->rear = 0;
280         evtq->cnt = 0;
281         evtq->old_timestamp = 0;
282 }
283
284 static inline void dq_evt(struct evt_queue *evtq)
285 {
286         if (evtq->cnt <= 0)
287                 return;
288         evtq->front++;
289         if (evtq->front >= SAMPLE_MAX)
290                 evtq->front -= SAMPLE_MAX;
291         evtq->cnt--;
292 }
293
294 static inline void enq_evt(struct evt_queue *evtq, Evas_Coord x, unsigned int timestamp)
295 {
296         unsigned int t_diff;
297         int replace;
298
299         replace = 0;
300         t_diff = timestamp - evtq->old_timestamp;
301
302         if (evtq->cnt <= 0)
303                 evtq->old_timestamp = timestamp;
304         else if (t_diff > EVT_SAMPLE_PERIOD)
305                 evtq->old_timestamp += EVT_SAMPLE_PERIOD * (t_diff / EVT_SAMPLE_PERIOD);
306         else
307                 replace = 1;
308
309         if (!replace) {
310                 if (evtq->cnt >= SAMPLE_MAX)
311                         dq_evt(evtq);
312                 evtq->rear++;
313                 if (evtq->rear >= SAMPLE_MAX)
314                         evtq->rear -= SAMPLE_MAX;
315                 evtq->cnt++;
316         }
317
318         evtq->ei[evtq->rear].x = x;
319         evtq->ei[evtq->rear].timestamp = evtq->old_timestamp;
320 }
321
322 static inline Evas_Coord get_evt_avg(struct evt_queue *evtq)
323 {
324         int i;
325         int rear;
326         Evas_Coord x;
327         int weight;
328         int t;
329
330         t = (int)(ecore_time_get() * 1000);
331         rear = evtq->rear;
332
333         x = 0;
334         for (i = 0; i < evtq->cnt; i += weight) {
335                 weight = (t - evtq->ei[rear].timestamp) / EVT_SAMPLE_PERIOD;
336                 if (weight > (evtq->cnt - i))
337                         weight = evtq->cnt - i;
338                 else
339                         weight = 1;
340
341                 x += evtq->ei[rear].x * weight;
342                 t = evtq->ei[rear].timestamp;
343                 rear--;
344                 if (rear < 0)
345                         rear += SAMPLE_MAX;
346         }
347
348         x /= evtq->cnt;
349         return x;
350 }
351
352 /* Move the item to given direction to fit its coordinates to border */
353 static inline int calc_anim_dx_with_dir(struct widget_data *sc_data, int *dir)
354 {
355         Evas_Coord x, w;
356
357         LIST_ITEM_GEO_GET(sc_data->curlist, &x, NULL, &w, NULL);
358         sc_data->sc_anim_dx = 0;
359
360         if (*dir < 0) {
361                 DbgPrint("MOVE to LEFT\n");
362                 if (x < sc_data->clip_bx) {
363                         (*dir)++;
364
365                         if (sc_data->tolist == sc_data->item_list) {
366                                 if (!sc_data->is_loop) {
367                                         *dir = 0;
368                                         DbgPrint("Looping is disabled\n");
369                                         return -EINVAL;
370                                 }
371                         }
372
373                         sc_data->tolist = LIST_PREV(sc_data->tolist);
374                         sc_data->sc_anim_dx = sc_data->clip_bx - x /*- w*/;
375                 } else {
376                         sc_data->sc_anim_dx = sc_data->clip_bx - x;
377                 }
378         } else if (*dir > 0) {
379                 DbgPrint("MOVE to RIGHT\n");
380                 if (x < sc_data->clip_bx) {
381                         sc_data->sc_anim_dx = sc_data->clip_bx - x;
382                 } else if (x > sc_data->clip_bx) {
383                         struct item_list_entry *newlist;
384
385                         (*dir)--;
386                         newlist = LIST_NEXT(sc_data->tolist);
387                         if (newlist == sc_data->item_list) {
388                                 if (!sc_data->is_loop) {
389                                         *dir = 0;
390                                         DbgPrint("Looping is disabled\n");
391                                         return -EINVAL;
392                                 }
393                         }
394                         sc_data->tolist = newlist;
395                         sc_data->sc_anim_dx = sc_data->clip_bx - x; /*(sc_data->clip_bx + sc_data->clip_bw) - x;*/
396                 }
397         }
398
399         return 0;
400 }
401
402 static inline void move_item(struct widget_data *sc_data, struct item_list_entry *ilist, int x, int y, int w, int h)
403 {
404         struct live_sc_move_info info;
405
406         info.item = LIST_DATA(ilist);
407
408         info.relx = ((double)x - (double)sc_data->clip_bx) / (double)sc_data->clip_bw;
409
410         LIST_ITEM_GEO_SET(ilist, x, y, w, h);
411         info.x = x;
412         info.y = y;
413         info.w = w;
414         info.h = h;
415
416         evas_object_smart_callback_call(sc_data->scroller, "item,moved", &info);
417 }
418
419 static struct item_list_entry *update_items_geo(struct widget_data *sc_data, int dx)
420 {
421         Evas_Coord sx, sw;
422         Evas_Coord y, w, h;
423         struct item_list_entry *ilist;
424         struct item_list_entry *newlist;
425         struct item_list_entry *boundary;
426         int bx_bw;
427         register int x;
428
429         LIST_ITEM_GEO_GET(sc_data->curlist, &sx, &y, &sw, &h);
430
431         bx_bw = sc_data->clip_bx + sc_data->clip_bw;
432
433         sx += dx;
434         move_item(sc_data, sc_data->curlist, sx, y, sw, h);
435
436         newlist = NULL;
437
438         if (sc_data->item_cnt < 3) {
439                 ilist = LIST_NEXT(sc_data->curlist);
440                 LIST_ITEM_GEO_GET(ilist, NULL, &y, &w, &h);
441
442                 if (sx + sw < bx_bw) {
443                         x = sx + sw;
444                         move_item(sc_data, ilist, x, y, w, h);
445                         if (x == sc_data->clip_bx || (x < sc_data->clip_bx && (x + w) > sc_data->clip_bx))
446                                 newlist = ilist;
447                 } else if (sx > 0) {
448                         x = sx - w;
449                         move_item(sc_data, ilist, x, y, w, h);
450                         if (x == sc_data->clip_bx || (x > sc_data->clip_bx && x < bx_bw)) {
451                                 newlist = ilist;
452                         }
453                 }
454
455                 goto out;
456         }
457
458         x = sx;
459         boundary = NULL;
460         ilist = sc_data->curlist;
461         do {
462                 if (!sc_data->is_loop && ilist == sc_data->item_list)
463                         break;
464
465                 ilist = LIST_PREV(ilist);
466                 if (!ilist)
467                         break;
468
469                 LIST_ITEM_GEO_GET(ilist, NULL, &y, &w, &h);
470                 x -= w;
471                 move_item(sc_data, ilist, x, y, w, h);
472
473                 if (dx > 0 && !newlist) {
474                         if ((x == sc_data->clip_bx) || (x > sc_data->clip_bx && x < bx_bw))
475                                 newlist = ilist;
476                 }
477
478                 boundary = ilist;
479         } while (x > sc_data->clip_bx);
480
481         x = sx;
482         w = sw;
483         ilist = sc_data->curlist;
484         do {
485                 ilist = LIST_NEXT(ilist);
486                 if (!ilist || (!sc_data->is_loop && ilist == sc_data->item_list) || ilist == boundary)
487                         break;
488
489                 x += w;
490                 LIST_ITEM_GEO_GET(ilist, NULL, &y, &w, &h);
491                 move_item(sc_data, ilist, x, y, w, h);
492
493                 if (dx < 0 && !newlist) {
494                         if ((x == sc_data->clip_bx) || (x < sc_data->clip_bx && (x + w) > sc_data->clip_bx))
495                                 newlist = ilist;
496                 }
497         } while (x < bx_bw);
498
499 out:
500         if (newlist)
501                 sc_data->curlist = newlist;
502                 
503         return newlist;
504 }
505
506 static Eina_Bool emulate_evt(void *data)
507 {
508         PROFILE_START();
509         struct widget_data *sc_data;
510         Evas_Coord x;
511         Evas_Coord dx;
512         struct item_list_entry *newlist;
513
514         sc_data = data;
515
516         x = get_evt_avg(&sc_data->evtq);
517         if (x == sc_data->old_x) {
518                 PROFILE_END();
519                 return ECORE_CALLBACK_RENEW;
520         }
521         
522         dx = x - sc_data->old_x;
523         sc_data->old_x = x;
524
525         newlist = update_items_geo(sc_data, dx);
526         if (newlist) {
527                 int idx;
528
529                 idx = list_item_idx(sc_data, newlist);
530                 evas_object_smart_callback_call(sc_data->scroller, "page,changed", (void *)idx);
531         }
532         PROFILE_END();
533         return ECORE_CALLBACK_RENEW;
534 }
535
536 static void evt_mouse_down_cb(void *data, Evas *e, Evas_Object *evt_layer, void *event_info)
537 {
538         Evas_Event_Mouse_Down *down;
539         struct widget_data *sc_data;
540
541         sc_data = data;
542         down = event_info;
543
544         sc_data->is_pressed = EINA_TRUE;
545         sc_data->press_x = down->canvas.x;
546         sc_data->press_y = down->canvas.y;
547         sc_data->old_x = down->canvas.x;
548
549         if (!sc_data->is_freezed) {
550                 init_evtq(&sc_data->evtq);
551                 enq_evt(&sc_data->evtq, down->canvas.x, down->timestamp);
552         }
553 }
554
555 static void evt_mouse_up_cb(void *data, Evas *e, Evas_Object *evt_layer, void *event_info)
556 {
557         Evas_Event_Mouse_Up *up;
558         struct widget_data *sc_data;
559         struct live_sc_drag_info info;
560
561         sc_data = data;
562
563         if (!sc_data->is_pressed)
564                 return;
565
566         sc_data->is_pressed = EINA_FALSE;
567
568         if (sc_data->evt_emulator) {
569                 ecore_timer_del(sc_data->evt_emulator);
570                 sc_data->evt_emulator = NULL;
571         }
572
573         if (sc_data->drag_started == EINA_FALSE) {
574                 DbgPrint("drag is not started\n");
575                 return;
576         }
577
578         up = event_info;
579
580         info.dx = up->canvas.x - sc_data->press_x;
581         info.dy = up->canvas.y - sc_data->press_y;
582
583         sc_data->drag_started = EINA_FALSE;
584
585         evas_object_smart_callback_call(sc_data->scroller, "drag,stop", &info);
586 }
587
588 static void evt_mouse_move_cb(void *data, Evas *e, Evas_Object *evt_layer, void *event_info)
589 {
590         struct widget_data *sc_data;
591         Evas_Event_Mouse_Move *move;
592
593         sc_data = data;
594         
595         if (sc_data->is_pressed == EINA_FALSE)
596                 return;
597
598         if (sc_data->item_cnt <= 1)
599                 return;
600
601         if (sc_data->is_freezed)
602                 return;
603
604         move = event_info;
605
606         if (sc_data->drag_started == EINA_FALSE) {
607                 if (abs(move->cur.canvas.x - sc_data->press_x) < DRAG_SENS)
608                         return;
609
610                 if (sc_data->sc_anim_timer) {
611                         ecore_timer_del(sc_data->sc_anim_timer);
612                         sc_data->sc_anim_timer = NULL;
613                 }
614
615                 evas_object_smart_callback_call(sc_data->scroller, "drag,start", NULL);
616                 sc_data->drag_started = EINA_TRUE;
617         }
618
619         sc_data->prev_timestamp = move->timestamp;
620         enq_evt(&sc_data->evtq, move->cur.canvas.x, move->timestamp);
621         if (!sc_data->evt_emulator) {
622                 if (!sc_data->curlist)
623                         sc_data->curlist = sc_data->item_list;
624
625                 sc_data->evt_emulator = ecore_timer_add(EVT_PERIOD, emulate_evt, sc_data);
626         }
627 }
628
629 static inline int prepare_evt_layer(struct widget_data *sc_data)
630 {
631         Evas *e;
632
633         e = evas_object_evas_get(sc_data->scroller);
634         if (!e)
635                 return -EFAULT;
636
637         sc_data->evt_layer = evas_object_rectangle_add(e);
638         if (!sc_data->evt_layer)
639                 return -EFAULT;
640
641         evas_object_smart_member_add(sc_data->evt_layer, sc_data->scroller);
642
643         evas_object_color_set(sc_data->evt_layer, 255, 255, 255, 0);
644         evas_object_show(sc_data->evt_layer);
645         evas_object_repeat_events_set(sc_data->evt_layer, EINA_TRUE);
646
647         evas_object_event_callback_add(sc_data->evt_layer,
648                         EVAS_CALLBACK_MOUSE_DOWN, evt_mouse_down_cb, sc_data);
649
650         evas_object_event_callback_add(sc_data->evt_layer,
651                         EVAS_CALLBACK_MOUSE_UP, evt_mouse_up_cb, sc_data);
652
653         evas_object_event_callback_add(sc_data->evt_layer,
654                         EVAS_CALLBACK_MOUSE_MOVE, evt_mouse_move_cb, sc_data);
655
656         evas_object_clip_set(sc_data->evt_layer, sc_data->clip);
657         return 0;
658 }
659
660 static void live_add(Evas_Object *scroller)
661 {
662         struct widget_data *sc_data;
663         Evas *e;
664         int ret;
665
666         sc_data = calloc(1, sizeof(*sc_data));
667         if (!sc_data)
668                 return;
669
670         e = evas_object_evas_get(scroller);
671         if (!e) {
672                 free(sc_data);
673                 return;
674         }
675
676         evas_object_smart_data_set(scroller, sc_data);
677
678         sc_data->clip = evas_object_rectangle_add(e);
679         if (!sc_data->clip) {
680                 free(sc_data);
681                 return;
682         }
683
684         sc_data->is_pressed = EINA_FALSE;
685         sc_data->drag_started = EINA_FALSE;
686         sc_data->tolist = NULL;
687         sc_data->curlist = NULL;
688         sc_data->item_list = NULL;
689         sc_data->scroller = scroller;
690
691         evas_object_smart_member_add(sc_data->clip, sc_data->scroller);
692
693         ret = prepare_evt_layer(sc_data);
694         if (ret < 0) {
695                 evas_object_del(sc_data->clip);
696                 free(sc_data);
697         }
698
699         return;
700 }
701
702 static void live_del(Evas_Object *scroller)
703 {
704         struct widget_data *sc_data;
705         Evas_Object *item;
706         struct item_list_entry *ilist;
707         struct item_list_entry *next;
708
709         sc_data = evas_object_smart_data_get(scroller);
710         if (!sc_data) {
711                 ErrPrint("sc_data is not valid\n");
712                 return;
713         }
714
715         ilist = sc_data->item_list;
716         if (ilist) {
717                 do {
718                         next = LIST_NEXT(ilist);
719                         item = LIST_DATA(ilist);
720                         evas_object_clip_unset(item);
721                         evas_object_smart_member_del(item);
722                         free(ilist);
723                         ilist = next;
724                 } while (ilist != sc_data->item_list);
725         }
726
727         evas_object_del(sc_data->evt_layer);
728         evas_object_del(sc_data->clip);
729         free(sc_data);
730 }
731
732 static void live_move(Evas_Object *scroller, Evas_Coord bx, Evas_Coord by)
733 {
734         struct widget_data *sc_data;
735         Evas_Coord x, y, w, h;
736         Evas_Coord bw;
737
738         Evas_Coord dx;
739         Evas_Coord dy;
740
741         struct item_list_entry *n;
742
743         sc_data = evas_object_smart_data_get(scroller);
744         if (!sc_data) {
745                 ErrPrint("sc_data is not valid\n");
746                 return;
747         }
748
749         evas_object_geometry_get(sc_data->clip, &x, &y, &bw, NULL);
750
751         evas_object_move(sc_data->evt_layer, bx, by);
752         evas_object_move(sc_data->clip, bx, by);
753         sc_data->clip_bx = bx;
754         sc_data->clip_bw = bw;
755
756         dx = bx - x;
757         dy = by - y;
758
759         if (sc_data->item_list) {
760                 n = sc_data->item_list;
761                 do {
762                         evas_object_move(LIST_DATA(n), bx, by);
763
764                         LIST_ITEM_GEO_GET(n, &x, &y, &w, &h);
765                         x += dx;
766                         y += dy;
767                         move_item(sc_data, n, x, y, w, h);
768                         n = LIST_NEXT(n);
769                 } while (n != sc_data->item_list);
770         }
771 }
772
773 static void live_resize(Evas_Object *scroller, Evas_Coord w, Evas_Coord h)
774 {
775         struct widget_data *sc_data;
776
777         sc_data = evas_object_smart_data_get(scroller);
778         if (!sc_data) {
779                 ErrPrint("sc_data is not valid\n");
780                 return;
781         }
782
783         evas_object_resize(sc_data->clip, w, h);
784         evas_object_resize(sc_data->evt_layer, w, h);
785
786         sc_data->clip_bw = w;
787 }
788
789 static void live_show(Evas_Object *scroller)
790 {
791         struct widget_data *sc_data;
792
793         sc_data = evas_object_smart_data_get(scroller);
794         if (!sc_data) {
795                 ErrPrint("sc_data is not valid\n");
796                 return;
797         }
798
799         evas_object_show(sc_data->clip);
800 }
801
802 static void live_hide(Evas_Object *scroller)
803 {
804         struct widget_data *sc_data;
805
806         sc_data = evas_object_smart_data_get(scroller);
807         if (!sc_data) {
808                 ErrPrint("sc_data is not valid\n");
809                 return;
810         }
811
812         evas_object_hide(sc_data->clip);
813 }
814
815 static void live_set_color(Evas_Object *scroller, int r, int g, int b, int a)
816 {
817         struct widget_data *sc_data;
818
819         sc_data = evas_object_smart_data_get(scroller);
820         if (!sc_data) {
821                 ErrPrint("sc_data is not valid\n");
822                 return;
823         }
824
825         evas_object_color_set(sc_data->clip, r, g, b, a);
826 }
827
828 static void live_set_clip(Evas_Object *scroller, Evas_Object *clip)
829 {
830         struct widget_data *sc_data;
831
832         sc_data = evas_object_smart_data_get(scroller);
833         if (!sc_data) {
834                 ErrPrint("sc_data is not valid\n");
835                 return;
836         }
837
838         evas_object_clip_set(sc_data->clip, clip);
839 }
840
841 static void live_unset_clip(Evas_Object *scroller)
842 {
843         struct widget_data *sc_data;
844
845         sc_data = evas_object_smart_data_get(scroller);
846         if (!sc_data) {
847                 ErrPrint("sc_data is not valid\n");
848                 return;
849         }
850
851         evas_object_clip_unset(sc_data->clip);
852 }
853
854 static inline void rearrange_items(struct widget_data *sc_data)
855 {
856         struct item_list_entry *ilist;
857         Evas_Coord x, y, w, h;
858         Evas_Coord sw;
859
860         LIST_ITEM_GEO_GET(sc_data->curlist, NULL, &y, &sw, &h);
861         move_item(sc_data, sc_data->curlist, sc_data->clip_bx, y, sw, h);
862
863         x = sc_data->clip_bx;
864         ilist = sc_data->curlist;
865         while (ilist != sc_data->item_list) {
866                 ilist = LIST_PREV(ilist);
867                 LIST_ITEM_GEO_GET(ilist, NULL, &y, &w, &h);
868                 x -= w;
869                 move_item(sc_data, ilist, x, y, w, h);
870         }
871
872         w = sw;
873         x = sc_data->clip_bx;
874         ilist = LIST_NEXT(sc_data->curlist);
875         while (ilist != sc_data->item_list) {
876                 x += w;
877                 LIST_ITEM_GEO_GET(ilist, NULL, &y, &w, &h);
878                 move_item(sc_data, ilist, x, y, w, h);
879                 ilist = LIST_NEXT(ilist);
880         }
881 }
882
883 static Eina_Bool sc_anim_cb(void *data)
884 {
885         PROFILE_START();
886         struct widget_data *sc_data;
887         Evas_Coord sx, sw;
888         Evas_Coord y;
889         Evas_Coord dx;
890         struct item_list_entry *ilist;
891
892         sc_data = data;
893
894         if (!sc_data->curlist || !sc_data->tolist) {
895                 DbgPrint("cur_list: %p, tolist: %p\n", sc_data->curlist, sc_data->tolist);
896                 goto clean_out;
897         }
898
899         ilist = sc_data->curlist;
900         if (sc_data->curlist != sc_data->tolist) {
901                 if (sc_data->sc_anim_dx > 0)
902                         ilist = LIST_PREV(ilist);
903                 else
904                         ilist = LIST_NEXT(ilist);
905         }
906
907         LIST_ITEM_GEO_GET(ilist, &sx, &y, &sw, NULL);
908         if (ilist == sc_data->tolist) {
909                 DbgPrint("next list == tolist\n");
910                 dx = abs(sx - sc_data->clip_bx);
911                 DbgPrint("sx: %d, clip_bx: %d --> %d\n", sx, sc_data->clip_bx, dx);
912                 if (dx < abs(sc_data->sc_anim_dx)) {
913                         if (sc_data->sc_anim_dx < 0)
914                                 dx = -dx;
915                 } else {
916                         dx = sc_data->sc_anim_dx;
917                 }
918         } else {
919                 dx = sc_data->sc_anim_dx;
920                 DbgPrint("dx: %d\n", dx);
921         }
922
923         if (!dx) {
924                 DbgPrint("dx is 0\n");
925                 goto clean_out;
926         }
927
928         ilist = update_items_geo(sc_data, dx);
929         if (ilist) {
930                 int idx;
931
932                 idx = list_item_idx(sc_data, ilist);
933                 evas_object_smart_callback_call(sc_data->scroller,"page,changed", (void *)idx);
934         }
935         PROFILE_END();
936         return ECORE_CALLBACK_RENEW;
937
938 clean_out:
939         PROFILE_END();
940         evas_object_smart_callback_call(sc_data->scroller, "anim,stop", NULL);
941         sc_data->sc_anim_timer = NULL;
942         return ECORE_CALLBACK_CANCEL;
943 }
944
945 Evas_Object *live_scroller_add(Evas_Object *parent)
946 {
947         static Evas_Smart_Class sc = EVAS_SMART_CLASS_INIT_NAME_VERSION("live,scroller");
948         static Evas_Smart *smart = NULL;
949         Evas_Object *scroller;
950         Evas *e;
951
952         if (!parent)
953                 return NULL;
954
955         e = evas_object_evas_get(parent);
956         if (!e)
957                 return NULL;
958
959         if (!smart) {
960                 sc.add = live_add;
961                 sc.del = live_del;
962                 sc.move = live_move;
963                 sc.resize = live_resize;
964                 sc.show = live_show;
965                 sc.hide = live_hide;
966                 sc.color_set = live_set_color;
967                 sc.clip_set = live_set_clip;
968                 sc.clip_unset = live_unset_clip;
969
970                 smart = evas_smart_class_new(&sc);
971         }
972
973         scroller = evas_object_smart_add(e, smart);
974
975         return scroller;
976 }
977
978 int live_scroller_append(Evas_Object *scroller, Evas_Object *item)
979 {
980         Evas_Coord x, y, w, h;
981         Evas_Coord bx, by, bw, bh;
982         struct widget_data *sc_data;
983         struct item_list_entry *tmplist;
984         Evas_Coord start_x = 0;
985         Evas_Coord start_w = 0;
986
987         sc_data = evas_object_smart_data_get(scroller);
988         if (!sc_data) {
989                 ErrPrint("sc_data is not valid\n");
990                 return -EINVAL;
991         }
992
993         evas_object_geometry_get(sc_data->clip, &bx, &by, &bw, &bh);
994
995         if (sc_data->item_list)
996                 LIST_ITEM_GEO_GET(sc_data->item_list, &start_x, NULL, &start_w, NULL);
997
998         tmplist = list_item_last_list(sc_data->item_list);
999         if (tmplist) {
1000                 LIST_ITEM_GEO_GET(tmplist, &x, NULL, &w, NULL);
1001                 if (x + w == start_x) {
1002                         x = start_x - w;
1003                 } else {
1004                         x += w;
1005                 }
1006         } else {
1007                 x = bx;
1008         }
1009         
1010         evas_object_geometry_get(item, NULL, NULL, &w, &h);
1011         evas_object_smart_member_add(item, sc_data->scroller);
1012
1013         y = by + ((bh - h) >> 1);
1014
1015         tmplist = list_item_append(sc_data->item_list, item);
1016         if (sc_data->item_list == sc_data->curlist)
1017                 sc_data->curlist = tmplist;
1018         if (sc_data->item_list == sc_data->tolist)
1019                 sc_data->tolist = tmplist;
1020         sc_data->item_list = tmplist;
1021
1022         sc_data->item_cnt++;
1023         evas_object_clip_set(item, sc_data->clip);
1024         evas_object_stack_below(item, sc_data->clip);
1025
1026         evas_object_move(item, bx, by);
1027         move_item(sc_data, list_item_find(sc_data->item_list, item), x, y, w, h);
1028
1029         return 0;
1030 }
1031
1032 int live_scroller_remove_by_obj(Evas_Object *scroller, Evas_Object *obj)
1033 {
1034         struct widget_data *sc_data;
1035         struct item_list_entry *tmplist;
1036         Evas_Object *item;
1037
1038         sc_data = evas_object_smart_data_get(scroller);
1039         if (!sc_data) {
1040                 ErrPrint("sc_data is not valid\n");
1041                 return -EINVAL;
1042         }
1043
1044         if (sc_data->curlist) {
1045                 if (LIST_DATA(sc_data->curlist) == obj) {
1046                         sc_data->curlist = sc_data->item_list;
1047                         DbgPrint("Reset curlist\n");
1048                 }
1049         }
1050
1051         if (sc_data->tolist) {
1052                 if (LIST_DATA(sc_data->tolist) == obj) {
1053                         sc_data->tolist = sc_data->item_list;
1054                         DbgPrint("Reset tolist\n");
1055                 }
1056         }
1057
1058         tmplist = list_item_remove(sc_data->item_list, obj);
1059         if (sc_data->item_list == sc_data->curlist) {
1060                 sc_data->curlist = tmplist;
1061                 DbgPrint("Update curlist\n");
1062         }
1063         if (sc_data->item_list == sc_data->tolist) {
1064                 sc_data->tolist = tmplist;
1065                 DbgPrint("Update tolist\n");
1066         }
1067         sc_data->item_list = tmplist;
1068
1069         sc_data->item_cnt--;
1070         evas_object_clip_unset(obj);
1071         evas_object_smart_member_del(obj);
1072         DbgPrint("Count of items: %d\n", sc_data->item_cnt);
1073
1074         item = LIST_DATA(sc_data->curlist);
1075         if (item) {
1076                 Evas_Coord y, w, h;
1077                 int idx;
1078
1079                 LIST_ITEM_GEO_GET(sc_data->curlist, NULL, &y, &w, &h);
1080                 DbgPrint("Current GEO: %d, %dx%d\n", y, w, h);
1081                 LIST_ITEM_GEO_SET(sc_data->curlist, sc_data->clip_bx, y, w, h);
1082                 DbgPrint("sc_data->curlist : %p\n", sc_data->curlist);
1083                 update_items_geo(sc_data, 0);
1084                 DbgPrint("sc_data->curlist : %p\n", sc_data->curlist);
1085
1086                 idx = list_item_idx(sc_data, sc_data->curlist);
1087                 evas_object_smart_callback_call(sc_data->scroller, "page,changed", (void *)idx);
1088         }
1089
1090         return 0;
1091 }
1092
1093 Evas_Object *live_scroller_remove(Evas_Object *scroller, int idx)
1094 {
1095         struct widget_data *sc_data;
1096         struct item_list_entry *tmplist;
1097         Evas_Object *ret;
1098         Evas_Object *item;
1099
1100         sc_data = evas_object_smart_data_get(scroller);
1101         if (!sc_data) {
1102                 ErrPrint("sc_data is not valid\n");
1103                 return NULL;
1104         }
1105
1106         if (idx < 0 || idx >= sc_data->item_cnt)
1107                 return NULL;
1108
1109         ret = list_item_nth(sc_data->item_list, idx);
1110         if (!ret)
1111                 return NULL;
1112
1113         if (list_item_nth_list(sc_data->item_list, idx) == sc_data->curlist) {
1114                 DbgPrint("Reset curlist\n");
1115                 sc_data->curlist = sc_data->item_list;
1116         }
1117
1118         if (list_item_nth_list(sc_data->item_list, idx) == sc_data->tolist) {
1119                 DbgPrint("Reset tolist\n");
1120                 sc_data->tolist = sc_data->item_list;
1121         }
1122
1123         tmplist = list_item_remove(sc_data->item_list, ret);
1124         if (sc_data->item_list == sc_data->curlist)
1125                 sc_data->curlist = tmplist;
1126         if (sc_data->item_list == sc_data->tolist)
1127                 sc_data->tolist = tmplist;
1128         sc_data->item_list = tmplist;
1129
1130         sc_data->item_cnt--;
1131         evas_object_clip_unset(ret);
1132         evas_object_smart_member_del(ret);
1133
1134         item = LIST_DATA(sc_data->curlist);
1135         if (item) {
1136                 Evas_Coord y, w, h;
1137                 int idx;
1138                 LIST_ITEM_GEO_GET(sc_data->curlist, NULL, &y, &w, &h);
1139                 LIST_ITEM_GEO_SET(sc_data->curlist, sc_data->clip_bx, y, w, h);
1140                 update_items_geo(sc_data, 0);
1141                 idx = list_item_idx(sc_data, sc_data->curlist);
1142                 evas_object_smart_callback_call(sc_data->scroller, "page,changed", (void *)idx);
1143         }
1144         return ret;
1145 }
1146
1147 Evas_Object *live_scroller_get_item(Evas_Object *scroller, int idx)
1148 {
1149         struct widget_data *sc_data;
1150
1151         sc_data = evas_object_smart_data_get(scroller);
1152         if (!sc_data) {
1153                 ErrPrint("sc_data is not valid\n");
1154                 return NULL;
1155         }
1156
1157         if (idx < 0 || idx >= sc_data->item_cnt)
1158                 return NULL;
1159
1160         return list_item_nth(sc_data->item_list, idx);
1161 }
1162
1163 int live_scroller_get_current(Evas_Object *scroller)
1164 {
1165         struct widget_data *sc_data;
1166         struct item_list_entry *ilist;
1167         int idx;
1168
1169         sc_data = evas_object_smart_data_get(scroller);
1170         if (!sc_data) {
1171                 ErrPrint("sc_data is not valid\n");
1172                 return -EINVAL;
1173         }
1174
1175         ilist = sc_data->curlist;
1176         idx = 0;
1177         if (ilist) {
1178                 while (ilist != sc_data->item_list) {
1179                         idx++;
1180                         ilist = LIST_PREV(ilist);
1181                 }
1182         }
1183
1184         return idx;
1185 }
1186
1187 int live_scroller_loop_set(Evas_Object *scroller, int is_loop)
1188 {
1189         struct widget_data *sc_data;
1190
1191         sc_data = evas_object_smart_data_get(scroller);
1192         if (!sc_data) {
1193                 ErrPrint("sc_data is not valid\n");
1194                 return -EINVAL;
1195         }
1196
1197         if (is_loop == EINA_FALSE && sc_data->is_loop == EINA_TRUE)
1198                 rearrange_items(sc_data);
1199
1200         sc_data->is_loop = is_loop;
1201         return 0;
1202 }
1203
1204 int live_scroller_freeze(Evas_Object *scroller)
1205 {
1206         struct widget_data *sc_data;
1207
1208         sc_data = evas_object_smart_data_get(scroller);
1209         if (!sc_data) {
1210                 ErrPrint("sc_data is not valid\n");
1211                 return -EINVAL;
1212         }
1213
1214         sc_data->is_freezed = EINA_TRUE;
1215         return 0;
1216 }
1217
1218 int live_scroller_thaw(Evas_Object *scroller)
1219 {
1220         struct widget_data *sc_data;
1221
1222         sc_data = evas_object_smart_data_get(scroller);
1223         if (!sc_data) {
1224                 ErrPrint("sc_data is not valid\n");
1225                 return -EINVAL;
1226         }
1227
1228         sc_data->is_freezed = EINA_FALSE;
1229         return 0;
1230 }
1231
1232 int live_scroller_anim_to(Evas_Object *scroller, double sec, int offset)
1233 {
1234         PROFILE_START();
1235         struct widget_data *sc_data;
1236         Evas_Coord sx, sw;
1237         Evas_Coord y;
1238         struct live_sc_event_info info;
1239         struct item_list_entry *ilist;
1240         double ftmp;
1241         int ret;
1242
1243         sc_data = evas_object_smart_data_get(scroller);
1244         if (!sc_data) {
1245                 ErrPrint("sc_data is not valid\n");
1246                 ret = -EINVAL;
1247                 goto out;
1248         }
1249         
1250         if (sc_data->is_freezed) {
1251                 ErrPrint("Scroller is freezed\n");
1252                 ret = -EBUSY;
1253                 goto out;
1254         }
1255
1256         if (!sc_data->curlist)
1257                 sc_data->curlist = sc_data->item_list;
1258
1259         if (!sc_data->curlist) {
1260                 ErrPrint("List is empty\n");
1261                 ret = -ENOENT;
1262                 goto out;
1263         }
1264
1265         if (sc_data->sc_anim_timer) {
1266                 ecore_timer_del(sc_data->sc_anim_timer);
1267                 sc_data->sc_anim_timer = NULL;
1268                 evas_object_smart_callback_call(sc_data->scroller, "anim,stop", NULL);
1269         } else {
1270                 sc_data->tolist = sc_data->curlist;
1271         }
1272
1273         LIST_ITEM_GEO_GET(sc_data->curlist, &sx, &y, &sw, NULL);
1274
1275         if (!offset) {
1276                 sc_data->sc_anim_dx = sc_data->clip_bx - sx;
1277                 DbgPrint("offset==0, dx: %d\n", sc_data->sc_anim_dx);
1278         } else {
1279                 Evas_Coord tw;
1280                 struct item_list_entry *tmplist;
1281
1282                 calc_anim_dx_with_dir(sc_data, &offset);
1283                 DbgPrint("Offset: %d\n", offset);
1284
1285                 ilist = sc_data->curlist;
1286                 while (offset < 0) {
1287                         if (!sc_data->is_loop && ilist == sc_data->item_list) {
1288                                 DbgPrint("Loop is disabled\n");
1289                                 break;
1290                         }
1291
1292                         LIST_ITEM_GEO_GET(ilist, NULL, NULL, &tw, NULL);
1293                         ilist = LIST_PREV(ilist);
1294
1295                         sc_data->sc_anim_dx += tw;
1296                         DbgPrint("tw: %d (%d)\n", tw, sc_data->sc_anim_dx);
1297
1298                         offset++;
1299                         if (sc_data->tolist == sc_data->item_list) {
1300                                 if (!sc_data->is_loop) {
1301                                         DbgPrint("Looping disabled\n");
1302                                         break;
1303                                 }
1304                         }
1305                         sc_data->tolist = LIST_PREV(sc_data->tolist);
1306                 }
1307                 
1308                 while (offset > 0) {
1309                         LIST_ITEM_GEO_GET(ilist, NULL, NULL, &tw, NULL);
1310                         ilist = LIST_NEXT(ilist);
1311
1312                         sc_data->sc_anim_dx -= tw;
1313                         DbgPrint("tw: %d (%d)\n", tw, sc_data->sc_anim_dx);
1314
1315                         offset--;
1316                         tmplist = LIST_NEXT(sc_data->tolist);
1317                         if (tmplist == sc_data->item_list) {
1318                                 if (!sc_data->is_loop) {
1319                                         DbgPrint("Looping disabled\n");
1320                                         break;
1321                                 }
1322                         }
1323                         sc_data->tolist = tmplist;
1324
1325                         if (!sc_data->is_loop && ilist == sc_data->item_list) {
1326                                 DbgPrint("Destination arrived or loop is disabled");
1327                                 break;
1328                         }
1329                 }
1330         }
1331
1332         if (abs(sc_data->sc_anim_dx) > ANIM_MIN) {
1333                 ftmp = (double)sc_data->sc_anim_dx / ANIM_UNIT;
1334                 DbgPrint("ftmp: %lf\n", ftmp);
1335                 if (fabs(ftmp) < ANIM_MIN || fabs(ftmp) > abs(sc_data->sc_anim_dx))
1336                         sc_data->sc_anim_dx = ftmp < 0 ? -ANIM_MIN : ANIM_MIN;
1337                 else
1338                         sc_data->sc_anim_dx = (int)ftmp;
1339                 DbgPrint("Result: %d\n", sc_data->sc_anim_dx);
1340         }
1341
1342         sc_data->sc_anim_timer = ecore_timer_add(sec, sc_anim_cb, sc_data);
1343         if (!sc_data->sc_anim_timer) {
1344                 ErrPrint("Failed to add a animator\n");
1345                 ret = -EFAULT;
1346                 goto out;
1347         }
1348
1349         info.curidx = list_item_idx(sc_data, sc_data->curlist);
1350         info.toidx = list_item_idx(sc_data, sc_data->tolist);
1351         DbgPrint("Current index: %d, To index: %d\n", info.curidx, info.toidx);
1352
1353         evas_object_smart_callback_call(sc_data->scroller, "anim,start", &info);
1354         ret = 0;
1355
1356 out:
1357         PROFILE_END();
1358         return ret;
1359 }
1360
1361 int live_scroller_go_to(Evas_Object *scroller, int idx)
1362 {
1363         struct widget_data *sc_data;
1364
1365         sc_data = evas_object_smart_data_get(scroller);
1366         if (!sc_data) {
1367                 ErrPrint("sc_data is not valid\n");
1368                 return -EINVAL;
1369         }
1370
1371         if (sc_data->is_freezed)
1372                 return -EBUSY;
1373
1374         if (idx < 0 || idx >= sc_data->item_cnt)
1375                 return -EINVAL;
1376
1377         sc_data->curlist = list_item_nth_list(sc_data->item_list, idx);
1378         if (!sc_data->curlist)
1379                 return -EFAULT;
1380
1381         rearrange_items(sc_data);
1382         evas_object_smart_callback_call(sc_data->scroller, "page,changed", (void *)idx);
1383
1384         return 0;
1385 }
1386
1387 int live_scroller_update(Evas_Object *scroller)
1388 {
1389         struct widget_data *sc_data;
1390         struct item_list_entry *n;
1391         Evas_Object *item;
1392         Evas_Coord x, y, w, h;
1393
1394         sc_data = evas_object_smart_data_get(scroller);
1395         if (!sc_data) {
1396                 ErrPrint("sc_data is not valid\n");
1397                 return -EINVAL;
1398         }
1399
1400         if (sc_data->item_list) {
1401                 n = sc_data->item_list;
1402                 do {
1403                         item = LIST_DATA(n);
1404                         LIST_ITEM_GEO_GET(n, &x, &y, &w, &h);
1405                         move_item(sc_data, n, x, y, w, h);
1406                         n = LIST_NEXT(n);
1407                 } while (n != sc_data->item_list);
1408         }
1409
1410         return 0;
1411 }
1412
1413 int live_scroller_get_item_count(Evas_Object *scroller)
1414 {
1415         struct widget_data *sc_data;
1416
1417         sc_data = evas_object_smart_data_get(scroller);
1418         if (!sc_data) {
1419                 ErrPrint("sc_data is not valid\n");
1420                 return 0;
1421         }
1422
1423         return list_item_count(sc_data->item_list);
1424 }
1425
1426 int live_scroller_get_item_index(Evas_Object *scroller, Evas_Object *item)
1427 {
1428         struct widget_data *sc_data;
1429         struct item_list_entry *n;
1430         Evas_Object *tmp;
1431         int idx;
1432
1433         sc_data = evas_object_smart_data_get(scroller);
1434         if (!sc_data) {
1435                 ErrPrint("sc_data is not valid\n");
1436                 return -EINVAL;
1437         }
1438
1439         if (!sc_data->item_list)
1440                 return -ENOENT;
1441
1442         idx = 0;
1443         n = sc_data->item_list;
1444         do {
1445                 tmp = LIST_DATA(n);
1446                 n = LIST_NEXT(n);
1447
1448                 if (tmp == item)
1449                         return idx;
1450
1451                 idx++;
1452         } while (n != sc_data->item_list);
1453
1454         return -ENOENT;
1455 }
1456
1457 /* End of a file */