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