elementry / entry, menu, notify, list, gengrid, actionslider, image, icon, anchorview...
[framework/uifw/elementary.git] / src / lib / elm_flipselector.c
1 #include <Elementary.h>
2 #include "elm_priv.h"
3
4 /**
5  * @addtogroup Flipselector Flipselector
6  *
7  * A flip selector is a widget to show a set of label items, one at a
8  * time, with an animation when one changes the current selection
9  * (like the flip of calendar sheets, in the default theme).
10  *
11  * Signals that you can add callbacks for are:
12  *
13  * "selected" - when flipselector selected item is changed
14  * "overflowed" - when flipselector item is changed to first item
15  *                from last item
16  * "underflowed" - when flipselector item is changed to last item
17  *                 from first item.
18  */
19
20 /* TODO: ideally, the default theme would use map{} blocks on the TEXT
21    parts to implement their fading in/out propertly (as in the clock
22    widget) */
23 /* TODO: if one ever wants to extend it to receiving generic widgets
24    as items, be my guest. in this case, remember to implement the
25    items tooltip infra. */
26 /* TODO: implement disabled mode -- disable_hook() and stuff. */
27 /* TODO: fix default theme image borders for looong strings as item
28    labels. */
29 /* TODO: set text elipsis on labels if one enforces mininum size on
30  * the overall widget less the required for displaying it. */
31 /* TODO: find a way to, in the default theme, to detect we are
32  * bootstrapping (receiving the 1st message) and populate the downmost
33  * TEXT parts with the same text as the upmost, where appropriate. */
34
35 #define FLIP_FIRST_INTERVAL (0.85)
36 #define FLIP_MIN_INTERVAL (0.1)
37 #define MSG_FLIP_DOWN (1)
38 #define MSG_FLIP_UP (2)
39 #define MAX_LEN_DEFAULT (50)
40
41 #define DATA_GET eina_list_data_get
42
43 struct _Elm_Flipselector_Item
44 {
45    Elm_Widget_Item base;
46    const char *label;
47    Evas_Smart_Cb func;
48    void *data;
49    int deleted : 1;
50 };
51
52 typedef struct _Widget_Data Widget_Data;
53
54 struct _Widget_Data
55 {
56    Evas_Object *self;
57    Evas_Object *base;
58    Eina_List *items;
59    Eina_List *current;
60    Eina_List *sentinel; /* item containing the largest label string */
61    int walking;
62    unsigned int max_len;
63    Ecore_Timer *spin;
64    double interval, first_interval;
65 };
66
67 static const char *widtype = NULL;
68 static void _del_hook(Evas_Object *obj);
69 static void _theme_hook(Evas_Object *obj);
70 static void _sizing_eval(Evas_Object *obj);
71 static void _update_view(Evas_Object *obj);
72 static void _callbacks_set(Evas_Object *obj);
73 static void _flip_up(Widget_Data *wd);
74 static void _flip_down(Widget_Data *wd);
75
76 static const char SIG_SELECTED[] = "selected";
77 static const char SIG_UNDERFLOWED[] = "underflowed";
78 static const char SIG_OVERFLOWED[] = "overflowed";
79 static const Evas_Smart_Cb_Description _signals[] = {
80   {SIG_SELECTED, ""},
81   {SIG_UNDERFLOWED, ""},
82   {SIG_OVERFLOWED, ""},
83   {NULL, NULL}
84 };
85
86 #define ELM_FLIPSELECTOR_ITEM_CHECK_DELETED_RETURN(it, ...)             \
87    ELM_WIDGET_ITEM_WIDTYPE_CHECK_OR_RETURN(it, __VA_ARGS__);            \
88   if (it->deleted)                                                      \
89     {                                                                   \
90        ERR(""#it" has been DELETED.\n");                                \
91        return __VA_ARGS__;                                              \
92     }                                                                   \
93
94 static Elm_Flipselector_Item *
95 _item_new(Evas_Object *obj, const char *label, Evas_Smart_Cb func, const void *data)
96 {
97    unsigned int len;
98    Elm_Flipselector_Item *it;
99    Widget_Data *wd = elm_widget_data_get(obj);
100
101    it = elm_widget_item_new(obj, Elm_Flipselector_Item);
102    if (!it)
103      return NULL;
104
105    len = strlen(label);
106    if (len > wd->max_len)
107      len = wd->max_len;
108
109    it->label = eina_stringshare_add_length(label, len);
110    it->func = func;
111    it->base.data = data;
112
113    /* TODO: no view here, but if one desires general contents in the
114     * future... */
115    return it;
116 }
117
118 static inline void
119 _item_free(Elm_Flipselector_Item *it)
120 {
121    eina_stringshare_del(it->label);
122    elm_widget_item_del(it);
123 }
124
125 static void
126 _del_hook(Evas_Object *obj)
127 {
128    Elm_Flipselector_Item *item;
129
130    Widget_Data *wd = elm_widget_data_get(obj);
131    if (!wd)
132      return;
133
134    if (wd->walking)
135      ERR("flipselector deleted while walking.\n");
136
137    EINA_LIST_FREE(wd->items, item)
138       _item_free(item);
139
140    if (wd->spin) ecore_timer_del(wd->spin);
141    free(wd);
142 }
143
144 static void
145 _theme_hook(Evas_Object *obj)
146 {
147    Widget_Data *wd;
148    const char *max_len;
149
150    wd = elm_widget_data_get(obj);
151    if (!wd)
152      return;
153
154    _elm_theme_object_set(obj, wd->base, "flipselector", "base",
155                          elm_widget_style_get(obj));
156    edje_object_scale_set(wd->base,
157                          elm_widget_scale_get(obj) * _elm_config->scale);
158
159    max_len = edje_object_data_get(wd->base, "max_len");
160    if (!max_len)
161      wd->max_len = MAX_LEN_DEFAULT;
162    else
163      {
164         wd->max_len = atoi(max_len);
165         if (!wd->max_len)
166           wd->max_len = MAX_LEN_DEFAULT;
167      }
168
169    _update_view(obj);
170    _sizing_eval(obj);
171 }
172
173 static void
174 _sentinel_eval(Widget_Data *wd)
175 {
176    Elm_Flipselector_Item *it;
177    Eina_List *l;
178
179    if (!wd->items)
180      {
181         wd->sentinel = NULL;
182         return;
183      }
184
185    wd->sentinel = wd->items;
186
187    EINA_LIST_FOREACH(wd->items, l, it)
188      {
189         if (strlen(elm_flipselector_item_label_get(it)) >
190             strlen(elm_flipselector_item_label_get(DATA_GET(wd->sentinel))))
191           wd->sentinel = l;
192      }
193 }
194
195 /* TODO: create a flag to avoid looping here all times */
196 static void
197 _flipselector_process_deletions(Widget_Data *wd)
198 {
199    Elm_Flipselector_Item *it;
200    Eina_List *l;
201    Eina_Bool skip = EINA_TRUE;
202    Eina_Bool sentinel_eval = EINA_FALSE;
203
204    wd->walking++; /* avoid nested deletions */
205
206    EINA_LIST_FOREACH(wd->items, l, it)
207      {
208         if (!it->deleted)
209           continue;
210
211         if (wd->current == l)
212           {
213              if (wd->current == wd->sentinel)
214                sentinel_eval = EINA_TRUE;
215
216              wd->current = eina_list_prev(wd->current);
217           }
218         wd->items = eina_list_remove(wd->items, it);
219
220         if (!wd->current)
221           wd->current = wd->items;
222
223         _item_free(it);
224         skip = EINA_FALSE;
225
226         if (eina_list_count(wd->items) <= 1)
227           edje_object_signal_emit(wd->base, "elm,state,button,hidden", "elm");
228         else
229           edje_object_signal_emit(wd->base, "elm,state,button,visible", "elm");
230      }
231
232    if (!skip)
233      _update_view(wd->self);
234
235    if (sentinel_eval)
236      _sentinel_eval(wd);
237
238    wd->walking--;
239 }
240
241 static inline void
242 _flipselector_walk(Widget_Data *wd)
243 {
244    if (wd->walking < 0)
245      {
246         ERR("walking was negative. fixed!\n");
247         wd->walking = 0;
248      }
249    wd->walking++;
250 }
251
252 static inline void
253 _flipselector_unwalk(Widget_Data *wd)
254 {
255    wd->walking--;
256    if (wd->walking < 0)
257      {
258         ERR("walking became negative. fixed!\n");
259         wd->walking = 0;
260      }
261
262    if (wd->walking)
263      return;
264
265    _flipselector_process_deletions(wd);
266 }
267
268 static Eina_Bool
269 _event_hook(Evas_Object *obj, Evas_Object *src __UNUSED__, Evas_Callback_Type type, void *event_info)
270 {
271    Evas_Event_Key_Down *ev;
272    Widget_Data *wd;
273    Eina_Bool is_up = EINA_TRUE;
274
275    if (type != EVAS_CALLBACK_KEY_DOWN)
276      return EINA_FALSE;
277
278    wd = elm_widget_data_get(obj);
279    if (!wd)
280      return EINA_FALSE;
281
282    ev = event_info;
283    if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD)
284      return EINA_FALSE;
285
286    if (elm_widget_disabled_get(obj))
287      return EINA_FALSE;
288
289    if ((!strcmp(ev->keyname, "Down")) || (!strcmp(ev->keyname, "KP_Down")))
290      is_up = EINA_FALSE;
291    else if ((strcmp(ev->keyname, "Up")) && (strcmp(ev->keyname, "KP_Up")))
292      return EINA_FALSE;
293
294    if (wd->spin) ecore_timer_del(wd->spin);
295
296    /* TODO: if direction setting via API is not coming in, replace
297       these calls by flip_{next,prev} */
298    _flipselector_walk(wd);
299    if (is_up)
300      _flip_up(wd);
301    else
302      _flip_down(wd);
303    _flipselector_unwalk(wd);
304
305    ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
306    return EINA_TRUE;
307 }
308
309 static void
310 _on_focus_hook(void *data __UNUSED__, Evas_Object *obj)
311 {
312    Widget_Data *wd = elm_widget_data_get(obj);
313    if (!wd)
314      return;
315
316    /* FIXME: no treatment of this signal so far */
317    if (elm_widget_focus_get(obj))
318      {
319         edje_object_signal_emit(wd->base, "elm,action,focus", "elm");
320         evas_object_focus_set(wd->base, EINA_TRUE);
321      }
322    else
323      {
324         edje_object_signal_emit(wd->base, "elm,action,unfocus", "elm");
325         evas_object_focus_set(wd->base, EINA_FALSE);
326      }
327 }
328
329 static void
330 _sizing_eval(Evas_Object *obj)
331 {
332    Widget_Data *wd;
333    const char *tmp = NULL;
334    Evas_Coord minw = -1, minh = -1, w, h;
335
336    wd = elm_widget_data_get(obj);
337    if (!wd)
338      return;
339
340    elm_coords_finger_size_adjust(1, &minw, 2, &minh);
341
342    if (wd->sentinel)
343      {
344         const char *label = \
345                             elm_flipselector_item_label_get(DATA_GET(wd->sentinel));
346
347         tmp = edje_object_part_text_get(wd->base, "top");
348         edje_object_part_text_set(wd->base, "top", label);
349      }
350
351    edje_object_size_min_restricted_calc(wd->base, &minw, &minh, minw, minh);
352    elm_coords_finger_size_adjust(1, &minw, 2, &minh);
353    evas_object_size_hint_min_get(obj, &w, &h);
354
355    if (wd->sentinel)
356      edje_object_part_text_set(wd->base, "top", tmp);
357
358    if (w > minw) minw = w;
359    if (h > minh) minh = h;
360
361    evas_object_size_hint_min_set(obj, minw, minh);
362 }
363
364 static void
365 _update_view(Evas_Object *obj)
366 {
367    Widget_Data *wd;
368    const char *label;
369    Elm_Flipselector_Item *item;
370
371    wd = elm_widget_data_get(obj);
372    if (!wd)
373      return;
374
375    label = NULL;
376    item = DATA_GET(wd->current);
377    if (item)
378      label = item->label;
379
380    edje_object_part_text_set(wd->base, "top", label ? label : "");
381    edje_object_part_text_set(wd->base, "bottom", label ? label : "");
382
383    edje_object_message_signal_process(wd->base);
384 }
385
386 static void
387 _changed(Widget_Data *wd)
388 {
389    Elm_Flipselector_Item *item;
390
391    item = DATA_GET(wd->current);
392    if (!item)
393      return;
394
395    if (item->func)
396      item->func((void *)item->base.data, item->base.widget, item);
397    if (!item->deleted)
398      evas_object_smart_callback_call(wd->self, SIG_SELECTED, item);
399 }
400
401 static void
402 _send_msg(Widget_Data *wd, int flipside, char *label)
403 {
404    Edje_Message_String msg;
405
406    msg.str = label;
407    edje_object_message_send(wd->base, EDJE_MESSAGE_STRING, flipside, &msg);
408    edje_object_message_signal_process(wd->base);
409
410    _changed(wd);
411 }
412
413 static void
414 _flip_up(Widget_Data *wd)
415 {
416    Elm_Flipselector_Item *item;
417
418    if (!wd->current)
419      return;
420
421    if (wd->current == wd->items)
422      {
423         wd->current = eina_list_last(wd->items);
424         evas_object_smart_callback_call(wd->self, SIG_UNDERFLOWED, NULL);
425      }
426    else
427      wd->current = eina_list_prev(wd->current);
428
429    item = DATA_GET(wd->current);
430    if (!item)
431      return;
432
433    _send_msg(wd, MSG_FLIP_UP, (char *)item->label);
434 }
435
436 static Eina_Bool
437 _signal_val_up(void *data)
438 {
439    Widget_Data *wd = elm_widget_data_get(data);
440
441    if (!wd)
442      goto val_up_exit_on_error;
443
444    _flipselector_walk(wd);
445
446    if (wd->interval > FLIP_MIN_INTERVAL)
447      wd->interval = wd->interval / 1.05;
448
449    ecore_timer_interval_set(wd->spin, wd->interval);
450
451    _flip_up(wd);
452
453    _flipselector_unwalk(wd);
454
455    return ECORE_CALLBACK_RENEW;
456
457 val_up_exit_on_error:
458    return ECORE_CALLBACK_CANCEL;
459 }
460
461 static void
462 _signal_val_up_start(void *data, Evas_Object *obj __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__)
463 {
464    Widget_Data *wd = elm_widget_data_get(data);
465    if (!wd)
466      return;
467
468    wd->interval = wd->first_interval;
469
470    if (wd->spin)
471      ecore_timer_del(wd->spin);
472    wd->spin = ecore_timer_add(wd->interval, _signal_val_up, data);
473
474    _signal_val_up(data);
475 }
476
477 static void
478 _flip_down(Widget_Data *wd)
479 {
480    Elm_Flipselector_Item *item;
481
482    if (!wd->current)
483      return;
484
485    wd->current = eina_list_next(wd->current);
486    if (!wd->current)
487      {
488         wd->current = wd->items;
489         evas_object_smart_callback_call(wd->self, SIG_OVERFLOWED, NULL);
490      }
491
492    item = DATA_GET(wd->current);
493    if (!item)
494      return;
495
496    _send_msg(wd, MSG_FLIP_DOWN, (char *)item->label);
497 }
498
499 static Eina_Bool
500 _signal_val_down(void *data)
501 {
502    Widget_Data *wd = elm_widget_data_get(data);
503
504    if (!wd)
505      goto val_down_exit_on_error;
506
507    _flipselector_walk(wd);
508
509    if (wd->interval > FLIP_MIN_INTERVAL)
510      wd->interval = wd->interval / 1.05;
511    ecore_timer_interval_set(wd->spin, wd->interval);
512
513    _flip_down(wd);
514
515    _flipselector_unwalk(wd);
516
517    return ECORE_CALLBACK_RENEW;
518
519 val_down_exit_on_error:
520    return ECORE_CALLBACK_CANCEL;
521 }
522
523 static void
524 _signal_val_down_start(void *data, Evas_Object *obj __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__)
525 {
526    Widget_Data *wd = elm_widget_data_get(data);
527    if (!wd)
528      return;
529
530    wd->interval = wd->first_interval;
531
532    if (wd->spin)
533      ecore_timer_del(wd->spin);
534    wd->spin = ecore_timer_add(wd->interval, _signal_val_down, data);
535
536    _signal_val_down(data);
537 }
538
539 static void
540 _signal_val_change_stop(void *data, Evas_Object *obj __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__)
541 {
542    Widget_Data *wd = elm_widget_data_get(data);
543    if (!wd)
544      return;
545
546    if (wd->spin)
547      ecore_timer_del(wd->spin);
548    wd->spin = NULL;
549 }
550
551 static void
552 _callbacks_set(Evas_Object *obj)
553 {
554    Widget_Data *wd = elm_widget_data_get(obj);
555
556    edje_object_signal_callback_add(wd->base, "elm,action,up,start",
557                                    "", _signal_val_up_start, obj);
558    edje_object_signal_callback_add(wd->base, "elm,action,up,stop",
559                                    "", _signal_val_change_stop, obj);
560    edje_object_signal_callback_add(wd->base, "elm,action,down,start",
561                                    "", _signal_val_down_start, obj);
562    edje_object_signal_callback_add(wd->base, "elm,action,down,stop",
563                                    "", _signal_val_change_stop, obj);
564 }
565
566 /**
567  * Add a new flipselector to the parent.
568  *
569  * @param parent The parent object
570  * @return The new object or NULL, if it cannot be created
571  *
572  * @ingroup Flipselector
573  */
574 EAPI Evas_Object *
575 elm_flipselector_add(Evas_Object *parent)
576 {
577    Evas_Object *obj;
578    Evas *e;
579    Widget_Data *wd;
580
581    ELM_WIDGET_STANDARD_SETUP(wd, Widget_Data, parent, e, obj, NULL);
582
583    ELM_SET_WIDTYPE(widtype, "flipselector");
584    elm_widget_type_set(obj, "flipselector");
585    elm_widget_sub_object_add(parent, obj);
586    elm_widget_data_set(obj, wd);
587
588    elm_widget_del_hook_set(obj, _del_hook);
589    elm_widget_theme_hook_set(obj, _theme_hook);
590    /* TODO: elm_widget_disable_hook_set(obj, _disable_hook); */
591
592    elm_widget_can_focus_set(obj, EINA_TRUE);
593    elm_widget_on_focus_hook_set(obj, _on_focus_hook, NULL);
594    elm_widget_event_hook_set(obj, _event_hook);
595
596    wd->base = edje_object_add(e);
597    elm_widget_resize_object_set(obj, wd->base);
598
599    _callbacks_set(obj);
600
601    wd->first_interval = FLIP_FIRST_INTERVAL;
602
603    _theme_hook(obj);
604
605    evas_object_smart_callbacks_descriptions_set(obj, _signals);
606    return obj;
607 }
608
609 /**
610  * Select next item of a flipselector.
611  *
612  * @param obj The flipselector object
613  *
614  * @ingroup Flipselector
615  */
616 EAPI void
617 elm_flipselector_flip_next(Evas_Object *obj)
618 {
619    ELM_CHECK_WIDTYPE(obj, widtype);
620
621    Widget_Data *wd = elm_widget_data_get(obj);
622    if (!wd)
623      return;
624
625    if (wd->spin) ecore_timer_del(wd->spin);
626
627    _flipselector_walk(wd);
628    _flip_down(wd);
629    _flipselector_unwalk(wd);
630 }
631
632 /**
633  * Select previous item of a flipselector.
634  *
635  * @param obj The flipselector object
636  *
637  * @ingroup Flipselector
638  */
639 EAPI void
640 elm_flipselector_flip_prev(Evas_Object *obj)
641 {
642    ELM_CHECK_WIDTYPE(obj, widtype);
643
644    Widget_Data *wd = elm_widget_data_get(obj);
645    if (!wd)
646      return;
647
648    if (wd->spin) ecore_timer_del(wd->spin);
649
650    _flipselector_walk(wd);
651    _flip_up(wd);
652    _flipselector_unwalk(wd);
653 }
654
655 /**
656  * Append item to a flipselector.
657  *
658  * @param obj The flipselector object
659  * @param label The label of new item
660  * @param func Convenience function called when item selected
661  * @param data Data passed to @p func above
662  * @return A handle to the item added or NULL, on errors
663  *
664  * @note The maximum length of the label is going to be determined by
665  * the widget's theme. Strings larger than that value are going to be
666  * truncated.
667  *
668  * @ingroup Flipselector
669  */
670 EAPI Elm_Flipselector_Item *
671 elm_flipselector_item_append(Evas_Object *obj, const char *label, void (*func)(void *data, Evas_Object *obj, void *event_info), void *data)
672 {
673    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
674
675    Elm_Flipselector_Item *item;
676    Widget_Data *wd;
677
678    wd = elm_widget_data_get(obj);
679    if (!wd)
680      return NULL;
681
682    item = _item_new(obj, label, func, data);
683    if (!item)
684      return NULL;
685
686    wd->items = eina_list_append(wd->items, item);
687    if (!wd->current) {
688         wd->current = wd->items;
689         _update_view(obj);
690    }
691
692    if (!wd->sentinel ||
693        (strlen(elm_flipselector_item_label_get(item)) >
694         strlen(elm_flipselector_item_label_get(DATA_GET(wd->sentinel)))))
695      {
696         wd->sentinel = eina_list_last(wd->items);
697         _sizing_eval(obj);
698      }
699
700    if (eina_list_count(wd->items) >= 2)
701      edje_object_signal_emit(wd->base, "elm,state,button,visible", "elm");
702
703    return item;
704 }
705
706 /**
707  * Prepend item to a flipselector.
708  *
709  * @param obj The flipselector object
710  * @param label The label of new item
711  * @param func Convenience function called when item selected
712  * @param data Data passed to @p func above
713  * @return A handle to the item added or NULL, on errors
714  *
715  * @note The maximum length of the label is going to be determined by
716  * the widget's theme. Strings larger than that value are going to be
717  * truncated.
718  *
719  * @ingroup Flipselector
720  */
721 EAPI Elm_Flipselector_Item *
722 elm_flipselector_item_prepend(Evas_Object *obj, const char *label, void (*func)(void *data, Evas_Object *obj, void *event_info), void *data)
723 {
724    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
725
726    Elm_Flipselector_Item *item;
727    Widget_Data *wd;
728
729    wd = elm_widget_data_get(obj);
730    if (!wd)
731      return NULL;
732
733    item = _item_new(obj, label, func, data);
734    if (!item)
735      return NULL;
736
737    wd->items = eina_list_prepend(wd->items, item);
738    if (!wd->current) {
739         wd->current = wd->items;
740         _update_view(obj);
741    }
742
743    if (!wd->sentinel ||
744        (strlen(elm_flipselector_item_label_get(item)) >
745         strlen(elm_flipselector_item_label_get(DATA_GET(wd->sentinel)))))
746      {
747         wd->sentinel = wd->items;
748         _sizing_eval(obj);
749      }
750
751    if (eina_list_count(wd->items) >= 2)
752      edje_object_signal_emit(wd->base, "elm,state,button,visible", "elm");
753
754    return item;
755 }
756
757 /* TODO: account for deleted items?  */
758 /**
759  * Get a list of items in the flipselector.
760  *
761  * @param obj The flipselector object
762  * @return The list of items, or NULL on errors.
763  *
764  * @ingroup Flipselector
765  */
766 EAPI const Eina_List *
767 elm_flipselector_items_get(const Evas_Object *obj)
768 {
769    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
770
771    Widget_Data *wd = elm_widget_data_get(obj);
772    if (!wd)
773      return NULL;
774
775    return wd->items;
776 }
777
778 /**
779  * Get the first item in the flipselector
780  *
781  * @param obj The flipselector object
782  * @return The first item, or NULL if none
783  *
784  * @ingroup Flipselector
785  */
786 EAPI Elm_Flipselector_Item *
787 elm_flipselector_first_item_get(const Evas_Object *obj)
788 {
789    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
790
791    Elm_Flipselector_Item *it;
792    Widget_Data *wd;
793    Eina_List *l;
794
795    wd = elm_widget_data_get(obj);
796    if (!wd || !wd->items)
797      return NULL;
798
799    EINA_LIST_FOREACH(wd->items, l, it)
800      {
801         if (it->deleted)
802           continue;
803
804         return it;
805      }
806
807    return NULL;
808 }
809
810 /**
811  * Get the last item in the flipselector
812  *
813  * @param obj The flipselector object
814  * @return The last item, or NULL if none
815  *
816  * @ingroup Flipselector
817  */
818 EAPI Elm_Flipselector_Item *
819 elm_flipselector_last_item_get(const Evas_Object *obj)
820 {
821    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
822
823    Elm_Flipselector_Item *it;
824    Widget_Data *wd;
825    Eina_List *l;
826
827    wd = elm_widget_data_get(obj);
828    if (!wd || !wd->items)
829      return NULL;
830
831    EINA_LIST_REVERSE_FOREACH(wd->items, l, it)
832      {
833         if (it->deleted)
834           continue;
835
836         return it;
837      }
838
839    return NULL;
840 }
841
842 /**
843  * Get the selected item in a flipselector.
844  *
845  * @param obj The flipselector object
846  * @return The selected item, or NULL if none
847  *
848  * @ingroup Flipselector
849  */
850 EAPI Elm_Flipselector_Item *
851 elm_flipselector_selected_item_get(const Evas_Object *obj)
852 {
853    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
854
855    Widget_Data *wd = elm_widget_data_get(obj);
856    if (!wd || !wd->current)
857      return NULL;
858
859    return DATA_GET(wd->current);
860 }
861
862 /**
863  * Set the selected state of an item
864  *
865  * This sets the selected state (EINA_TRUE selected, EINA_FALSE not selected)
866  * of the given item @p item.
867  * If a new item is selected the previosly selected will be unselected.
868  * If the item @p item is unselected, the first item will be selected.
869  *
870  * @param item The item
871  * @param selected The selected state
872  *
873  * @ingroup Flipselector
874  */
875 EAPI void
876 elm_flipselector_item_selected_set(Elm_Flipselector_Item *item, Eina_Bool selected)
877 {
878    ELM_FLIPSELECTOR_ITEM_CHECK_DELETED_RETURN(item);
879
880    Elm_Flipselector_Item *_item, *cur;
881    int flipside = MSG_FLIP_UP;
882    Widget_Data *wd;
883    Eina_List *l;
884
885    wd = elm_widget_data_get(item->base.widget);
886    if (!wd)
887      return;
888
889    cur = DATA_GET(wd->current);
890    if ((selected) && (cur == item))
891      return;
892
893    _flipselector_walk(wd);
894
895    if ((!selected) && (cur == item))
896      {
897         EINA_LIST_FOREACH(wd->items, l, _item)
898           {
899              if (!_item->deleted)
900                {
901                   wd->current = l;
902                   _send_msg(wd, MSG_FLIP_UP, (char *)_item->label);
903                   break;
904                }
905           }
906         _flipselector_unwalk(wd);
907         return;
908      }
909
910    EINA_LIST_FOREACH(wd->items, l, _item)
911      {
912         if (_item == cur)
913           flipside = MSG_FLIP_DOWN;
914
915         if (_item == item)
916           {
917              wd->current = l;
918              _send_msg(wd, flipside, (char *)item->label);
919              break;
920           }
921      }
922
923    _flipselector_unwalk(wd);
924 }
925
926 /*
927  * Get the selected state of @p item.
928  *
929  * @param item The flipselector item
930  * @return If true, the item is selected
931  *
932  * @ingroup Flipselector
933  */
934 EAPI Eina_Bool
935 elm_flipselector_item_selected_get(const Elm_Flipselector_Item *item)
936 {
937    ELM_FLIPSELECTOR_ITEM_CHECK_DELETED_RETURN(item, EINA_FALSE);
938    Widget_Data *wd;
939
940    wd = elm_widget_data_get(item->base.widget);
941    if (!wd) return EINA_FALSE;
942    return (eina_list_data_get(wd->current) == item);
943 }
944
945 /**
946  * Delete a given item from a flipselector.
947  *
948  * @param item The item
949  *
950  * @ingroup Flipselector
951  */
952 EAPI void
953 elm_flipselector_item_del(Elm_Flipselector_Item *item)
954 {
955    ELM_FLIPSELECTOR_ITEM_CHECK_DELETED_RETURN(item);
956
957    Widget_Data *wd;
958
959    wd = elm_widget_data_get(item->base.widget);
960    if (!wd)
961      return;
962
963    if (wd->walking > 0)
964      {
965         item->deleted = EINA_TRUE;
966         return;
967      }
968
969    _flipselector_walk(wd);
970
971    wd->items = eina_list_remove(wd->items, item);
972    _item_free(item);
973    _sentinel_eval(wd);
974
975    _flipselector_unwalk(wd);
976 }
977
978 /**
979  * Get the label of a given flipselector item.
980  *
981  * @param item The item
982  * @return The label of a given item, or NULL if none
983  *
984  * @ingroup Flipselector
985  */
986 EAPI const char *
987 elm_flipselector_item_label_get(const Elm_Flipselector_Item *item)
988 {
989    ELM_FLIPSELECTOR_ITEM_CHECK_DELETED_RETURN(item, NULL);
990
991    Elm_Flipselector_Item *_item;
992    Widget_Data *wd;
993    Eina_List *l;
994
995    wd = elm_widget_data_get(item->base.widget);
996    if ((!wd) || (!wd->items))
997      return NULL;
998
999    EINA_LIST_FOREACH(wd->items, l, _item)
1000       if (_item == item)
1001         return item->label;
1002
1003    return NULL;
1004 }
1005
1006 /**
1007  * Set the label of a given flipselector item.
1008  *
1009  * @param item The item
1010  * @param label The text label string in UTF-8
1011  *
1012  * @ingroup Flipselector
1013  */
1014 EAPI void
1015 elm_flipselector_item_label_set(Elm_Flipselector_Item *item, const char *label)
1016 {
1017    ELM_FLIPSELECTOR_ITEM_CHECK_DELETED_RETURN(item);
1018
1019    Widget_Data *wd;
1020    Eina_List *l;
1021
1022    if ((!item) || (!label))
1023      return;
1024
1025    wd = elm_widget_data_get(item->base.widget);
1026    if ((!wd) || (!wd->items))
1027      return;
1028
1029    l = eina_list_data_find_list(wd->items, item);
1030    if (!l)
1031      return;
1032
1033    eina_stringshare_del(item->label);
1034    item->label = eina_stringshare_add_length(label, wd->max_len);
1035
1036    if (strlen(label) >
1037        strlen(elm_flipselector_item_label_get(DATA_GET(wd->sentinel))))
1038      wd->sentinel = l;
1039
1040    if (wd->current == l)
1041      {
1042         _update_view(item->base.widget);
1043         _sizing_eval(wd->self);
1044      }
1045
1046    return;
1047 }
1048
1049 /**
1050  * Gets the item before @p item in a flipselector.
1051  *
1052  * @param item The item
1053  * @return The item before the item @p item
1054  *
1055  * @ingroup Flipselector
1056  */
1057 EAPI Elm_Flipselector_Item *
1058 elm_flipselector_item_prev_get(Elm_Flipselector_Item *item)
1059 {
1060    ELM_FLIPSELECTOR_ITEM_CHECK_DELETED_RETURN(item, NULL);
1061
1062    Elm_Flipselector_Item *_item;
1063    Widget_Data *wd;
1064    Eina_List *l;
1065
1066    wd = elm_widget_data_get(item->base.widget);
1067    if ((!wd) || (!wd->items))
1068      return NULL;
1069
1070    EINA_LIST_FOREACH(wd->items, l, _item)
1071       if (_item == item)
1072         {
1073            l = eina_list_prev(l);
1074            if (!l)
1075              return NULL;
1076            return DATA_GET(l);
1077         }
1078
1079    return NULL;
1080 }
1081
1082 /**
1083  * Gets the item after @p item in a flipselector.
1084  *
1085  * @param item The item
1086  * @return The item after the item @p item
1087  *
1088  * @ingroup Flipselector
1089  */
1090 EAPI Elm_Flipselector_Item *
1091 elm_flipselector_item_next_get(Elm_Flipselector_Item *item)
1092 {
1093    ELM_FLIPSELECTOR_ITEM_CHECK_DELETED_RETURN(item, NULL);
1094
1095    Elm_Flipselector_Item *_item;
1096    Widget_Data *wd;
1097    Eina_List *l;
1098
1099    wd = elm_widget_data_get(item->base.widget);
1100    if ((!wd) || (!wd->items))
1101      return NULL;
1102
1103    EINA_LIST_FOREACH(wd->items, l, _item)
1104       if (_item == item)
1105         {
1106            l = eina_list_next(l);
1107            if (!l)
1108              return NULL;
1109            return DATA_GET(l);
1110         }
1111
1112    return NULL;
1113 }
1114
1115 /**
1116  * Set the flipping interval for the flipselector.
1117  *
1118  * @param obj The flipselector object
1119  * @param interval The interval value in seconds
1120  *
1121  * The interval value is decreased while the user flips the widget up
1122  * or down repeatedly. The next interval value is the previous
1123  * interval / 1.05, so it speeds up a bit. Default value is 0.85
1124  * seconds.
1125  *
1126  * @ingroup Flipselector
1127  */
1128 EAPI void
1129 elm_flipselector_interval_set(Evas_Object *obj, double interval)
1130 {
1131    ELM_CHECK_WIDTYPE(obj, widtype);
1132
1133    Widget_Data *wd = elm_widget_data_get(obj);
1134    if (!wd)
1135      return;
1136
1137    wd->first_interval = interval;
1138 }
1139
1140 /**
1141  * Get the flipping interval of the flipselector.
1142  *
1143  * @param obj The flipselector object
1144  * @return The value of the first interval in seconds
1145  *
1146  * The interval value is decreased while the user flips the widget up
1147  * or down repeatedly. The next interval value is the previous
1148  * interval / 1.05, so it speeds up a bit. Default value is 0.85
1149  * seconds.
1150  *
1151  * @ingroup Flipselector
1152  */
1153 EAPI double
1154 elm_flipselector_interval_get(const Evas_Object *obj)
1155 {
1156    ELM_CHECK_WIDTYPE(obj, widtype) 0.0;
1157
1158    Widget_Data *wd = elm_widget_data_get(obj);
1159
1160    if (!wd)
1161      return 0.0;
1162    return wd->first_interval;
1163 }