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