fa327b44612aee458f244d368a4b566ee68647f7
[framework/uifw/elementary.git] / src / lib / elm_flipselector.c
1 #include <Elementary.h>
2 #include "elm_priv.h"
3
4 /* TODO: ideally, the default theme would use map{} blocks on the TEXT
5    parts to implement their fading in/out propertly (as in the clock
6    widget) */
7 /* TODO: if one ever wants to extend it to receiving generic widgets
8    as items, be my guest. in this case, remember to implement the
9    items tooltip infra. */
10 /* TODO: implement disabled mode -- disable_hook() and stuff. */
11 /* TODO: fix default theme image borders for looong strings as item
12    labels. */
13 /* TODO: set text elipsis on labels if one enforces mininum size on
14  * the overall widget less the required for displaying it. */
15 /* TODO: find a way to, in the default theme, to detect we are
16  * bootstrapping (receiving the 1st message) and populate the downmost
17  * TEXT parts with the same text as the upmost, where appropriate. */
18
19 #define FLIP_FIRST_INTERVAL (0.85)
20 #define FLIP_MIN_INTERVAL (0.1)
21 #define MSG_FLIP_DOWN (1)
22 #define MSG_FLIP_UP (2)
23 #define MAX_LEN_DEFAULT (50)
24
25 #define DATA_GET eina_list_data_get
26
27 struct _Elm_Flipselector_Item
28 {
29    ELM_WIDGET_ITEM;
30    const char *label;
31    Evas_Smart_Cb func;
32    void *data;
33    int deleted : 1;
34 };
35
36 typedef struct _Widget_Data Widget_Data;
37 typedef struct _Elm_Flipselector_Item Elm_Flipselector_Item;
38
39 struct _Widget_Data
40 {
41    Evas_Object *self;
42    Evas_Object *base;
43    Eina_List *items;
44    Eina_List *current;
45    Eina_List *sentinel; /* item containing the largest label string */
46    int walking;
47    unsigned int max_len;
48    Ecore_Timer *spin;
49    double interval, first_interval;
50 };
51
52 static const char *widtype = NULL;
53 static void _del_hook(Evas_Object *obj);
54 static void _theme_hook(Evas_Object *obj);
55 static void _sizing_eval(Evas_Object *obj);
56 static void _update_view(Evas_Object *obj);
57 static void _callbacks_set(Evas_Object *obj);
58 static void _flip_up(Widget_Data *wd);
59 static void _flip_down(Widget_Data *wd);
60
61 static const char SIG_SELECTED[] = "selected";
62 static const char SIG_UNDERFLOWED[] = "underflowed";
63 static const char SIG_OVERFLOWED[] = "overflowed";
64 static const Evas_Smart_Cb_Description _signals[] = {
65   {SIG_SELECTED, ""},
66   {SIG_UNDERFLOWED, ""},
67   {SIG_OVERFLOWED, ""},
68   {NULL, NULL}
69 };
70
71 static void
72 _item_text_set_hook(Elm_Object_Item *it,
73                     const char *part,
74                     const char *label)
75 {
76    ELM_OBJ_ITEM_CHECK_OR_RETURN(it);
77
78    Widget_Data *wd;
79    Eina_List *l;
80    Elm_Flipselector_Item *item;
81
82    if (!label) return;
83
84    if (part && strcmp(part ,"default")) return;
85
86    item = (Elm_Flipselector_Item *) it;
87    wd = elm_widget_data_get(WIDGET(item));
88    if ((!wd) || (!wd->items)) return;
89
90    l = eina_list_data_find_list(wd->items, item);
91    if (!l) return;
92
93    eina_stringshare_del(item->label);
94    item->label = eina_stringshare_add_length(label, wd->max_len);
95
96    if (strlen(label) > strlen(elm_object_item_text_get(DATA_GET(wd->sentinel))))
97      wd->sentinel = l;
98
99    if (wd->current == l)
100      {
101         _update_view(WIDGET(item));
102         _sizing_eval(wd->self);
103      }
104 }
105
106 static const char *
107 _item_text_get_hook(const Elm_Object_Item *it, const char *part)
108 {
109    ELM_OBJ_ITEM_CHECK_OR_RETURN(it, NULL);
110
111    Elm_Flipselector_Item *item, *_item;
112    Widget_Data *wd;
113    Eina_List *l;
114
115    if (part && strcmp(part ,"default")) return NULL;
116
117    item = (Elm_Flipselector_Item *) it;
118    wd = elm_widget_data_get(WIDGET(item));
119    if ((!wd) || (!wd->items)) return NULL;
120
121    EINA_LIST_FOREACH(wd->items, l, _item)
122      if (_item == item) return item->label;
123    return NULL;
124 }
125
126 static void
127 _item_signal_emit_hook(Elm_Object_Item *it,
128                        const char *emission,
129                        const char *source)
130 {
131    ELM_OBJ_ITEM_CHECK_OR_RETURN(it);
132    Elm_Flipselector_Item *item = (Elm_Flipselector_Item *) it;
133    edje_object_signal_emit(VIEW(item), emission, source);
134 }
135
136 static Elm_Flipselector_Item *
137 _item_new(Evas_Object *obj, const char *label, Evas_Smart_Cb func, const void *data)
138 {
139    unsigned int len;
140    Elm_Flipselector_Item *it;
141    Widget_Data *wd = elm_widget_data_get(obj);
142
143    it = elm_widget_item_new(obj, Elm_Flipselector_Item);
144    if (!it) return NULL;
145
146    elm_widget_item_text_set_hook_set(it, _item_text_set_hook);
147    elm_widget_item_text_get_hook_set(it, _item_text_get_hook);
148    elm_widget_item_signal_emit_hook_set(it, _item_signal_emit_hook);
149
150    len = strlen(label);
151    if (len > wd->max_len)
152      len = wd->max_len;
153
154    it->label = eina_stringshare_add_length(label, len);
155    it->func = func;
156    it->base.data = data;
157
158    /* TODO: no view here, but if one desires general contents in the
159     * future... */
160    return it;
161 }
162
163 static inline void
164 _item_free(Elm_Flipselector_Item *it)
165 {
166    eina_stringshare_del(it->label);
167    elm_widget_item_del(it);
168 }
169
170 static void
171 _del_hook(Evas_Object *obj)
172 {
173    Elm_Flipselector_Item *item;
174
175    Widget_Data *wd = elm_widget_data_get(obj);
176    if (!wd) return;
177
178    if (wd->walking) ERR("flipselector deleted while walking.\n");
179
180    EINA_LIST_FREE(wd->items, item)
181      _item_free(item);
182
183    if (wd->spin) ecore_timer_del(wd->spin);
184    free(wd);
185 }
186
187 static void
188 _theme_hook(Evas_Object *obj)
189 {
190    Widget_Data *wd;
191    const char *max_len;
192
193    wd = elm_widget_data_get(obj);
194    if (!wd) return;
195
196    _elm_theme_object_set(obj, wd->base, "flipselector", "base",
197                          elm_widget_style_get(obj));
198    edje_object_scale_set(wd->base,
199                          elm_widget_scale_get(obj) * _elm_config->scale);
200
201    max_len = edje_object_data_get(wd->base, "max_len");
202    if (!max_len)
203      wd->max_len = MAX_LEN_DEFAULT;
204    else
205      {
206         wd->max_len = atoi(max_len);
207         if (!wd->max_len)
208           wd->max_len = MAX_LEN_DEFAULT;
209      }
210
211    _update_view(obj);
212    _sizing_eval(obj);
213 }
214
215 static void
216 _sentinel_eval(Widget_Data *wd)
217 {
218    Elm_Flipselector_Item *it;
219    Eina_List *l;
220
221    if (!wd->items)
222      {
223         wd->sentinel = NULL;
224         return;
225      }
226
227    wd->sentinel = wd->items;
228
229    EINA_LIST_FOREACH(wd->items, l, it)
230      {
231         if (strlen(elm_object_item_text_get((Elm_Object_Item *) it)) >
232             strlen(elm_object_item_text_get(DATA_GET(wd->sentinel))))
233           wd->sentinel = l;
234      }
235 }
236
237 /* TODO: create a flag to avoid looping here all times */
238 static void
239 _flipselector_process_deletions(Widget_Data *wd)
240 {
241    Elm_Flipselector_Item *it;
242    Eina_List *l;
243    Eina_Bool skip = EINA_TRUE;
244    Eina_Bool sentinel_eval = EINA_FALSE;
245
246    wd->walking++; /* avoid nested deletions */
247
248    EINA_LIST_FOREACH(wd->items, l, it)
249      {
250         if (!it->deleted) continue;
251
252         if (wd->current == l)
253           {
254              if (wd->current == wd->sentinel)
255                sentinel_eval = EINA_TRUE;
256              wd->current = eina_list_prev(wd->current);
257           }
258         wd->items = eina_list_remove(wd->items, it);
259
260         if (!wd->current)
261           wd->current = wd->items;
262
263         _item_free(it);
264         skip = EINA_FALSE;
265
266         if (eina_list_count(wd->items) <= 1)
267           edje_object_signal_emit(wd->base, "elm,state,button,hidden", "elm");
268         else
269           edje_object_signal_emit(wd->base, "elm,state,button,visible", "elm");
270      }
271
272    if (!skip)
273      _update_view(wd->self);
274
275    if (sentinel_eval)
276      _sentinel_eval(wd);
277
278    wd->walking--;
279 }
280
281 static inline void
282 _flipselector_walk(Widget_Data *wd)
283 {
284    if (wd->walking < 0)
285      {
286         ERR("walking was negative. fixed!\n");
287         wd->walking = 0;
288      }
289    wd->walking++;
290 }
291
292 static inline void
293 _flipselector_unwalk(Widget_Data *wd)
294 {
295    wd->walking--;
296    if (wd->walking < 0)
297      {
298         ERR("walking became negative. fixed!\n");
299         wd->walking = 0;
300      }
301    if (wd->walking) return;
302
303    _flipselector_process_deletions(wd);
304 }
305
306 static Eina_Bool
307 _event_hook(Evas_Object *obj, Evas_Object *src __UNUSED__, Evas_Callback_Type type, void *event_info)
308 {
309    Evas_Event_Key_Down *ev;
310    Widget_Data *wd;
311    Eina_Bool is_up = EINA_TRUE;
312
313    if (type != EVAS_CALLBACK_KEY_DOWN) return EINA_FALSE;
314
315    wd = elm_widget_data_get(obj);
316    if (!wd) return EINA_FALSE;
317
318    ev = event_info;
319    if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return EINA_FALSE;
320
321    if (elm_widget_disabled_get(obj)) return EINA_FALSE;
322
323    if ((!strcmp(ev->keyname, "Down")) || (!strcmp(ev->keyname, "KP_Down")))
324      is_up = EINA_FALSE;
325    else if ((strcmp(ev->keyname, "Up")) && (strcmp(ev->keyname, "KP_Up")))
326      return EINA_FALSE;
327
328    if (wd->spin) ecore_timer_del(wd->spin);
329
330    /* TODO: if direction setting via API is not coming in, replace
331       these calls by flip_{next,prev} */
332    _flipselector_walk(wd);
333    if (is_up) _flip_up(wd);
334    else _flip_down(wd);
335    _flipselector_unwalk(wd);
336
337    ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
338    return EINA_TRUE;
339 }
340
341 static void
342 _on_focus_hook(void *data __UNUSED__, Evas_Object *obj)
343 {
344    Widget_Data *wd = elm_widget_data_get(obj);
345    if (!wd) return;
346
347    /* FIXME: no treatment of this signal so far */
348    if (elm_widget_focus_get(obj))
349      {
350         edje_object_signal_emit(wd->base, "elm,action,focus", "elm");
351         evas_object_focus_set(wd->base, EINA_TRUE);
352      }
353    else
354      {
355         edje_object_signal_emit(wd->base, "elm,action,unfocus", "elm");
356         evas_object_focus_set(wd->base, EINA_FALSE);
357      }
358 }
359
360 static void
361 _sizing_eval(Evas_Object *obj)
362 {
363    Widget_Data *wd;
364    const char *tmp = NULL;
365    Evas_Coord minw = -1, minh = -1, w, h;
366
367    wd = elm_widget_data_get(obj);
368    if (!wd) return;
369
370    elm_coords_finger_size_adjust(1, &minw, 2, &minh);
371
372    if (wd->sentinel)
373      {
374         const char *label = elm_object_item_text_get(DATA_GET(wd->sentinel));
375         tmp = edje_object_part_text_get(wd->base, "top");
376         edje_object_part_text_set(wd->base, "top", label);
377      }
378
379    edje_object_size_min_restricted_calc(wd->base, &minw, &minh, minw, minh);
380    elm_coords_finger_size_adjust(1, &minw, 2, &minh);
381    evas_object_size_hint_min_get(obj, &w, &h);
382
383    if (wd->sentinel) edje_object_part_text_set(wd->base, "top", tmp);
384
385    if (w > minw) minw = w;
386    if (h > minh) minh = h;
387
388    evas_object_size_hint_min_set(obj, minw, minh);
389 }
390
391 static void
392 _update_view(Evas_Object *obj)
393 {
394    Widget_Data *wd;
395    const char *label;
396    Elm_Flipselector_Item *item;
397
398    wd = elm_widget_data_get(obj);
399    if (!wd) return;
400
401    label = NULL;
402    item = DATA_GET(wd->current);
403    if (item) label = item->label;
404
405    edje_object_part_text_set(wd->base, "top", label ? label : "");
406    edje_object_part_text_set(wd->base, "bottom", label ? label : "");
407    edje_object_message_signal_process(wd->base);
408 }
409
410 static void
411 _changed(Widget_Data *wd)
412 {
413    Elm_Flipselector_Item *item;
414
415    item = DATA_GET(wd->current);
416    if (!item) return;
417
418    if (item->func)
419      item->func((void *)item->base.data, WIDGET(item), item);
420    if (!item->deleted)
421      evas_object_smart_callback_call(wd->self, SIG_SELECTED, item);
422 }
423
424 static void
425 _send_msg(Widget_Data *wd, int flipside, char *label)
426 {
427    Edje_Message_String msg;
428
429    msg.str = label;
430    edje_object_message_send(wd->base, EDJE_MESSAGE_STRING, flipside, &msg);
431    edje_object_message_signal_process(wd->base);
432
433    _changed(wd);
434 }
435
436 static void
437 _flip_up(Widget_Data *wd)
438 {
439    Elm_Flipselector_Item *item;
440
441    if (!wd->current) return;
442
443    if (wd->current == wd->items)
444      {
445         wd->current = eina_list_last(wd->items);
446         evas_object_smart_callback_call(wd->self, SIG_UNDERFLOWED, NULL);
447      }
448    else
449      wd->current = eina_list_prev(wd->current);
450
451    item = DATA_GET(wd->current);
452    if (!item) return;
453
454    _send_msg(wd, MSG_FLIP_UP, (char *)item->label);
455 }
456
457 static Eina_Bool
458 _signal_val_up(void *data)
459 {
460    Widget_Data *wd = elm_widget_data_get(data);
461
462    if (!wd) goto val_up_exit_on_error;
463
464    _flipselector_walk(wd);
465
466    if (wd->interval > FLIP_MIN_INTERVAL)
467      wd->interval = wd->interval / 1.05;
468
469    ecore_timer_interval_set(wd->spin, wd->interval);
470
471    _flip_up(wd);
472
473    _flipselector_unwalk(wd);
474
475    return ECORE_CALLBACK_RENEW;
476
477 val_up_exit_on_error:
478    return ECORE_CALLBACK_CANCEL;
479 }
480
481 static void
482 _signal_val_up_start(void *data, Evas_Object *obj __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__)
483 {
484    Widget_Data *wd = elm_widget_data_get(data);
485    if (!wd) return;
486
487    wd->interval = wd->first_interval;
488
489    if (wd->spin) ecore_timer_del(wd->spin);
490    wd->spin = ecore_timer_add(wd->interval, _signal_val_up, data);
491
492    _signal_val_up(data);
493 }
494
495 static void
496 _flip_down(Widget_Data *wd)
497 {
498    Elm_Flipselector_Item *item;
499
500    if (!wd->current) return;
501
502    wd->current = eina_list_next(wd->current);
503    if (!wd->current)
504      {
505         wd->current = wd->items;
506         evas_object_smart_callback_call(wd->self, SIG_OVERFLOWED, NULL);
507      }
508
509    item = DATA_GET(wd->current);
510    if (!item) return;
511
512    _send_msg(wd, MSG_FLIP_DOWN, (char *)item->label);
513 }
514
515 static Eina_Bool
516 _signal_val_down(void *data)
517 {
518    Widget_Data *wd = elm_widget_data_get(data);
519
520    if (!wd) goto val_down_exit_on_error;
521
522    _flipselector_walk(wd);
523
524    if (wd->interval > FLIP_MIN_INTERVAL)
525      wd->interval = wd->interval / 1.05;
526    ecore_timer_interval_set(wd->spin, wd->interval);
527
528    _flip_down(wd);
529
530    _flipselector_unwalk(wd);
531
532    return ECORE_CALLBACK_RENEW;
533
534 val_down_exit_on_error:
535    return ECORE_CALLBACK_CANCEL;
536 }
537
538 static void
539 _signal_val_down_start(void *data, Evas_Object *obj __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__)
540 {
541    Widget_Data *wd = elm_widget_data_get(data);
542    if (!wd) return;
543
544    wd->interval = wd->first_interval;
545
546    if (wd->spin) ecore_timer_del(wd->spin);
547    wd->spin = ecore_timer_add(wd->interval, _signal_val_down, data);
548
549    _signal_val_down(data);
550 }
551
552 static void
553 _signal_val_change_stop(void *data, Evas_Object *obj __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__)
554 {
555    Widget_Data *wd = elm_widget_data_get(data);
556    if (!wd) return;
557
558    if (wd->spin) ecore_timer_del(wd->spin);
559    wd->spin = NULL;
560 }
561
562 static void
563 _callbacks_set(Evas_Object *obj)
564 {
565    Widget_Data *wd = elm_widget_data_get(obj);
566
567    edje_object_signal_callback_add(wd->base, "elm,action,up,start",
568                                    "", _signal_val_up_start, obj);
569    edje_object_signal_callback_add(wd->base, "elm,action,up,stop",
570                                    "", _signal_val_change_stop, obj);
571    edje_object_signal_callback_add(wd->base, "elm,action,down,start",
572                                    "", _signal_val_down_start, obj);
573    edje_object_signal_callback_add(wd->base, "elm,action,down,stop",
574                                    "", _signal_val_change_stop, obj);
575 }
576
577 EAPI Evas_Object *
578 elm_flipselector_add(Evas_Object *parent)
579 {
580    Evas_Object *obj;
581    Evas *e;
582    Widget_Data *wd;
583
584    ELM_WIDGET_STANDARD_SETUP(wd, Widget_Data, parent, e, obj, NULL);
585
586    ELM_SET_WIDTYPE(widtype, "flipselector");
587    elm_widget_type_set(obj, "flipselector");
588    elm_widget_sub_object_add(parent, obj);
589    elm_widget_data_set(obj, wd);
590
591    wd->self = obj;
592    elm_widget_del_hook_set(obj, _del_hook);
593    elm_widget_theme_hook_set(obj, _theme_hook);
594    /* TODO: elm_widget_disable_hook_set(obj, _disable_hook); */
595
596    elm_widget_can_focus_set(obj, EINA_TRUE);
597    elm_widget_on_focus_hook_set(obj, _on_focus_hook, NULL);
598    elm_widget_event_hook_set(obj, _event_hook);
599
600    wd->base = edje_object_add(e);
601    elm_widget_resize_object_set(obj, wd->base);
602
603    _callbacks_set(obj);
604
605    wd->first_interval = FLIP_FIRST_INTERVAL;
606
607    _theme_hook(obj);
608
609    evas_object_smart_callbacks_descriptions_set(obj, _signals);
610    return obj;
611 }
612
613 EAPI void
614 elm_flipselector_flip_next(Evas_Object *obj)
615 {
616    ELM_CHECK_WIDTYPE(obj, widtype);
617
618    Widget_Data *wd = elm_widget_data_get(obj);
619    if (!wd) return;
620
621    if (wd->spin) ecore_timer_del(wd->spin);
622
623    _flipselector_walk(wd);
624    _flip_down(wd);
625    _flipselector_unwalk(wd);
626 }
627
628 EAPI void
629 elm_flipselector_flip_prev(Evas_Object *obj)
630 {
631    ELM_CHECK_WIDTYPE(obj, widtype);
632
633    Widget_Data *wd = elm_widget_data_get(obj);
634    if (!wd) return;
635
636    if (wd->spin) ecore_timer_del(wd->spin);
637
638    _flipselector_walk(wd);
639    _flip_up(wd);
640    _flipselector_unwalk(wd);
641 }
642
643 EAPI Elm_Object_Item *
644 elm_flipselector_item_append(Evas_Object *obj, const char *label, void (*func)(void *data, Evas_Object *obj, void *event_info), void *data)
645 {
646    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
647
648    Elm_Flipselector_Item *item;
649    Widget_Data *wd;
650
651    wd = elm_widget_data_get(obj);
652    if (!wd) return NULL;
653
654    item = _item_new(obj, label, func, data);
655    if (!item) return NULL;
656
657    wd->items = eina_list_append(wd->items, item);
658    if (!wd->current)
659      {
660         wd->current = wd->items;
661         _update_view(obj);
662      }
663
664    if (!wd->sentinel ||
665        (strlen(elm_object_item_text_get((Elm_Object_Item *) item)) >
666         strlen(elm_object_item_text_get(DATA_GET(wd->sentinel)))))
667      {
668         wd->sentinel = eina_list_last(wd->items);
669         _sizing_eval(obj);
670      }
671
672    if (eina_list_count(wd->items) >= 2)
673      edje_object_signal_emit(wd->base, "elm,state,button,visible", "elm");
674
675    return (Elm_Object_Item *) item;
676 }
677
678 EAPI Elm_Object_Item *
679 elm_flipselector_item_prepend(Evas_Object *obj, const char *label, void (*func)(void *data, Evas_Object *obj, void *event_info), void *data)
680 {
681    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
682
683    Elm_Flipselector_Item *item;
684    Widget_Data *wd;
685
686    wd = elm_widget_data_get(obj);
687    if (!wd) return NULL;
688
689    item = _item_new(obj, label, func, data);
690    if (!item) return NULL;
691
692    wd->items = eina_list_prepend(wd->items, item);
693    if (!wd->current)
694      {
695         wd->current = wd->items;
696         _update_view(obj);
697      }
698
699    if (!wd->sentinel ||
700        (strlen(elm_object_item_text_get((Elm_Object_Item *) item)) >
701         strlen(elm_object_item_text_get(DATA_GET(wd->sentinel)))))
702      {
703         wd->sentinel = wd->items;
704         _sizing_eval(obj);
705      }
706
707    if (eina_list_count(wd->items) >= 2)
708      edje_object_signal_emit(wd->base, "elm,state,button,visible", "elm");
709
710    return (Elm_Object_Item *) item;
711 }
712
713 /* TODO: account for deleted items?  */
714 EAPI const Eina_List *
715 elm_flipselector_items_get(const Evas_Object *obj)
716 {
717    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
718
719    Widget_Data *wd = elm_widget_data_get(obj);
720    if (!wd) return NULL;
721    return wd->items;
722 }
723
724 EAPI Elm_Object_Item *
725 elm_flipselector_first_item_get(const Evas_Object *obj)
726 {
727    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
728
729    Elm_Flipselector_Item *it;
730    Widget_Data *wd;
731    Eina_List *l;
732
733    wd = elm_widget_data_get(obj);
734    if (!wd || !wd->items) return NULL;
735
736    EINA_LIST_FOREACH(wd->items, l, it)
737      {
738         if (it->deleted) continue;
739         return (Elm_Object_Item *) it;
740      }
741    return NULL;
742 }
743
744 EAPI Elm_Object_Item *
745 elm_flipselector_last_item_get(const Evas_Object *obj)
746 {
747    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
748
749    Elm_Flipselector_Item *it;
750    Widget_Data *wd;
751    Eina_List *l;
752
753    wd = elm_widget_data_get(obj);
754    if (!wd || !wd->items) return NULL;
755
756    EINA_LIST_REVERSE_FOREACH(wd->items, l, it)
757      {
758         if (it->deleted) continue;
759         return (Elm_Object_Item *) it;
760      }
761    return NULL;
762 }
763
764 EAPI Elm_Object_Item *
765 elm_flipselector_selected_item_get(const Evas_Object *obj)
766 {
767    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
768
769    Widget_Data *wd = elm_widget_data_get(obj);
770    if (!wd || !wd->current) return NULL;
771    return DATA_GET(wd->current);
772 }
773
774 EAPI void
775 elm_flipselector_item_selected_set(Elm_Object_Item *it, Eina_Bool selected)
776 {
777    ELM_OBJ_ITEM_CHECK_OR_RETURN(it);
778
779    Elm_Flipselector_Item *item, *_item, *cur;
780    int flipside = MSG_FLIP_UP;
781    Widget_Data *wd;
782    Eina_List *l;
783
784    item = (Elm_Flipselector_Item *) it;
785    wd = elm_widget_data_get(WIDGET(item));
786    if (!wd) return;
787
788    cur = DATA_GET(wd->current);
789    if ((selected) && (cur == item)) return;
790
791    _flipselector_walk(wd);
792
793    if ((!selected) && (cur == item))
794      {
795         EINA_LIST_FOREACH(wd->items, l, _item)
796           {
797              if (!_item->deleted)
798                {
799                   wd->current = l;
800                   _send_msg(wd, MSG_FLIP_UP, (char *)_item->label);
801                   break;
802                }
803           }
804         _flipselector_unwalk(wd);
805         return;
806      }
807
808    EINA_LIST_FOREACH(wd->items, l, _item)
809      {
810         if (_item == cur) flipside = MSG_FLIP_DOWN;
811
812         if (_item == item)
813           {
814              wd->current = l;
815              _send_msg(wd, flipside, (char *)item->label);
816              break;
817           }
818      }
819
820    _flipselector_unwalk(wd);
821 }
822
823 EAPI Eina_Bool
824 elm_flipselector_item_selected_get(const Elm_Object_Item *it)
825 {
826    ELM_OBJ_ITEM_CHECK_OR_RETURN(it, EINA_FALSE);
827    Widget_Data *wd;
828    Elm_Flipselector_Item *item;
829
830    item = (Elm_Flipselector_Item *) it;
831    wd = elm_widget_data_get(WIDGET(item));
832    if (!wd) return EINA_FALSE;
833    return (eina_list_data_get(wd->current) == item);
834 }
835
836 EAPI void
837 elm_flipselector_item_del(Elm_Object_Item *it)
838 {
839    ELM_OBJ_ITEM_CHECK_OR_RETURN(it);
840    Widget_Data *wd;
841    Elm_Flipselector_Item *item, *item2;
842    Eina_List *l;
843
844    item = (Elm_Flipselector_Item *) it;
845    wd = elm_widget_data_get(WIDGET(item));
846    if (!wd) return;
847
848    if (wd->walking > 0)
849      {
850         item->deleted = EINA_TRUE;
851         return;
852      }
853
854    _flipselector_walk(wd);
855
856    wd->items = eina_list_remove(wd->items, item);
857    _item_free(item);
858    _sentinel_eval(wd);
859    _flipselector_unwalk(wd);
860 }
861
862 EAPI const char *
863 elm_flipselector_item_label_get(const Elm_Object_Item *it)
864 {
865    return _item_text_get_hook(it, NULL);
866 }
867
868 EAPI void
869 elm_flipselector_item_label_set(Elm_Object_Item *it, const char *label)
870 {
871    _item_text_set_hook(it, NULL, label);
872 }
873
874 EAPI Elm_Object_Item *
875 elm_flipselector_item_prev_get(Elm_Object_Item *it)
876 {
877    ELM_OBJ_ITEM_CHECK_OR_RETURN(it, NULL);
878
879    Elm_Flipselector_Item *item, *_item;
880    Widget_Data *wd;
881    Eina_List *l;
882
883    item = (Elm_Flipselector_Item *) it;
884    wd = elm_widget_data_get(WIDGET(item));
885    if ((!wd) || (!wd->items)) return NULL;
886
887    EINA_LIST_FOREACH(wd->items, l, _item)
888      if (_item == item)
889        {
890           l = eina_list_prev(l);
891           if (!l) return NULL;
892           return DATA_GET(l);
893        }
894    return NULL;
895 }
896
897 EAPI Elm_Object_Item *
898 elm_flipselector_item_next_get(Elm_Object_Item *it)
899 {
900    ELM_OBJ_ITEM_CHECK_OR_RETURN(it, NULL);
901
902    Elm_Flipselector_Item *item, *_item;
903    Widget_Data *wd;
904    Eina_List *l;
905
906    item = (Elm_Flipselector_Item *) it;
907    wd = elm_widget_data_get(WIDGET(item));
908    if ((!wd) || (!wd->items)) return NULL;
909
910    EINA_LIST_FOREACH(wd->items, l, _item)
911      if (_item == item)
912        {
913           l = eina_list_next(l);
914           if (!l) return NULL;
915           return DATA_GET(l);
916        }
917    return NULL;
918 }
919
920 EAPI void
921 elm_flipselector_interval_set(Evas_Object *obj, double interval)
922 {
923    ELM_CHECK_WIDTYPE(obj, widtype);
924
925    Widget_Data *wd = elm_widget_data_get(obj);
926    if (!wd) return;
927    wd->first_interval = interval;
928 }
929
930 EAPI double
931 elm_flipselector_interval_get(const Evas_Object *obj)
932 {
933    ELM_CHECK_WIDTYPE(obj, widtype) 0.0;
934
935    Widget_Data *wd = elm_widget_data_get(obj);
936    if (!wd) return 0;
937    return wd->first_interval;
938 }