Merge "[TC] Build Options are modified."
[framework/uifw/elementary.git] / src / lib / elm_list.c
1 #include <Elementary.h>
2 #include "elm_priv.h"
3
4 #define SWIPE_MOVES 12
5
6 /**
7  * @defgroup List List
8  * @ingroup Elementary
9  *
10  * A list is a very simple type of list widget.  For more robust
11  * lists, @ref Genlist should probably be used.
12  */
13
14 typedef struct _Widget_Data Widget_Data;
15
16 struct _Widget_Data
17 {
18    Evas_Object *scr, *box, *self;
19    Eina_List *items, *selected, *to_delete;
20    Elm_List_Item *last_selected_item;
21    Elm_List_Mode mode;
22    Elm_List_Mode h_mode;
23    Evas_Coord minw[2], minh[2];
24    Eina_Bool scr_minw : 1;
25    Eina_Bool scr_minh : 1;
26    int walking;
27    int movements;
28    struct
29    {
30       Evas_Coord x, y;
31    } history[SWIPE_MOVES];
32    Eina_Bool swipe : 1;
33    Eina_Bool fix_pending : 1;
34    Eina_Bool on_hold : 1;
35    Eina_Bool multi : 1;
36    Eina_Bool always_select : 1;
37    Eina_Bool longpressed : 1;
38    Eina_Bool wasselected : 1;
39 };
40
41 struct _Elm_List_Item
42 {
43    Elm_Widget_Item base;
44    Widget_Data *wd;
45    Eina_List *node;
46    const char *label;
47    Evas_Object *icon, *end;
48    Evas_Smart_Cb func;
49    Ecore_Timer *long_timer;
50    Ecore_Timer *swipe_timer;
51    Eina_Bool deleted : 1;
52    Eina_Bool disabled : 1;
53    Eina_Bool even : 1;
54    Eina_Bool is_even : 1;
55    Eina_Bool is_separator : 1;
56    Eina_Bool fixed : 1;
57    Eina_Bool selected : 1;
58    Eina_Bool hilighted : 1;
59    Eina_Bool dummy_icon : 1;
60    Eina_Bool dummy_end : 1;
61 };
62
63 static const char *widtype = NULL;
64 static void _del_hook(Evas_Object *obj);
65 static void _theme_hook(Evas_Object *obj);
66 static void _sizing_eval(Evas_Object *obj);
67 static void _disable_hook(Evas_Object *obj);
68 static void _on_focus_hook(void *data, Evas_Object *obj);
69 static void _signal_emit_hook(Evas_Object *obj, const char *emission, const char *source);
70 static void _changed_size_hints(void *data, Evas *e, Evas_Object *obj, void *event_info);
71 static void _sub_del(void *data, Evas_Object *obj, void *event_info);
72 static void _fix_items(Evas_Object *obj);
73 static void _mouse_down(void *data, Evas *evas, Evas_Object *obj, void *event_info);
74 static void _mouse_up(void *data, Evas *evas, Evas_Object *obj, void *event_info);
75 static void _mouse_move(void *data, Evas *evas, Evas_Object *obj, void *event_info);
76 static void _scroll_edge_left(void *data, Evas_Object *scr, void *event_info);
77 static void _scroll_edge_right(void *data, Evas_Object *scr, void *event_info);
78 static void _scroll_edge_top(void *data, Evas_Object *scr, void *event_info);
79 static void _scroll_edge_bottom(void *data, Evas_Object *scr, void *event_info);
80 static Eina_Bool _item_multi_select_up(Widget_Data *wd);
81 static Eina_Bool _item_multi_select_down(Widget_Data *wd);
82 static Eina_Bool _item_single_select_up(Widget_Data *wd);
83 static Eina_Bool _item_single_select_down(Widget_Data *wd);
84 static Eina_Bool _event_hook(Evas_Object *obj, Evas_Object *src,
85                              Evas_Callback_Type type, void *event_info);
86 static Eina_Bool _deselect_all_items(Widget_Data *wd);
87
88 #define ELM_LIST_ITEM_CHECK_DELETED_RETURN(it, ...)                     \
89   ELM_WIDGET_ITEM_WIDTYPE_CHECK_OR_RETURN(it, __VA_ARGS__);             \
90   if (it->deleted)                                                      \
91     {                                                                   \
92        ERR("ERROR: "#it" has been DELETED.\n");                         \
93        return __VA_ARGS__;                                              \
94     }
95
96 static inline void
97 _elm_list_item_free(Elm_List_Item *it)
98 {
99    evas_object_event_callback_del_full
100      (it->base.view, EVAS_CALLBACK_MOUSE_DOWN, _mouse_down, it);
101    evas_object_event_callback_del_full
102      (it->base.view, EVAS_CALLBACK_MOUSE_UP, _mouse_up, it);
103    evas_object_event_callback_del_full
104      (it->base.view, EVAS_CALLBACK_MOUSE_MOVE, _mouse_move, it);
105
106    if (it->icon)
107      evas_object_event_callback_del_full
108        (it->icon, EVAS_CALLBACK_CHANGED_SIZE_HINTS,
109         _changed_size_hints, it->base.widget);
110
111    if (it->end)
112      evas_object_event_callback_del_full
113        (it->end, EVAS_CALLBACK_CHANGED_SIZE_HINTS,
114         _changed_size_hints, it->base.widget);
115
116    eina_stringshare_del(it->label);
117
118    if (it->swipe_timer) ecore_timer_del(it->swipe_timer);
119    if (it->long_timer) ecore_timer_del(it->long_timer);
120    if (it->icon) evas_object_del(it->icon);
121    if (it->end) evas_object_del(it->end);
122
123    elm_widget_item_del(it);
124 }
125
126 static Eina_Bool
127 _event_hook(Evas_Object *obj, Evas_Object *src __UNUSED__, Evas_Callback_Type type, void *event_info)
128 {
129    if (type != EVAS_CALLBACK_KEY_DOWN) return EINA_FALSE;
130    Evas_Event_Key_Down *ev = event_info;
131    Widget_Data *wd = elm_widget_data_get(obj);
132    if (!wd) return EINA_FALSE;
133    if (!wd->items) return EINA_FALSE;
134    if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return EINA_FALSE;
135    if (elm_widget_disabled_get(obj)) return EINA_FALSE;
136
137    Elm_List_Item *it = NULL;
138    Evas_Coord x = 0;
139    Evas_Coord y = 0;
140    Evas_Coord step_x = 0;
141    Evas_Coord step_y = 0;
142    Evas_Coord v_w = 0;
143    Evas_Coord v_h = 0;
144    Evas_Coord page_x = 0;
145    Evas_Coord page_y = 0;
146
147    elm_smart_scroller_child_pos_get(wd->scr, &x, &y);
148    elm_smart_scroller_step_size_get(wd->scr, &step_x, &step_y);
149    elm_smart_scroller_page_size_get(wd->scr, &page_x, &page_y);
150    elm_smart_scroller_child_viewport_size_get(wd->scr, &v_w, &v_h);
151
152    /* TODO: fix logic for horizontal mode */
153    if ((!strcmp(ev->keyname, "Left")) ||
154        (!strcmp(ev->keyname, "KP_Left")))
155      {
156         if ((wd->h_mode) &&
157             (((evas_key_modifier_is_set(ev->modifiers, "Shift")) &&
158               (_item_multi_select_up(wd)))
159              || (_item_single_select_up(wd))))
160           {
161              ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
162              return EINA_TRUE;
163           }
164         else
165           x -= step_x;
166      }
167    else if ((!strcmp(ev->keyname, "Right")) ||
168             (!strcmp(ev->keyname, "KP_Right")))
169      {
170         if ((wd->h_mode) &&
171             (((evas_key_modifier_is_set(ev->modifiers, "Shift")) &&
172               (_item_multi_select_down(wd)))
173              || (_item_single_select_down(wd))))
174           {
175              ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
176              return EINA_TRUE;
177           }
178         else
179           x += step_x;
180      }
181    else if ((!strcmp(ev->keyname, "Up"))  ||
182             (!strcmp(ev->keyname, "KP_Up")))
183      {
184         if ((!wd->h_mode) &&
185             (((evas_key_modifier_is_set(ev->modifiers, "Shift")) &&
186               (_item_multi_select_up(wd)))
187              || (_item_single_select_up(wd))))
188           {
189              ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
190              return EINA_TRUE;
191           }
192         else
193           y -= step_y;
194      }
195    else if ((!strcmp(ev->keyname, "Down")) ||
196             (!strcmp(ev->keyname, "KP_Down")))
197      {
198         if ((!wd->h_mode) &&
199             (((evas_key_modifier_is_set(ev->modifiers, "Shift")) &&
200               (_item_multi_select_down(wd)))
201              || (_item_single_select_down(wd))))
202           {
203              ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
204              return EINA_TRUE;
205           }
206         else
207           y += step_y;
208      }
209    else if ((!strcmp(ev->keyname, "Home")) ||
210             (!strcmp(ev->keyname, "KP_Home")))
211      {
212         it = eina_list_data_get(wd->items);
213         elm_list_item_bring_in(it);
214         ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
215         return EINA_TRUE;
216      }
217    else if ((!strcmp(ev->keyname, "End")) ||
218             (!strcmp(ev->keyname, "KP_End")))
219      {
220         it = eina_list_data_get(eina_list_last(wd->items));
221         elm_list_item_bring_in(it);
222         ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
223         return EINA_TRUE;
224      }
225    else if ((!strcmp(ev->keyname, "Prior")) ||
226             (!strcmp(ev->keyname, "KP_Prior")))
227      {
228         if (wd->h_mode)
229           {
230              if (page_x < 0)
231                x -= -(page_x * v_w) / 100;
232              else
233                x -= page_x;
234           }
235         else
236           {
237              if (page_y < 0)
238                y -= -(page_y * v_h) / 100;
239              else
240                y -= page_y;
241           }
242      }
243    else if ((!strcmp(ev->keyname, "Next")) ||
244             (!strcmp(ev->keyname, "KP_Next")))
245      {
246         if (wd->h_mode)
247           {
248              if (page_x < 0)
249                x += -(page_x * v_w) / 100;
250              else
251                x += page_x;
252           }
253         else
254           {
255              if (page_y < 0)
256                y += -(page_y * v_h) / 100;
257              else
258                y += page_y;
259           }
260      }
261    else if (!strcmp(ev->keyname, "Escape"))
262      {
263        if (!_deselect_all_items(wd)) return EINA_FALSE;
264        ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
265        return EINA_TRUE;
266      }
267    else return EINA_FALSE;
268
269    ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
270    elm_smart_scroller_child_pos_set(wd->scr, x, y);
271    return EINA_TRUE;
272 }
273
274 static Eina_Bool
275 _deselect_all_items(Widget_Data *wd)
276 {
277    if (!wd->selected) return EINA_FALSE;
278    while(wd->selected)
279      elm_list_item_selected_set(wd->selected->data, EINA_FALSE);
280
281    return EINA_TRUE;
282 }
283
284 static Eina_Bool
285 _item_multi_select_up(Widget_Data *wd)
286 {
287    if (!wd->selected) return EINA_FALSE;
288    if (!wd->multi) return EINA_FALSE;
289
290    Elm_List_Item *prev = elm_list_item_prev(wd->last_selected_item);
291    if (!prev) return EINA_TRUE;
292
293    if (elm_list_item_selected_get(prev))
294      {
295         elm_list_item_selected_set(wd->last_selected_item, EINA_FALSE);
296         wd->last_selected_item = prev;
297         elm_list_item_show(wd->last_selected_item);
298      }
299    else
300      {
301         elm_list_item_selected_set(prev, EINA_TRUE);
302         elm_list_item_show(prev);
303      }
304    return EINA_TRUE;
305 }
306
307 static Eina_Bool
308 _item_multi_select_down(Widget_Data *wd)
309 {
310    if (!wd->selected) return EINA_FALSE;
311    if (!wd->multi) return EINA_FALSE;
312
313    Elm_List_Item *next = elm_list_item_next(wd->last_selected_item);
314    if (!next) return EINA_TRUE;
315
316    if (elm_list_item_selected_get(next))
317      {
318         elm_list_item_selected_set(wd->last_selected_item, EINA_FALSE);
319         wd->last_selected_item = next;
320         elm_list_item_show(wd->last_selected_item);
321      }
322    else
323      {
324         elm_list_item_selected_set(next, EINA_TRUE);
325         elm_list_item_show(next);
326      }
327    return EINA_TRUE;
328 }
329
330 static Eina_Bool
331 _item_single_select_up(Widget_Data *wd)
332 {
333    Elm_List_Item *prev;
334
335    if (!wd->selected) prev = eina_list_data_get(eina_list_last(wd->items));
336    else prev = elm_list_item_prev(wd->last_selected_item);
337
338    if (!prev) return EINA_FALSE;
339
340    _deselect_all_items(wd);
341
342    elm_list_item_selected_set(prev, EINA_TRUE);
343    elm_list_item_show(prev);
344    return EINA_TRUE;
345 }
346
347 static Eina_Bool
348 _item_single_select_down(Widget_Data *wd)
349 {
350    Elm_List_Item *next;
351
352    if (!wd->selected) next = eina_list_data_get(wd->items);
353    else next = elm_list_item_next(wd->last_selected_item);
354
355    if (!next) return EINA_FALSE;
356
357    _deselect_all_items(wd);
358
359    elm_list_item_selected_set(next, EINA_TRUE);
360    elm_list_item_show(next);
361    return EINA_TRUE;
362 }
363
364 static void
365 _elm_list_process_deletions(Widget_Data *wd)
366 {
367    Elm_List_Item *it;
368
369    wd->walking++; // avoid nested deletion and also _sub_del() fix_items
370
371    EINA_LIST_FREE(wd->to_delete, it)
372      {
373         elm_widget_item_pre_notify_del(it);
374
375         wd->items = eina_list_remove_list(wd->items, it->node);
376         _elm_list_item_free(it);
377      }
378
379    wd->walking--;
380 }
381
382 static inline void
383 _elm_list_walk(Widget_Data *wd)
384 {
385    if (wd->walking < 0)
386      {
387         ERR("ERROR: walking was negative. fixed!\n");
388         wd->walking = 0;
389      }
390    wd->walking++;
391 }
392
393 static inline void
394 _elm_list_unwalk(Widget_Data *wd)
395 {
396    wd->walking--;
397    if (wd->walking < 0)
398      {
399         ERR("ERROR: walking became negative. fixed!\n");
400         wd->walking = 0;
401      }
402
403    if (wd->walking)
404      return;
405
406    if (wd->to_delete)
407      _elm_list_process_deletions(wd);
408
409    if (wd->fix_pending)
410      {
411         wd->fix_pending = EINA_FALSE;
412         _fix_items(wd->self);
413         _sizing_eval(wd->self);
414      }
415 }
416
417 static void
418 _del_hook(Evas_Object *obj)
419 {
420    Widget_Data *wd = elm_widget_data_get(obj);
421    Elm_List_Item *it;
422    Eina_List *n;
423
424    if (!wd) return;
425    if (wd->walking)
426      ERR("ERROR: list deleted while walking.\n");
427
428    _elm_list_walk(wd);
429    EINA_LIST_FOREACH(wd->items, n, it) elm_widget_item_pre_notify_del(it);
430    _elm_list_unwalk(wd);
431    if (wd->to_delete)
432      ERR("ERROR: leaking nodes!\n");
433
434    EINA_LIST_FREE(wd->items, it) _elm_list_item_free(it);
435    eina_list_free(wd->selected);
436    free(wd);
437 }
438
439 static void
440 _show_region_hook(void *data, Evas_Object *obj)
441 {
442    Widget_Data *wd = elm_widget_data_get(data);
443    Evas_Coord x, y, w, h;
444    elm_widget_show_region_get(obj, &x, &y, &w, &h);
445    elm_smart_scroller_child_region_show(wd->scr, x, y, w, h);
446 }
447
448 static void
449 _disable_hook(Evas_Object *obj)
450 {
451    Widget_Data *wd = elm_widget_data_get(obj);
452    if (!wd) return;
453    if (elm_widget_disabled_get(obj))
454      {
455         _signal_emit_hook(obj, "elm,state,disabled", "elm");
456         elm_widget_scroll_freeze_push(obj);
457         elm_widget_scroll_hold_push(obj);
458         /* FIXME: if we get to have a way to only un-hilight items
459          * in the future, keeping them selected... */
460         _deselect_all_items(wd);
461      }
462    else
463      {
464         _signal_emit_hook(obj, "elm,state,enabled", "elm");
465         elm_widget_scroll_freeze_pop(obj);
466         elm_widget_scroll_hold_pop(obj);
467      }
468 }
469
470 static void
471 _sizing_eval(Evas_Object *obj)
472 {
473
474    Widget_Data *wd = elm_widget_data_get(obj);
475    if (!wd) return;
476    Evas_Coord  vw, vh, minw, minh, maxw, maxh, w, h, vmw, vmh;
477    double xw, yw;
478
479    evas_object_size_hint_min_get(wd->box, &minw, &minh);
480    evas_object_size_hint_max_get(wd->box, &maxw, &maxh);
481    evas_object_size_hint_weight_get(wd->box, &xw, &yw);
482    if (!wd->scr) return;
483    elm_smart_scroller_child_viewport_size_get(wd->scr, &vw, &vh);
484    if (xw > 0.0)
485      {
486         if ((minw > 0) && (vw < minw)) vw = minw;
487         else if ((maxw > 0) && (vw > maxw)) vw = maxw;
488      }
489    else if (minw > 0) vw = minw;
490    if (yw > 0.0)
491      {
492         if ((minh > 0) && (vh < minh)) vh = minh;
493         else if ((maxh > 0) && (vh > maxh)) vh = maxh;
494      }
495    else if (minh > 0) vh = minh;
496    evas_object_resize(wd->box, vw, vh);
497    w = -1;
498    h = -1;
499    edje_object_size_min_calc(elm_smart_scroller_edje_object_get(wd->scr),
500                              &vmw, &vmh);
501    if (wd->scr_minw) w = vmw + minw;
502    if (wd->scr_minh) h = vmh + minh;
503
504    evas_object_size_hint_max_get(obj, &maxw, &maxh);
505    if ((maxw > 0) && (w > maxw))
506      w = maxw;
507    if ((maxh > 0) && (h > maxh))
508      h = maxh;
509
510    evas_object_size_hint_min_set(obj, w, h);
511 }
512
513 static void
514 _signal_emit_hook(Evas_Object *obj, const char *emission, const char *source)
515 {
516    Widget_Data *wd = elm_widget_data_get(obj);
517    edje_object_signal_emit(elm_smart_scroller_edje_object_get(wd->scr),
518                            emission, source);
519 }
520
521 static void
522 _signal_callback_add_hook(Evas_Object *obj, const char *emission, const char *source, void (*func_cb) (void *data, Evas_Object *o, const char *emission, const char *source), void *data)
523 {
524    Widget_Data *wd = elm_widget_data_get(obj);
525    edje_object_signal_callback_add(elm_smart_scroller_edje_object_get(wd->scr),
526                                    emission, source, func_cb, data);
527 }
528
529 static void
530 _signal_callback_del_hook(Evas_Object *obj, const char *emission, const char *source, void (*func_cb) (void *data, Evas_Object *o, const char *emission, const char *source), void *data)
531 {
532    Widget_Data *wd = elm_widget_data_get(obj);
533    edje_object_signal_callback_del_full(
534                               elm_smart_scroller_edje_object_get(wd->scr),
535                               emission, source, func_cb, data);
536 }
537
538 static void
539 _theme_hook(Evas_Object *obj)
540 {
541    Widget_Data *wd = elm_widget_data_get(obj);
542    Elm_List_Item *it;
543    Eina_List *n;
544
545    if (!wd) return;
546    if (wd->scr)
547      {
548         Evas_Object *edj;
549         const char *str;
550
551         elm_smart_scroller_object_theme_set(obj, wd->scr, "list", "base",
552                                             elm_widget_style_get(obj));
553 //        edje_object_scale_set(wd->scr, elm_widget_scale_get(obj) * _elm_config->scale);
554         edj = elm_smart_scroller_edje_object_get(wd->scr);
555         str = edje_object_data_get(edj, "focus_highlight");
556         if ((str) && (!strcmp(str, "on")))
557           elm_widget_highlight_in_theme_set(obj, EINA_TRUE);
558         else
559           elm_widget_highlight_in_theme_set(obj, EINA_FALSE);
560         elm_object_style_set(wd->scr, elm_widget_style_get(obj));
561      }
562    EINA_LIST_FOREACH(wd->items, n, it)
563      {
564         edje_object_scale_set(it->base.view, elm_widget_scale_get(obj) * _elm_config->scale);
565         it->fixed = 0;
566      }
567    _fix_items(obj);
568    _sizing_eval(obj);
569 }
570
571 static void
572 _on_focus_hook(void *data __UNUSED__, Evas_Object *obj)
573 {
574    Widget_Data *wd = elm_widget_data_get(obj);
575    if (!wd) return;
576    if (elm_widget_focus_get(obj))
577      {
578         edje_object_signal_emit(wd->self, "elm,action,focus", "elm");
579         evas_object_focus_set(wd->self, EINA_TRUE);
580
581         if ((wd->selected) && (!wd->last_selected_item))
582           wd->last_selected_item = eina_list_data_get(wd->selected);
583      }
584    else
585      {
586         edje_object_signal_emit(wd->self, "elm,action,unfocus", "elm");
587         evas_object_focus_set(wd->self, EINA_FALSE);
588      }
589 }
590
591 static void
592 _changed_size_hints(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
593 {
594    Widget_Data *wd = elm_widget_data_get(data);
595    if (!wd) return;
596    _fix_items(data);
597    _sizing_eval(data);
598 }
599
600 static void
601 _sub_del(void *data __UNUSED__, Evas_Object *obj, void *event_info)
602 {
603    Widget_Data *wd = elm_widget_data_get(obj);
604    Evas_Object *sub = event_info;
605    const Eina_List *l;
606    Elm_List_Item *it;
607
608    if (!wd) return;
609    if (!sub) abort();
610    if (sub == wd->scr)
611      wd->scr = NULL;
612    else
613      {
614         EINA_LIST_FOREACH(wd->items, l, it)
615           {
616              if ((sub == it->icon) || (sub == it->end))
617                {
618                   if (it->icon == sub) it->icon = NULL;
619                   if (it->end == sub) it->end = NULL;
620                   evas_object_event_callback_del_full
621                     (sub, EVAS_CALLBACK_CHANGED_SIZE_HINTS, _changed_size_hints,
622                      obj);
623                   if (!wd->walking)
624                     {
625                        _fix_items(obj);
626                        _sizing_eval(obj);
627                     }
628                   else
629                     wd->fix_pending = EINA_TRUE;
630                   break;
631                }
632           }
633      }
634 }
635
636 static void
637 _item_hilight(Elm_List_Item *it)
638 {
639    Widget_Data *wd = elm_widget_data_get(it->base.widget);
640    const char *selectraise;
641
642    if (!wd) return;
643    ELM_LIST_ITEM_CHECK_DELETED_RETURN(it);
644    if (it->hilighted) return;
645    _elm_list_walk(wd);
646
647    edje_object_signal_emit(it->base.view, "elm,state,selected", "elm");
648    selectraise = edje_object_data_get(it->base.view, "selectraise");
649    if ((selectraise) && (!strcmp(selectraise, "on")))
650      evas_object_raise(it->base.view);
651    it->hilighted = EINA_TRUE;
652
653    _elm_list_unwalk(wd);
654 }
655
656 static void
657 _item_select(Elm_List_Item *it)
658 {
659    Widget_Data *wd = elm_widget_data_get(it->base.widget);
660
661    if (!wd) return;
662    ELM_LIST_ITEM_CHECK_DELETED_RETURN(it);
663    if (it->selected)
664      {
665         if (wd->always_select) goto call;
666         return;
667      }
668    it->selected = EINA_TRUE;
669    wd->selected = eina_list_append(wd->selected, it);
670    call:
671    _elm_list_walk(wd);
672
673    if (it->func) it->func((void *)it->base.data, it->base.widget, it);
674    evas_object_smart_callback_call(it->base.widget, "selected", it);
675
676    _elm_list_unwalk(wd);
677    it->wd->last_selected_item = it;
678 }
679
680 static void
681 _item_unselect(Elm_List_Item *it)
682 {
683    Widget_Data *wd = elm_widget_data_get(it->base.widget);
684    const char *stacking, *selectraise;
685
686    if (!wd) return;
687    ELM_LIST_ITEM_CHECK_DELETED_RETURN(it);
688    if (!it->hilighted) return;
689    _elm_list_walk(wd);
690
691    edje_object_signal_emit(it->base.view, "elm,state,unselected", "elm");
692    stacking = edje_object_data_get(it->base.view, "stacking");
693    selectraise = edje_object_data_get(it->base.view, "selectraise");
694    if ((selectraise) && (!strcmp(selectraise, "on")))
695      {
696         if ((stacking) && (!strcmp(stacking, "below")))
697            evas_object_lower(it->base.view);
698      }
699    it->hilighted = EINA_FALSE;
700    if (it->selected)
701      {
702         it->selected = EINA_FALSE;
703         wd->selected = eina_list_remove(wd->selected, it);
704         evas_object_smart_callback_call(it->base.widget, "unselected", it);
705      }
706
707    _elm_list_unwalk(wd);
708 }
709
710 static Eina_Bool
711 _swipe_cancel(void *data)
712 {
713    Elm_List_Item *it = data;
714    Widget_Data *wd = elm_widget_data_get(it->base.widget);
715
716    if (!wd) return ECORE_CALLBACK_CANCEL;
717    ELM_LIST_ITEM_CHECK_DELETED_RETURN(it, ECORE_CALLBACK_CANCEL);
718    wd->swipe = EINA_FALSE;
719    wd->movements = 0;
720    return ECORE_CALLBACK_RENEW;
721 }
722
723 static void
724 _mouse_move(void *data, Evas *evas __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info)
725 {
726    Elm_List_Item *it = data;
727    Widget_Data *wd = elm_widget_data_get(it->base.widget);
728    Evas_Event_Mouse_Move *ev = event_info;
729
730    if (!wd) return;
731    ELM_LIST_ITEM_CHECK_DELETED_RETURN(it);
732    if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD)
733      {
734         if (!wd->on_hold)
735           {
736              wd->on_hold = EINA_TRUE;
737              if (it->long_timer)
738                {
739                   ecore_timer_del(it->long_timer);
740                   it->long_timer = NULL;
741                }
742              if (!wd->wasselected)
743                _item_unselect(it);
744           }
745         if (wd->movements == SWIPE_MOVES) wd->swipe = EINA_TRUE;
746         else
747           {
748              wd->history[wd->movements].x = ev->cur.canvas.x;
749              wd->history[wd->movements].y = ev->cur.canvas.y;
750              if (abs((wd->history[wd->movements].x - wd->history[0].x)) > 40)
751                 wd->swipe = EINA_TRUE;
752              else
753                 wd->movements++;
754           }
755      }
756 }
757
758 static void
759 _scroll_edge_left(void *data, Evas_Object *scr __UNUSED__, void *event_info __UNUSED__)
760 {
761    Evas_Object *obj = data;
762    evas_object_smart_callback_call(obj, "scroll,edge,left", NULL);
763 }
764
765 static void
766 _scroll_edge_right(void *data, Evas_Object *scr __UNUSED__, void *event_info __UNUSED__)
767 {
768    Evas_Object *obj = data;
769    evas_object_smart_callback_call(obj, "scroll,edge,right", NULL);
770 }
771
772 static void
773 _scroll_edge_top(void *data, Evas_Object *scr __UNUSED__, void *event_info __UNUSED__)
774 {
775    Evas_Object *obj = data;
776    evas_object_smart_callback_call(obj, "scroll,edge,top", NULL);
777 }
778
779 static void
780 _scroll_edge_bottom(void *data, Evas_Object *scr __UNUSED__, void *event_info __UNUSED__)
781 {
782    Evas_Object *obj = data;
783    evas_object_smart_callback_call(obj, "scroll,edge,bottom", NULL);
784 }
785
786 static Eina_Bool
787 _long_press(void *data)
788 {
789    Elm_List_Item *it = data;
790    Widget_Data *wd = elm_widget_data_get(it->base.widget);
791
792    if (!wd)
793      goto end;
794
795    ELM_LIST_ITEM_CHECK_DELETED_RETURN(it, ECORE_CALLBACK_CANCEL);
796
797    it->long_timer = NULL;
798
799    if (it->disabled)
800      goto end;
801
802    wd->longpressed = EINA_TRUE;
803    evas_object_smart_callback_call(it->base.widget, "longpressed", it);
804
805  end:
806    return ECORE_CALLBACK_CANCEL;
807 }
808
809 static void
810 _swipe(Elm_List_Item *it)
811 {
812    int i, sum = 0;
813    Widget_Data *wd = elm_widget_data_get(it->base.widget);
814
815    ELM_LIST_ITEM_CHECK_DELETED_RETURN(it);
816    if (!wd) return;
817    wd->swipe = EINA_FALSE;
818    for (i = 0; i < wd->movements; i++)
819      {
820         sum += wd->history[i].x;
821         if (abs(wd->history[0].y - wd->history[i].y) > 10) return;
822      }
823
824    sum /= wd->movements;
825    if (abs(sum - wd->history[0].x) <= 10) return;
826    evas_object_smart_callback_call(it->base.widget, "swipe", it);
827 }
828
829 static void
830 _mouse_down(void *data, Evas *evas __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info)
831 {
832    Elm_List_Item *it = data;
833    Widget_Data *wd = elm_widget_data_get(it->base.widget);
834    Evas_Event_Mouse_Down *ev = event_info;
835
836    if (!wd) return;
837    ELM_LIST_ITEM_CHECK_DELETED_RETURN(it);
838    if (ev->button != 1) return;
839    if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) wd->on_hold = EINA_TRUE;
840    else wd->on_hold = EINA_FALSE;
841    if (wd->on_hold) return;
842    wd->wasselected = it->selected;
843    _item_hilight(it);
844    wd->longpressed = EINA_FALSE;
845    if (it->long_timer) ecore_timer_del(it->long_timer);
846    it->long_timer = ecore_timer_add(_elm_config->longpress_timeout, _long_press, it);
847    if (it->swipe_timer) ecore_timer_del(it->swipe_timer);
848    it->swipe_timer = ecore_timer_add(0.4, _swipe_cancel, it);
849    /* Always call the callbacks last - the user may delete our context! */
850    if (ev->flags & EVAS_BUTTON_DOUBLE_CLICK)
851      evas_object_smart_callback_call(it->base.widget, "clicked,double", it);
852    wd->swipe = EINA_FALSE;
853    wd->movements = 0;
854 }
855
856 static void
857 _mouse_up(void *data, Evas *evas __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info)
858 {
859    Elm_List_Item *it = data;
860    Widget_Data *wd = elm_widget_data_get(it->base.widget);
861    Evas_Event_Mouse_Up *ev = event_info;
862
863    if (!wd) return;
864    ELM_LIST_ITEM_CHECK_DELETED_RETURN(it);
865    if (ev->button != 1) return;
866    if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) wd->on_hold = EINA_TRUE;
867    else wd->on_hold = EINA_FALSE;
868    wd->longpressed = EINA_FALSE;
869    if (it->long_timer)
870      {
871         ecore_timer_del(it->long_timer);
872         it->long_timer = NULL;
873      }
874    if (it->swipe_timer)
875      {
876         ecore_timer_del(it->swipe_timer);
877         it->swipe_timer = NULL;
878      }
879    if (wd->on_hold)
880      {
881         if (wd->swipe) _swipe(data);
882         wd->on_hold = EINA_FALSE;
883         return;
884      }
885    if (wd->longpressed)
886      {
887         if (!wd->wasselected) _item_unselect(it);
888         wd->wasselected = 0;
889         return;
890      }
891
892    if (it->disabled)
893      return;
894    if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return;
895   
896    _elm_list_walk(wd); // watch out "return" before unwalk!
897
898    if (wd->multi)
899      {
900         if (!it->selected)
901           {
902              _item_hilight(it);
903              _item_select(it);
904           }
905         else _item_unselect(it);
906      }
907    else
908      {
909         if (!it->selected)
910           {
911              while (wd->selected)
912                 _item_unselect(wd->selected->data);
913              _item_hilight(it);
914              _item_select(it);
915           }
916         else
917           {
918              const Eina_List *l, *l_next;
919              Elm_List_Item *it2;
920
921              EINA_LIST_FOREACH_SAFE(wd->selected, l, l_next, it2)
922                 if (it2 != it) _item_unselect(it2);
923              _item_hilight(it);
924              _item_select(it);
925           }
926      }
927
928    _elm_list_unwalk(wd);
929 }
930
931 static Elm_List_Item *
932 _item_new(Evas_Object *obj, const char *label, Evas_Object *icon, Evas_Object *end, Evas_Smart_Cb func, const void *data)
933 {
934    Widget_Data *wd = elm_widget_data_get(obj);
935    Elm_List_Item *it;
936
937    if (!wd) return NULL;
938    it = elm_widget_item_new(obj, Elm_List_Item);
939    it->wd = wd;
940    it->label = eina_stringshare_add(label);
941    it->icon = icon;
942    it->end = end;
943    it->func = func;
944    it->base.data = data;
945    it->base.view = edje_object_add(evas_object_evas_get(obj));
946    evas_object_event_callback_add(it->base.view, EVAS_CALLBACK_MOUSE_DOWN,
947                                   _mouse_down, it);
948    evas_object_event_callback_add(it->base.view, EVAS_CALLBACK_MOUSE_UP,
949                                   _mouse_up, it);
950    evas_object_event_callback_add(it->base.view, EVAS_CALLBACK_MOUSE_MOVE,
951                                   _mouse_move, it);
952    evas_object_size_hint_weight_set(it->base.view, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
953    evas_object_size_hint_align_set(it->base.view, EVAS_HINT_FILL, EVAS_HINT_FILL);
954    if (it->icon)
955      {
956         elm_widget_sub_object_add(obj, it->icon);
957         evas_object_event_callback_add(it->icon, EVAS_CALLBACK_CHANGED_SIZE_HINTS,
958                                        _changed_size_hints, obj);
959      }
960    if (it->end)
961      {
962         elm_widget_sub_object_add(obj, it->end);
963         evas_object_event_callback_add(it->end, EVAS_CALLBACK_CHANGED_SIZE_HINTS,
964                                        _changed_size_hints, obj);
965      }
966    return it;
967 }
968
969 static void
970 _elm_list_mode_set_internal(Widget_Data *wd)
971 {
972    if (!wd->scr)
973      return;
974
975    if (wd->mode == ELM_LIST_LIMIT)
976      {
977         if (!wd->h_mode)
978           {
979              wd->scr_minw = EINA_TRUE;
980              wd->scr_minh = EINA_FALSE;
981           }
982         else
983           {
984              wd->scr_minw = EINA_FALSE;
985              wd->scr_minh = EINA_TRUE;
986           }
987      }
988    else if (wd->mode == ELM_LIST_EXPAND)
989      {
990         wd->scr_minw = EINA_TRUE;
991         wd->scr_minh = EINA_TRUE;
992      }
993    else
994      {
995         wd->scr_minw = EINA_FALSE;
996         wd->scr_minh = EINA_FALSE;
997      }
998
999    _sizing_eval(wd->self);
1000 }
1001
1002 static void
1003 _fix_items(Evas_Object *obj)
1004 {
1005    Widget_Data *wd = elm_widget_data_get(obj);
1006    const Eina_List *l;
1007    Elm_List_Item *it;
1008    Evas_Coord minw[2] = { 0, 0 }, minh[2] = { 0, 0 };
1009    Evas_Coord mw, mh;
1010    int i, redo = 0;
1011    const char *style = elm_widget_style_get(obj);
1012    const char *it_plain = wd->h_mode ? "h_item" : "item";
1013    const char *it_odd = wd->h_mode ? "h_item_odd" : "item_odd";
1014    const char *it_compress = wd->h_mode ? "h_item_compress" : "item_compress";
1015    const char *it_compress_odd = wd->h_mode ? "h_item_compress_odd" : "item_compress_odd";
1016
1017    if (!wd) return;
1018    if (wd->walking)
1019      {
1020         wd->fix_pending = EINA_TRUE;
1021         return;
1022      }
1023
1024    _elm_list_walk(wd); // watch out "return" before unwalk!
1025
1026    EINA_LIST_FOREACH(wd->items, l, it)
1027      {
1028         if (it->deleted) continue;
1029         if (it->icon)
1030           {
1031              evas_object_size_hint_min_get(it->icon, &mw, &mh);
1032              if (mw > minw[0]) minw[0] = mw;
1033              if (mh > minh[0]) minh[0] = mh;
1034           }
1035         if (it->end)
1036           {
1037              evas_object_size_hint_min_get(it->end, &mw, &mh);
1038              if (mw > minw[1]) minw[1] = mw;
1039              if (mh > minh[1]) minh[1] = mh;
1040           }
1041      }
1042
1043    if ((minw[0] != wd->minw[0]) || (minw[1] != wd->minw[1]) ||
1044        (minw[0] != wd->minh[0]) || (minh[1] != wd->minh[1]))
1045      {
1046         wd->minw[0] = minw[0];
1047         wd->minw[1] = minw[1];
1048         wd->minh[0] = minh[0];
1049         wd->minh[1] = minh[1];
1050         redo = 1;
1051      }
1052    i = 0;
1053    EINA_LIST_FOREACH(wd->items, l, it)
1054      {
1055         if (it->deleted)
1056           continue;
1057
1058         it->even = i & 0x1;
1059         if ((it->even != it->is_even) || (!it->fixed) || (redo))
1060           {
1061              const char *stacking;
1062
1063              /* FIXME: separators' themes seem to be b0rked */
1064              if (it->is_separator)
1065                _elm_theme_object_set(obj, it->base.view, "separator",
1066                                      wd->h_mode ? "horizontal" : "vertical",
1067                                      style);
1068              else if (wd->mode == ELM_LIST_COMPRESS)
1069                {
1070                   if (it->even)
1071                     _elm_theme_object_set(obj, it->base.view, "list",
1072                                           it_compress, style);
1073                   else
1074                     _elm_theme_object_set(obj, it->base.view, "list",
1075                                           it_compress_odd, style);
1076                }
1077              else
1078                {
1079                   if (it->even)
1080                     _elm_theme_object_set(obj, it->base.view, "list", it_plain,
1081                                           style);
1082                   else
1083                     _elm_theme_object_set(obj, it->base.view, "list", it_odd,
1084                                           style);
1085                }
1086              stacking = edje_object_data_get(it->base.view, "stacking");
1087              if (stacking)
1088                {
1089                   if (!strcmp(stacking, "below"))
1090                     evas_object_lower(it->base.view);
1091                   else if (!strcmp(stacking, "above"))
1092                     evas_object_raise(it->base.view);
1093                }
1094              edje_object_part_text_set(it->base.view, "elm.text", it->label);
1095
1096              if ((!it->icon) && (minh[0] > 0))
1097                {
1098                   it->icon = evas_object_rectangle_add(evas_object_evas_get(it->base.view));
1099                   evas_object_color_set(it->icon, 0, 0, 0, 0);
1100                   it->dummy_icon = EINA_TRUE;
1101                }
1102              if ((!it->end) && (minh[1] > 0))
1103                {
1104                   it->end = evas_object_rectangle_add(evas_object_evas_get(it->base.view));
1105                   evas_object_color_set(it->end, 0, 0, 0, 0);
1106                   it->dummy_end = EINA_TRUE;
1107                }
1108              if (it->icon)
1109                {
1110                   evas_object_size_hint_min_set(it->icon, minw[0], minh[0]);
1111                   evas_object_size_hint_max_set(it->icon, 99999, 99999);
1112                   edje_object_part_swallow(it->base.view, "elm.swallow.icon", it->icon);
1113                }
1114              if (it->end)
1115                {
1116                   evas_object_size_hint_min_set(it->end, minw[1], minh[1]);
1117                   evas_object_size_hint_max_set(it->end, 99999, 99999);
1118                   edje_object_part_swallow(it->base.view, "elm.swallow.end", it->end);
1119                }
1120              if (!it->fixed)
1121                {
1122                   // this may call up user and it may modify the list item
1123                   // but we're safe as we're flagged as walking.
1124                   // just don't process further
1125                   edje_object_message_signal_process(it->base.view);
1126                   if (it->deleted)
1127                     continue;
1128                   mw = mh = -1;
1129                   elm_coords_finger_size_adjust(1, &mw, 1, &mh);
1130                   edje_object_size_min_restricted_calc(it->base.view, &mw, &mh, mw, mh);
1131                   elm_coords_finger_size_adjust(1, &mw, 1, &mh);
1132                   evas_object_size_hint_min_set(it->base.view, mw, mh);
1133                   evas_object_show(it->base.view);
1134                }
1135              if ((it->selected) || (it->hilighted))
1136                {
1137                   const char *selectraise;
1138
1139                   // this may call up user and it may modify the list item
1140                   // but we're safe as we're flagged as walking.
1141                   // just don't process further
1142                   edje_object_signal_emit(it->base.view, "elm,state,selected", "elm");
1143                   if (it->deleted)
1144                     continue;
1145
1146                   selectraise = edje_object_data_get(it->base.view, "selectraise");
1147                   if ((selectraise) && (!strcmp(selectraise, "on")))
1148                     evas_object_raise(it->base.view);
1149                }
1150              if (it->disabled)
1151                edje_object_signal_emit(it->base.view, "elm,state,disabled",
1152                                        "elm");
1153
1154              it->fixed = EINA_TRUE;
1155              it->is_even = it->even;
1156           }
1157         i++;
1158      }
1159
1160    _elm_list_unwalk(wd);
1161
1162    mw = 0; mh = 0;
1163    evas_object_size_hint_min_get(wd->box, &mw, &mh);
1164
1165    _elm_list_mode_set_internal(wd);
1166 }
1167
1168 static void
1169 _hold_on(void *data __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__)
1170 {
1171    Widget_Data *wd = elm_widget_data_get(obj);
1172    if (!wd) return;
1173    if (wd->scr)
1174      elm_smart_scroller_hold_set(wd->scr, EINA_TRUE);
1175 }
1176
1177 static void
1178 _hold_off(void *data __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__)
1179 {
1180    Widget_Data *wd = elm_widget_data_get(obj);
1181    if (!wd) return;
1182    if (wd->scr)
1183      elm_smart_scroller_hold_set(wd->scr, EINA_FALSE);
1184 }
1185
1186 static void
1187 _freeze_on(void *data __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__)
1188 {
1189    Widget_Data *wd = elm_widget_data_get(obj);
1190    if (!wd) return;
1191    if (wd->scr)
1192      elm_smart_scroller_freeze_set(wd->scr, EINA_TRUE);
1193 }
1194
1195 static void
1196 _freeze_off(void *data __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__)
1197 {
1198    Widget_Data *wd = elm_widget_data_get(obj);
1199    if (!wd) return;
1200    if (wd->scr)
1201      elm_smart_scroller_freeze_set(wd->scr, EINA_FALSE);
1202 }
1203
1204 static void
1205 _resize(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
1206 {
1207    _sizing_eval(data);
1208 }
1209
1210 /**
1211  * Adds a list object.
1212  *
1213  * @param parent The parent object
1214  * @return The created object or NULL upon failure
1215  *
1216  * @ingroup List
1217  */
1218 EAPI Evas_Object *
1219 elm_list_add(Evas_Object *parent)
1220 {
1221    Evas_Object *obj;
1222    Evas *e;
1223    Widget_Data *wd;
1224    Evas_Coord minw, minh;
1225
1226    EINA_SAFETY_ON_NULL_RETURN_VAL(parent, NULL);
1227
1228    wd = ELM_NEW(Widget_Data);
1229    e = evas_object_evas_get(parent);
1230    if (!e) return NULL;
1231    wd->self = obj = elm_widget_add(e);
1232    ELM_SET_WIDTYPE(widtype, "list");
1233    elm_widget_type_set(obj, "list");
1234    elm_widget_sub_object_add(parent, obj);
1235    elm_widget_on_focus_hook_set(obj, _on_focus_hook, NULL);
1236    elm_widget_data_set(obj, wd);
1237    elm_widget_del_hook_set(obj, _del_hook);
1238    elm_widget_theme_hook_set(obj, _theme_hook);
1239    elm_widget_disable_hook_set(obj, _disable_hook);
1240    elm_widget_can_focus_set(obj, EINA_TRUE);
1241    elm_widget_signal_emit_hook_set(obj, _signal_emit_hook);
1242    elm_widget_signal_callback_add_hook_set(obj, _signal_callback_add_hook);
1243    elm_widget_signal_callback_del_hook_set(obj, _signal_callback_del_hook);
1244    elm_widget_event_hook_set(obj, _event_hook);
1245
1246    wd->scr = elm_smart_scroller_add(e);
1247    elm_smart_scroller_widget_set(wd->scr, obj);
1248    elm_widget_resize_object_set(obj, wd->scr);
1249    evas_object_event_callback_add(wd->scr, EVAS_CALLBACK_CHANGED_SIZE_HINTS,
1250                                   _changed_size_hints, obj);
1251    edje_object_size_min_calc(elm_smart_scroller_edje_object_get(wd->scr), &minw, &minh);
1252    evas_object_size_hint_min_set(obj, minw, minh);
1253    evas_object_event_callback_add(obj, EVAS_CALLBACK_RESIZE, _resize, obj);
1254
1255    elm_smart_scroller_bounce_allow_set(wd->scr, EINA_FALSE,
1256                                        _elm_config->thumbscroll_bounce_enable);
1257
1258    wd->box = elm_box_add(parent);
1259    elm_box_homogenous_set(wd->box, 1);
1260    evas_object_size_hint_weight_set(wd->box, EVAS_HINT_EXPAND, 0.0);
1261    evas_object_size_hint_align_set(wd->box, EVAS_HINT_FILL, 0.0);
1262    elm_widget_on_show_region_hook_set(wd->box, _show_region_hook, obj);
1263    elm_widget_sub_object_add(obj, wd->box);
1264    elm_smart_scroller_child_set(wd->scr, wd->box);
1265    evas_object_event_callback_add(wd->box, EVAS_CALLBACK_CHANGED_SIZE_HINTS,
1266                                   _changed_size_hints, obj);
1267
1268    evas_object_show(wd->box);
1269
1270    _theme_hook(obj);
1271
1272    wd->mode = ELM_LIST_SCROLL;
1273
1274    evas_object_smart_callback_add(wd->scr, "edge,left", _scroll_edge_left, obj);
1275    evas_object_smart_callback_add(wd->scr, "edge,right", _scroll_edge_right, obj);
1276    evas_object_smart_callback_add(wd->scr, "edge,top", _scroll_edge_top, obj);
1277    evas_object_smart_callback_add(wd->scr, "edge,bottom", _scroll_edge_bottom, obj);
1278
1279    evas_object_smart_callback_add(obj, "sub-object-del", _sub_del, obj);
1280    evas_object_smart_callback_add(obj, "scroll-hold-on", _hold_on, obj);
1281    evas_object_smart_callback_add(obj, "scroll-hold-off", _hold_off, obj);
1282    evas_object_smart_callback_add(obj, "scroll-freeze-on", _freeze_on, obj);
1283    evas_object_smart_callback_add(obj, "scroll-freeze-off", _freeze_off, obj);
1284
1285    _sizing_eval(obj);
1286    return obj;
1287 }
1288
1289 /**
1290  * Appends an item to the list object.
1291  *
1292  * @param obj The list object
1293  * @param label The label of the list item
1294  * @param icon The icon object to use for the left side of the item
1295  * @param end The icon object to use for the right side of the item
1296  * @param func The function to call when the item is clicked
1297  * @param data The data to associate with the item for related callbacks
1298  *
1299  * @return The created item or NULL upon failure
1300  *
1301  * @ingroup List
1302  */
1303 EAPI Elm_List_Item *
1304 elm_list_item_append(Evas_Object *obj, const char *label, Evas_Object *icon, Evas_Object *end, Evas_Smart_Cb func, const void *data)
1305 {
1306    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
1307    Widget_Data *wd = elm_widget_data_get(obj);
1308    Elm_List_Item *it = _item_new(obj, label, icon, end, func, data);
1309
1310    wd->items = eina_list_append(wd->items, it);
1311    it->node = eina_list_last(wd->items);
1312    elm_box_pack_end(wd->box, it->base.view);
1313    return it;
1314 }
1315
1316 /**
1317  * Prepends an item to the list object.
1318  *
1319  * @param obj The list object
1320  * @param label The label of the list item
1321  * @param icon The icon object to use for the left side of the item
1322  * @param end The icon object to use for the right side of the item
1323  * @param func The function to call when the item is clicked
1324  * @param data The data to associate with the item for related callbacks
1325  *
1326  * @return The created item or NULL upon failure
1327  *
1328  * @ingroup List
1329  */
1330 EAPI Elm_List_Item *
1331 elm_list_item_prepend(Evas_Object *obj, const char *label, Evas_Object *icon, Evas_Object *end, Evas_Smart_Cb func, const void *data)
1332 {
1333    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
1334    Widget_Data *wd = elm_widget_data_get(obj);
1335    Elm_List_Item *it = _item_new(obj, label, icon, end, func, data);
1336
1337    wd->items = eina_list_prepend(wd->items, it);
1338    it->node = wd->items;
1339    elm_box_pack_start(wd->box, it->base.view);
1340    return it;
1341 }
1342
1343 /**
1344  * Inserts an item into the list object before @p before.
1345  *
1346  * @param obj The list object
1347  * @param before The list item to insert before
1348  * @param label The label of the list item
1349  * @param icon The icon object to use for the left side of the item
1350  * @param end The icon object to use for the right side of the item
1351  * @param func The function to call when the item is clicked
1352  * @param data The data to associate with the item for related callbacks
1353  *
1354  * @return The created item or NULL upon failure
1355  *
1356  * @ingroup List
1357  */
1358 EAPI Elm_List_Item *
1359 elm_list_item_insert_before(Evas_Object *obj, Elm_List_Item *before, const char *label, Evas_Object *icon, Evas_Object *end, Evas_Smart_Cb func, const void *data)
1360 {
1361    Widget_Data *wd;
1362    Elm_List_Item *it;
1363
1364    EINA_SAFETY_ON_NULL_RETURN_VAL(before, NULL);
1365    if (!before->node) return NULL;
1366    ELM_LIST_ITEM_CHECK_DELETED_RETURN(before, NULL);
1367
1368    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
1369    wd = elm_widget_data_get(obj);
1370    if (!wd) return NULL;
1371    it = _item_new(obj, label, icon, end, func, data);
1372    wd->items = eina_list_prepend_relative_list(wd->items, it, before->node);
1373    it->node = before->node->prev;
1374    elm_box_pack_before(wd->box, it->base.view, before->base.view);
1375    return it;
1376 }
1377
1378 /**
1379  * Inserts an item into the list object after @p after.
1380  *
1381  * @param obj The list object
1382  * @param after The list item to insert after
1383  * @param label The label of the list item
1384  * @param icon The icon object to use for the left side of the item
1385  * @param end The icon object to use for the right side of the item
1386  * @param func The function to call when the item is clicked
1387  * @param data The data to associate with the item for related callbacks
1388  *
1389  * @return The created item or NULL upon failure
1390  *
1391  * @ingroup List
1392  */
1393 EAPI Elm_List_Item *
1394 elm_list_item_insert_after(Evas_Object *obj, Elm_List_Item *after, const char *label, Evas_Object *icon, Evas_Object *end, Evas_Smart_Cb func, const void *data)
1395 {
1396    Widget_Data *wd;
1397    Elm_List_Item *it;
1398
1399    EINA_SAFETY_ON_NULL_RETURN_VAL(after, NULL);
1400    if (!after->node) return NULL;
1401    ELM_LIST_ITEM_CHECK_DELETED_RETURN(after, NULL);
1402
1403    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
1404    wd = elm_widget_data_get(obj);
1405    if (!wd) return NULL;
1406    it = _item_new(obj, label, icon, end, func, data);
1407    wd->items = eina_list_append_relative_list(wd->items, it, after->node);
1408    it->node = after->node->next;
1409    elm_box_pack_after(wd->box, it->base.view, after->base.view);
1410    return it;
1411 }
1412
1413 /**
1414  * Insert a new item into the sorted list object.
1415  *
1416  * @param obj The list object
1417  * @param label The label of the list item
1418  * @param icon The icon object to use for the left side of the item
1419  * @param end The icon object to use for the right side of the item
1420  * @param func The function to call when the item is clicked
1421  * @param data The data to associate with the item for related callbacks
1422  * @param cmp_func The function called for the sort.
1423  *
1424  * @return The created item or NULL upon failure
1425  *
1426  * @ingroup List
1427  */
1428 EAPI Elm_List_Item *
1429 elm_list_item_sorted_insert(Evas_Object *obj, const char *label, Evas_Object *icon, Evas_Object *end, Evas_Smart_Cb func, const void *data, Eina_Compare_Cb cmp_func)
1430 {
1431    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
1432    Widget_Data *wd = elm_widget_data_get(obj);
1433    Elm_List_Item *it = _item_new(obj, label, icon, end, func, data);
1434    Eina_List *l;
1435
1436    wd->items = eina_list_sorted_insert(wd->items, cmp_func, it);
1437    l = eina_list_data_find_list(wd->items, it);
1438    l = eina_list_next(l);
1439    if (!l)
1440      {
1441         it->node = eina_list_last(wd->items);
1442         elm_box_pack_end(wd->box, it->base.view);
1443      }
1444    else
1445      {
1446         Elm_List_Item *before = eina_list_data_get(l);
1447         it->node = before->node->prev;
1448         elm_box_pack_before(wd->box, it->base.view, before->base.view);
1449      }
1450    return it;
1451 }
1452
1453 /**
1454  * Clears a list of all items.
1455  *
1456  * @param obj The list object
1457  *
1458  * @ingroup List
1459  */
1460 EAPI void
1461 elm_list_clear(Evas_Object *obj)
1462 {
1463    ELM_CHECK_WIDTYPE(obj, widtype);
1464    Widget_Data *wd = elm_widget_data_get(obj);
1465    Elm_List_Item *it;
1466
1467    if (!wd) return;
1468    if (!wd->items) return;
1469
1470    eina_list_free(wd->selected);
1471    wd->selected = NULL;
1472
1473    if (wd->walking > 0)
1474      {
1475         Eina_List *n;
1476
1477         EINA_LIST_FOREACH(wd->items, n, it)
1478           {
1479              if (it->deleted) continue;
1480              it->deleted = EINA_TRUE;
1481              wd->to_delete = eina_list_append(wd->to_delete, it);
1482           }
1483         return;
1484      }
1485
1486    _elm_list_walk(wd);
1487
1488    EINA_LIST_FREE(wd->items, it)
1489      {
1490         elm_widget_item_pre_notify_del(it);
1491         _elm_list_item_free(it);
1492      }
1493
1494    _elm_list_unwalk(wd);
1495
1496    _fix_items(obj);
1497    _sizing_eval(obj);
1498 }
1499
1500 /**
1501  * Starts the list.  Call before running show() on the list object.
1502  *
1503  * @param obj The list object
1504  *
1505  * @ingroup List
1506  */
1507 EAPI void
1508 elm_list_go(Evas_Object *obj)
1509 {
1510    ELM_CHECK_WIDTYPE(obj, widtype);
1511    Widget_Data *wd = elm_widget_data_get(obj);
1512    if (!wd) return;
1513    _fix_items(obj);
1514 }
1515
1516 /**
1517  * Enables/disables the state of multi-select on the list object.
1518  *
1519  * @param obj The list object
1520  * @param multi If true, multi-select is enabled
1521  *
1522  * @ingroup List
1523  */
1524 EAPI void
1525 elm_list_multi_select_set(Evas_Object *obj, Eina_Bool multi)
1526 {
1527    ELM_CHECK_WIDTYPE(obj, widtype);
1528    Widget_Data *wd = elm_widget_data_get(obj);
1529    if (!wd) return;
1530    wd->multi = multi;
1531 }
1532
1533 /**
1534  * Gets the state of multi-select on the list object.
1535  *
1536  * @param obj The list object
1537  * @return If true, multi-select is enabled
1538  *
1539  * @ingroup List
1540  */
1541 EAPI Eina_Bool
1542 elm_list_multi_select_get(const Evas_Object *obj)
1543 {
1544    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
1545    Widget_Data *wd = elm_widget_data_get(obj);
1546    if (!wd) return EINA_FALSE;
1547    return wd->multi;
1548 }
1549
1550 /**
1551  * Set which mode to use for the list with.
1552  *
1553  * @param obj The list object
1554  * @param mode One of @c ELM_LIST_COMPRESS, @c ELM_LIST_SCROLL, @c
1555  *             ELM_LIST_LIMIT or @c ELM_LIST_EXPAND.
1556  *
1557  * @note Default value is @c ELM_LIST_SCROLL. At this mode, the list
1558  * object won't set any of its size hints to inform how a possible
1559  * container should resize it. Then, if it's not created as a "resize
1560  * object", it might end with zero dimensions. The list will respect
1561  * the container's geometry and, if any of its items won't fit into
1562  * its transverse axis, one will be able to scroll it in that
1563  * direction. @c ELM_LIST_COMPRESS is the same as the previous, except
1564  * that it <b>won't</b> let one scroll in the transverse axis, on
1565  * those cases (large items will get cropped). @c ELM_LIST_LIMIT will
1566  * actually set a minimun size hint on the list object, so that
1567  * containers may respect it (and resize itself to fit the child
1568  * properly). More specifically, a minimum size hint will be set for
1569  * its transverse axis, so that the <b>largest</b> item in that
1570  * direction fits well. @c ELM_LIST_EXPAND, besides setting a minimum
1571  * size on the transverse axis, just like the previous mode, will set
1572  * a minimum size on the longitudinal axis too, trying to reserve
1573  * space to all its children to be visible at a time. The last two
1574  * modes can always have effects bounded by setting the list object's
1575  * maximum size hints, though.
1576  *
1577  * @ingroup List
1578  */
1579 EAPI void
1580 elm_list_mode_set(Evas_Object *obj, Elm_List_Mode mode)
1581 {
1582    ELM_CHECK_WIDTYPE(obj, widtype);
1583
1584    Widget_Data *wd;
1585
1586    wd = elm_widget_data_get(obj);
1587    if (!wd)
1588      return;
1589    if (wd->mode == mode)
1590      return;
1591    wd->mode = mode;
1592
1593    _elm_list_mode_set_internal(wd);
1594 }
1595
1596 /**
1597  * Get the mode the list is at.
1598  *
1599  * @param obj The list object
1600  * @return mode One of @c ELM_LIST_COMPRESS, @c ELM_LIST_SCROLL or @c
1601  *         ELM_LIST_LIMIT (@c ELM_LIST_LAST on errors).
1602  *
1603  * @note see elm_list_mode_set() for more information.
1604  *
1605  * @ingroup List
1606  */
1607 EAPI Elm_List_Mode
1608 elm_list_mode_get(const Evas_Object *obj)
1609 {
1610    ELM_CHECK_WIDTYPE(obj, widtype) ELM_LIST_LAST;
1611    Widget_Data *wd = elm_widget_data_get(obj);
1612    if (!wd) return ELM_LIST_LAST;
1613    return wd->mode;
1614 }
1615
1616 /**
1617  * Enables/disables horizontal mode of the list.
1618  *
1619  * @param obj The list object
1620  * @param mode If true, horizontale mode is enabled
1621  *
1622  * @note Bounce options for the list will be reset to default values
1623  * with this funcion. Re-call elm_list_bounce_set() once more after
1624  * this one, if you had custom values.
1625  *
1626  * @ingroup List
1627  */
1628 EAPI void
1629 elm_list_horizontal_set(Evas_Object *obj, Eina_Bool horizontal)
1630 {
1631    ELM_CHECK_WIDTYPE(obj, widtype);
1632
1633    Widget_Data *wd;
1634    Eina_Bool bounce = _elm_config->thumbscroll_bounce_enable;
1635
1636    wd = elm_widget_data_get(obj);
1637    if (!wd)
1638      return;
1639
1640    if (wd->h_mode == horizontal)
1641      return;
1642
1643    wd->h_mode = horizontal;
1644    elm_box_horizontal_set(wd->box, horizontal);
1645
1646    if (horizontal)
1647      {
1648         evas_object_size_hint_weight_set(wd->box, 0.0, EVAS_HINT_EXPAND);
1649         evas_object_size_hint_align_set(wd->box, 0.0, EVAS_HINT_FILL);
1650         elm_smart_scroller_bounce_allow_set(wd->scr, bounce, EINA_FALSE);
1651      }
1652    else
1653      {
1654         evas_object_size_hint_weight_set(wd->box, EVAS_HINT_EXPAND, 0.0);
1655         evas_object_size_hint_align_set(wd->box, EVAS_HINT_FILL, 0.0);
1656         elm_smart_scroller_bounce_allow_set(wd->scr, EINA_FALSE, bounce);
1657      }
1658
1659    _elm_list_mode_set_internal(wd);
1660 }
1661
1662 /**
1663  * Retrieve whether horizontal mode is enabled for a list.
1664  *
1665  * @param obj The list object
1666  * @return @c EINA_TRUE, if horizontal mode is enabled and @c
1667  *            EINA_FALSE, otherwise.
1668  *
1669  * @note see elm_list_horizontal_set() for more information.
1670  *
1671  * @ingroup List
1672  */
1673 EAPI Eina_Bool
1674 elm_list_horizontal_get(const Evas_Object *obj)
1675 {
1676    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
1677
1678    Widget_Data *wd;
1679
1680    wd = elm_widget_data_get(obj);
1681    if (!wd)
1682      return EINA_FALSE;
1683
1684    return wd->h_mode;
1685 }
1686
1687 /**
1688  * Enables/disables the state of always_select, meaning that
1689  * an item will always be selected.
1690  *
1691  * @param obj The list object
1692  * @param always_select If true, always_select is enabled
1693  *
1694  * @ingroup List
1695  */
1696 EAPI void
1697 elm_list_always_select_mode_set(Evas_Object *obj, Eina_Bool always_select)
1698 {
1699    ELM_CHECK_WIDTYPE(obj, widtype);
1700    Widget_Data *wd = elm_widget_data_get(obj);
1701    if (!wd) return;
1702    wd->always_select = always_select;
1703 }
1704
1705 /**
1706  * Gets the state of always_select.
1707  * See also elm_list_always_select_mode_set()
1708  *
1709  * @param obj The list object
1710  * @return If true, always_select is enabled
1711  *
1712  * @ingroup List
1713  */
1714 EAPI Eina_Bool
1715 elm_list_always_select_mode_get(const Evas_Object *obj)
1716 {
1717    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
1718    Widget_Data *wd = elm_widget_data_get(obj);
1719    if (!wd) return EINA_FALSE;
1720    return wd->always_select;
1721 }
1722
1723 /**
1724  * Returns a list of all the list items.
1725  *
1726  * @param obj The list object
1727  * @return An Eina_List* of the list items, or NULL on failure
1728  *
1729  * @ingroup List
1730  */
1731 EAPI const Eina_List *
1732 elm_list_items_get(const Evas_Object *obj)
1733 {
1734    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
1735    Widget_Data *wd = elm_widget_data_get(obj);
1736    if (!wd) return NULL;
1737    return wd->items;
1738 }
1739
1740 /**
1741  * Returns the currently selected list item.
1742  *
1743  * @param obj The list object
1744  * @return The selected list item, or NULL on failure
1745  *
1746  * @ingroup List
1747  */
1748 EAPI Elm_List_Item *
1749 elm_list_selected_item_get(const Evas_Object *obj)
1750 {
1751    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
1752    Widget_Data *wd = elm_widget_data_get(obj);
1753    if (!wd) return NULL;
1754    if (wd->selected) return wd->selected->data;
1755    return NULL;
1756 }
1757
1758 /**
1759  * Returns a list of the currently selected list items.
1760  *
1761  * @param obj The list object
1762  * @return An Eina_List* of the selected list items, or NULL on failure
1763  *
1764  * @ingroup List
1765  */
1766 EAPI const Eina_List *
1767 elm_list_selected_items_get(const Evas_Object *obj)
1768 {
1769    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
1770    Widget_Data *wd = elm_widget_data_get(obj);
1771    if (!wd) return NULL;
1772    return wd->selected;
1773 }
1774
1775 /**
1776  * Sets if item is a separator.
1777  *
1778  * @param it The list item object
1779  * @param setting
1780  */
1781 EAPI void
1782 elm_list_item_separator_set(Elm_List_Item *it, Eina_Bool setting)
1783 {
1784    ELM_LIST_ITEM_CHECK_DELETED_RETURN(it);
1785    it->is_separator = !!setting;
1786 }
1787
1788 /**
1789  * Returns EINA_TRUE if Elm_List_Item is a separator.
1790  *
1791  * @param it The list item object
1792  */
1793 EAPI Eina_Bool
1794 elm_list_item_separator_get(const Elm_List_Item *it)
1795 {
1796    ELM_LIST_ITEM_CHECK_DELETED_RETURN(it, EINA_FALSE);
1797    return it->is_separator;
1798 }
1799
1800
1801 /**
1802  * Sets the selected state of @p it.
1803  *
1804  * @param it The list item
1805  * @param selected Enables/disables the selected state
1806  *
1807  * @ingroup List
1808  */
1809 EAPI void
1810 elm_list_item_selected_set(Elm_List_Item *it, Eina_Bool selected)
1811 {
1812    ELM_LIST_ITEM_CHECK_DELETED_RETURN(it);
1813
1814    Widget_Data *wd = elm_widget_data_get(it->base.widget);
1815    if (!wd) return;
1816
1817    selected = !!selected;
1818    if (it->selected == selected) return;
1819
1820    _elm_list_walk(wd);
1821
1822    if (selected)
1823      {
1824         if (!wd->multi)
1825           {
1826              while (wd->selected)
1827                 _item_unselect(wd->selected->data);
1828           }
1829         _item_hilight(it);
1830         _item_select(it);
1831      }
1832    else
1833       _item_unselect(it);
1834
1835    _elm_list_unwalk(wd);
1836 }
1837
1838 /**
1839  * Gets the selected state of @p it.
1840  *
1841  * @param it The list item
1842  * @return If true, the item is selected
1843  *
1844  * @ingroup List
1845  */
1846 EAPI Eina_Bool
1847 elm_list_item_selected_get(const Elm_List_Item *it)
1848 {
1849    ELM_LIST_ITEM_CHECK_DELETED_RETURN(it, EINA_FALSE);
1850    return it->selected;
1851 }
1852
1853 /**
1854  * Brings @p it to the center of the list view.
1855  *
1856  * @param it The list item
1857  *
1858  * @ingroup List
1859  */
1860 EAPI void
1861 elm_list_item_show(Elm_List_Item *it)
1862 {
1863    ELM_LIST_ITEM_CHECK_DELETED_RETURN(it);
1864    Widget_Data *wd = elm_widget_data_get(it->base.widget);
1865    Evas_Coord bx, by, bw, bh;
1866    Evas_Coord x, y, w, h;
1867
1868    evas_object_geometry_get(wd->box, &bx, &by, &bw, &bh);
1869    evas_object_geometry_get(it->base.view, &x, &y, &w, &h);
1870    x -= bx;
1871    y -= by;
1872    if (wd->scr)
1873      elm_smart_scroller_child_region_show(wd->scr, x, y, w, h);
1874 }
1875
1876 /**
1877  * Bring in the given item
1878  *
1879  * This causes list to jump to the given item @p it and show it (by scrolling),
1880  * if it is not fully visible. This may use animation to do so and take a
1881  * period of time
1882  *
1883  * @param it The item
1884  *
1885  * @ingroup List
1886  */
1887 EAPI void
1888 elm_list_item_bring_in(Elm_List_Item *it)
1889 {
1890    ELM_LIST_ITEM_CHECK_DELETED_RETURN(it);
1891    Widget_Data *wd = elm_widget_data_get(it->base.widget);
1892    Evas_Coord bx, by, bw, bh;
1893    Evas_Coord x, y, w, h;
1894
1895    evas_object_geometry_get(wd->box, &bx, &by, &bw, &bh);
1896    evas_object_geometry_get(it->base.view, &x, &y, &w, &h);
1897    x -= bx;
1898    y -= by;
1899    if (wd->scr)
1900      elm_smart_scroller_region_bring_in(wd->scr, x, y, w, h);
1901 }
1902
1903 /**
1904  * Deletes item @p it from the list.
1905  *
1906  * @param it The list item to delete
1907  *
1908  * @ingroup List
1909  */
1910 EAPI void
1911 elm_list_item_del(Elm_List_Item *it)
1912 {
1913    ELM_LIST_ITEM_CHECK_DELETED_RETURN(it);
1914    Widget_Data *wd = elm_widget_data_get(it->base.widget);
1915    if (!wd) return;
1916
1917    if (it->selected) _item_unselect(it);
1918
1919    if (wd->walking > 0)
1920      {
1921         if (it->deleted) return;
1922         it->deleted = EINA_TRUE;
1923         wd->to_delete = eina_list_append(wd->to_delete, it);
1924         return;
1925      }
1926
1927    wd->items = eina_list_remove_list(wd->items, it->node);
1928
1929    _elm_list_walk(wd);
1930
1931    elm_widget_item_pre_notify_del(it);
1932    _elm_list_item_free(it);
1933
1934    _elm_list_unwalk(wd);
1935 }
1936
1937 /**
1938  * Set the function called when a list item is freed.
1939  *
1940  * @param it The item to set the callback on
1941  * @param func The function called
1942  *
1943  * @ingroup List
1944  */
1945 EAPI void
1946 elm_list_item_del_cb_set(Elm_List_Item *it, Evas_Smart_Cb func)
1947 {
1948    ELM_LIST_ITEM_CHECK_DELETED_RETURN(it);
1949    elm_widget_item_del_cb_set(it, func);
1950 }
1951
1952 /**
1953  * Returns the data associated with the item.
1954  *
1955  * @param it The list item
1956  * @return The data associated with @p it
1957  *
1958  * @ingroup List
1959  */
1960 EAPI void *
1961 elm_list_item_data_get(const Elm_List_Item *it)
1962 {
1963    ELM_LIST_ITEM_CHECK_DELETED_RETURN(it, NULL);
1964    return elm_widget_item_data_get(it);
1965 }
1966
1967 /**
1968  * Returns the left side icon associated with the item.
1969  *
1970  * @param it The list item
1971  * @return The left side icon associated with @p it
1972  *
1973  * @ingroup List
1974  */
1975 EAPI Evas_Object *
1976 elm_list_item_icon_get(const Elm_List_Item *it)
1977 {
1978    ELM_LIST_ITEM_CHECK_DELETED_RETURN(it, NULL);
1979    if (it->dummy_icon) return NULL;
1980    return it->icon;
1981 }
1982
1983 /**
1984  * Sets the left side icon associated with the item.
1985  *
1986  * Once the icon object is set, a previously set one will be deleted.
1987  * You probably don't want, then, to have the <b>same</b> icon object set
1988  * for more than one item of the list.
1989  *
1990  * @param it The list item
1991  * @param icon The left side icon object to associate with @p it
1992  *
1993  * @ingroup List
1994  */
1995 EAPI void
1996 elm_list_item_icon_set(Elm_List_Item *it, Evas_Object *icon)
1997 {
1998    ELM_LIST_ITEM_CHECK_DELETED_RETURN(it);
1999    if (it->icon == icon) return;
2000    if ((it->dummy_icon) && (!icon)) return;
2001    if (it->dummy_icon)
2002      {
2003         evas_object_del(it->icon);
2004         it->dummy_icon = EINA_FALSE;
2005      }
2006    if (!icon)
2007      {
2008         icon = evas_object_rectangle_add(evas_object_evas_get(it->base.widget));
2009         evas_object_color_set(icon, 0, 0, 0, 0);
2010         it->dummy_icon = EINA_TRUE;
2011      }
2012    if (it->icon)
2013      {
2014         evas_object_del(it->icon);
2015         it->icon = NULL;
2016      }
2017    it->icon = icon;
2018    if (it->base.view)
2019      edje_object_part_swallow(it->base.view, "elm.swallow.icon", icon);
2020 }
2021
2022 /**
2023  * Gets the right side icon associated with the item.
2024  *
2025  * @param it The list item
2026  * @return The right side icon object associated with @p it
2027  *
2028  * @ingroup List
2029  */
2030 EAPI Evas_Object *
2031 elm_list_item_end_get(const Elm_List_Item *it)
2032 {
2033    ELM_LIST_ITEM_CHECK_DELETED_RETURN(it, NULL);
2034    if (it->dummy_end) return NULL;
2035    return it->end;
2036 }
2037
2038 /**
2039  * Sets the right side icon associated with the item.
2040  *
2041  * Once the icon object is set, a previously set one will be deleted.
2042  * You probably don't want, then, to have the <b>same</b> icon object set
2043  * for more than one item of the list.
2044  *
2045  * @param it The list item
2046  * @param icon The right side icon object to associate with @p it
2047  *
2048  * @ingroup List
2049  */
2050 EAPI void
2051 elm_list_item_end_set(Elm_List_Item *it, Evas_Object *end)
2052 {
2053    ELM_LIST_ITEM_CHECK_DELETED_RETURN(it);
2054    if (it->end == end) return;
2055    if ((it->dummy_end) && (!end)) return;
2056    if (it->dummy_end)
2057      {
2058         evas_object_del(it->end);
2059         it->dummy_icon = EINA_FALSE;
2060      }
2061    if (!end)
2062      {
2063         end = evas_object_rectangle_add(evas_object_evas_get(it->base.widget));
2064         evas_object_color_set(end, 0, 0, 0, 0);
2065         it->dummy_end = EINA_TRUE;
2066      }
2067    if (it->end)
2068      {
2069         evas_object_del(it->end);
2070         it->end = NULL;
2071      }
2072    it->end = end;
2073    if (it->base.view)
2074      edje_object_part_swallow(it->base.view, "elm.swallow.end", end);
2075 }
2076
2077 /**
2078  * Gets the base object of the item.
2079  *
2080  * @param it The list item
2081  * @return The base object associated with @p it
2082  *
2083  * @ingroup List
2084  */
2085 EAPI Evas_Object *
2086 elm_list_item_base_get(const Elm_List_Item *it)
2087 {
2088    ELM_LIST_ITEM_CHECK_DELETED_RETURN(it, NULL);
2089    return it->base.view;
2090 }
2091
2092 /**
2093  * Gets the label of the item.
2094  *
2095  * @param it The list item
2096  * @return The label of @p it
2097  *
2098  * @ingroup List
2099  */
2100 EAPI const char *
2101 elm_list_item_label_get(const Elm_List_Item *it)
2102 {
2103    ELM_LIST_ITEM_CHECK_DELETED_RETURN(it, NULL);
2104    return it->label;
2105 }
2106
2107 /**
2108  * Sets the label of the item.
2109  *
2110  * @param it The list item
2111  * @param text The label of @p it
2112  *
2113  * @ingroup List
2114  */
2115 EAPI void
2116 elm_list_item_label_set(Elm_List_Item *it, const char *text)
2117 {
2118    ELM_LIST_ITEM_CHECK_DELETED_RETURN(it);
2119    if (!eina_stringshare_replace(&it->label, text)) return;
2120    if (it->base.view)
2121      edje_object_part_text_set(it->base.view, "elm.text", it->label);
2122 }
2123
2124 /**
2125  * Gets the item before @p it in the list.
2126  *
2127  * @param it The list item
2128  * @return The item before @p it, or NULL on failure
2129  *
2130  * @ingroup List
2131  */
2132 EAPI Elm_List_Item *
2133 elm_list_item_prev(const Elm_List_Item *it)
2134 {
2135    ELM_LIST_ITEM_CHECK_DELETED_RETURN(it, NULL);
2136    if (it->node->prev) return it->node->prev->data;
2137    else return NULL;
2138 }
2139
2140 /**
2141  * Gets the item after @p it in the list.
2142  *
2143  * @param it The list item
2144  * @return The item after @p it, or NULL on failure
2145  *
2146  * @ingroup List
2147  */
2148 EAPI Elm_List_Item *
2149 elm_list_item_next(const Elm_List_Item *it)
2150 {
2151    ELM_LIST_ITEM_CHECK_DELETED_RETURN(it, NULL);
2152    if (it->node->next) return it->node->next->data;
2153    else return NULL;
2154 }
2155
2156 /**
2157  * Set the text to be shown in the list item.
2158  *
2159  * @param item Target item
2160  * @param text The text to set in the content
2161  *
2162  * Setup the text as tooltip to object. The item can have only one tooltip,
2163  * so any previous tooltip data is removed.
2164  *
2165  * @ingroup List
2166  */
2167 EAPI void
2168 elm_list_item_tooltip_text_set(Elm_List_Item *item, const char *text)
2169 {
2170    ELM_LIST_ITEM_CHECK_DELETED_RETURN(item);
2171    elm_widget_item_tooltip_text_set(item, text);
2172 }
2173
2174 /**
2175  * Set the content to be shown in the tooltip item
2176  *
2177  * Setup the tooltip to item. The item can have only one tooltip,
2178  * so any previous tooltip data is removed. @p func(with @p data) will
2179  * be called every time that need show the tooltip and it should
2180  * return a valid Evas_Object. This object is then managed fully by
2181  * tooltip system and is deleted when the tooltip is gone.
2182  *
2183  * @param item the list item being attached a tooltip.
2184  * @param func the function used to create the tooltip contents.
2185  * @param data what to provide to @a func as callback data/context.
2186  * @param del_cb called when data is not needed anymore, either when
2187  *        another callback replaces @func, the tooltip is unset with
2188  *        elm_list_item_tooltip_unset() or the owner @a item
2189  *        dies. This callback receives as the first parameter the
2190  *        given @a data, and @c event_info is the item.
2191  *
2192  * @ingroup List
2193  */
2194 EAPI void
2195 elm_list_item_tooltip_content_cb_set(Elm_List_Item *item, Elm_Tooltip_Item_Content_Cb func, const void *data, Evas_Smart_Cb del_cb)
2196 {
2197    ELM_LIST_ITEM_CHECK_DELETED_RETURN(item);
2198    elm_widget_item_tooltip_content_cb_set(item, func, data, del_cb);
2199 }
2200
2201 /**
2202  * Unset tooltip from item
2203  *
2204  * @param item list item to remove previously set tooltip.
2205  *
2206  * Remove tooltip from item. The callback provided as del_cb to
2207  * elm_list_item_tooltip_content_cb_set() will be called to notify
2208  * it is not used anymore.
2209  *
2210  * @see elm_list_item_tooltip_content_cb_set()
2211  *
2212  * @ingroup List
2213  */
2214 EAPI void
2215 elm_list_item_tooltip_unset(Elm_List_Item *item)
2216 {
2217    ELM_LIST_ITEM_CHECK_DELETED_RETURN(item);
2218    elm_widget_item_tooltip_unset(item);
2219 }
2220
2221 /**
2222  * Sets a different style for this item tooltip.
2223  *
2224  * @note before you set a style you should define a tooltip with
2225  *       elm_list_item_tooltip_content_cb_set() or
2226  *       elm_list_item_tooltip_text_set()
2227  *
2228  * @param item list item with tooltip already set.
2229  * @param style the theme style to use (default, transparent, ...)
2230  *
2231  * @ingroup List
2232  */
2233 EAPI void
2234 elm_list_item_tooltip_style_set(Elm_List_Item *item, const char *style)
2235 {
2236    ELM_LIST_ITEM_CHECK_DELETED_RETURN(item);
2237    elm_widget_item_tooltip_style_set(item, style);
2238 }
2239
2240 /**
2241  * Get the style for this item tooltip.
2242  *
2243  * @param item list item with tooltip already set.
2244  * @return style the theme style in use, defaults to "default". If the
2245  *         object does not have a tooltip set, then NULL is returned.
2246  *
2247  * @ingroup List
2248  */
2249 EAPI const char *
2250 elm_list_item_tooltip_style_get(const Elm_List_Item *item)
2251 {
2252    ELM_LIST_ITEM_CHECK_DELETED_RETURN(item, NULL);
2253    return elm_widget_item_tooltip_style_get(item);
2254 }
2255
2256 /**
2257  * Set the cursor to be shown when mouse is over the list item
2258  *
2259  * @param item Target item
2260  * @param cursor the cursor name to be used.
2261  *
2262  * @see elm_object_cursor_set()
2263  * @ingroup List
2264  */
2265 EAPI void
2266 elm_list_item_cursor_set(Elm_List_Item *item, const char *cursor)
2267 {
2268    ELM_LIST_ITEM_CHECK_DELETED_RETURN(item);
2269    elm_widget_item_cursor_set(item, cursor);
2270 }
2271
2272 /**
2273  * Get the cursor to be shown when mouse is over the list item
2274  *
2275  * @param item list item with cursor already set.
2276  * @return the cursor name.
2277  *
2278  * @ingroup List
2279  */
2280 EAPI const char *
2281 elm_list_item_cursor_get(const Elm_List_Item *item)
2282 {
2283    ELM_LIST_ITEM_CHECK_DELETED_RETURN(item, NULL);
2284    return elm_widget_item_cursor_get(item);
2285 }
2286
2287 /**
2288  * Unset the cursor to be shown when mouse is over the list item
2289  *
2290  * @param item Target item
2291  *
2292  * @see elm_object_cursor_unset()
2293  * @ingroup List
2294  */
2295 EAPI void
2296 elm_list_item_cursor_unset(Elm_List_Item *item)
2297 {
2298    ELM_LIST_ITEM_CHECK_DELETED_RETURN(item);
2299    elm_widget_item_cursor_unset(item);
2300 }
2301
2302 /**
2303  * Sets a different style for this item cursor.
2304  *
2305  * @note before you set a style you should define a cursor with
2306  *       elm_list_item_cursor_set()
2307  *
2308  * @param item list item with cursor already set.
2309  * @param style the theme style to use (default, transparent, ...)
2310  *
2311  * @ingroup List
2312  */
2313 EAPI void
2314 elm_list_item_cursor_style_set(Elm_List_Item *item, const char *style)
2315 {
2316    ELM_LIST_ITEM_CHECK_DELETED_RETURN(item);
2317    elm_widget_item_cursor_style_set(item, style);
2318 }
2319
2320 /**
2321  * Get the style for this item cursor.
2322  *
2323  * @param item list item with cursor already set.
2324  * @return style the theme style in use, defaults to "default". If the
2325  *         object does not have a cursor set, then NULL is returned.
2326  *
2327  * @ingroup List
2328  */
2329 EAPI const char *
2330 elm_list_item_cursor_style_get(const Elm_List_Item *item)
2331 {
2332    ELM_LIST_ITEM_CHECK_DELETED_RETURN(item, NULL);
2333    return elm_widget_item_cursor_style_get(item);
2334 }
2335
2336 /**
2337  * Set if the cursor set should be searched on the theme or should use
2338  * the provided by the engine, only.
2339  *
2340  * @note before you set if should look on theme you should define a cursor
2341  * with elm_object_cursor_set(). By default it will only look for cursors
2342  * provided by the engine.
2343  *
2344  * @param item widget item with cursor already set.
2345  * @param engine_only boolean to define it cursors should be looked only
2346  * between the provided by the engine or searched on widget's theme as well.
2347  *
2348  * @ingroup List
2349  */
2350 EAPI void
2351 elm_list_item_cursor_engine_only_set(Elm_List_Item *item, Eina_Bool engine_only)
2352 {
2353    ELM_LIST_ITEM_CHECK_DELETED_RETURN(item);
2354    elm_widget_item_cursor_engine_only_set(item, engine_only);
2355 }
2356
2357 /**
2358  * Get the cursor engine only usage for this item cursor.
2359  *
2360  * @param item widget item with cursor already set.
2361  * @return engine_only boolean to define it cursors should be looked only
2362  * between the provided by the engine or searched on widget's theme as well. If
2363  *         the object does not have a cursor set, then EINA_FALSE is returned.
2364  *
2365  * @ingroup List
2366  */
2367 EAPI Eina_Bool
2368 elm_list_item_cursor_engine_only_get(const Elm_List_Item *item)
2369 {
2370    ELM_LIST_ITEM_CHECK_DELETED_RETURN(item, EINA_FALSE);
2371    return elm_widget_item_cursor_engine_only_get(item);
2372 }
2373
2374 /**
2375  * Set bounce mode
2376  *
2377  * This will enable or disable the scroller bounce mode for the list. See
2378  * elm_scroller_bounce_set() for details
2379  *
2380  * @param obj The list object
2381  * @param h_bounce Allow bounce horizontally
2382  * @param v_bounce Allow bounce vertically
2383  *
2384  * @ingroup List
2385  */
2386 EAPI void
2387 elm_list_bounce_set(Evas_Object *obj, Eina_Bool h_bounce, Eina_Bool v_bounce)
2388 {
2389    ELM_CHECK_WIDTYPE(obj, widtype);
2390    Widget_Data *wd = elm_widget_data_get(obj);
2391    if (!wd) return;
2392    if (wd->scr)
2393      elm_smart_scroller_bounce_allow_set(wd->scr, h_bounce, v_bounce);
2394 }
2395
2396 /**
2397  * Get the bounce mode
2398  *
2399  * @param obj The List object
2400  * @param h_bounce Allow bounce horizontally
2401  * @param v_bounce Allow bounce vertically
2402  *
2403  * @ingroup List
2404  */
2405 EAPI void
2406 elm_list_bounce_get(const Evas_Object *obj, Eina_Bool *h_bounce, Eina_Bool *v_bounce)
2407 {
2408    ELM_CHECK_WIDTYPE(obj, widtype);
2409    Widget_Data *wd = elm_widget_data_get(obj);
2410    if (!wd) return;
2411    elm_smart_scroller_bounce_allow_get(wd->scr, h_bounce, v_bounce);
2412 }
2413
2414 /**
2415  * Set the scrollbar policy
2416  *
2417  * This sets the scrollbar visibility policy for the given list scroller.
2418  * ELM_SMART_SCROLLER_POLICY_AUTO means the scrollber is made visible if it
2419  * is needed, and otherwise kept hidden. ELM_SMART_SCROLLER_POLICY_ON turns
2420  * it on all the time, and ELM_SMART_SCROLLER_POLICY_OFF always keeps it off.
2421  * This applies respectively for the horizontal and vertical scrollbars.
2422  *
2423  * @param obj The list object
2424  * @param policy_h Horizontal scrollbar policy
2425  * @param policy_v Vertical scrollbar policy
2426  *
2427  * @ingroup List
2428  */
2429 EAPI void
2430 elm_list_scroller_policy_set(Evas_Object *obj, Elm_Scroller_Policy policy_h, Elm_Scroller_Policy policy_v)
2431 {
2432    ELM_CHECK_WIDTYPE(obj, widtype);
2433    Widget_Data *wd = elm_widget_data_get(obj);
2434    if (!wd) return;
2435    if ((policy_h >= ELM_SCROLLER_POLICY_LAST) ||
2436        (policy_v >= ELM_SCROLLER_POLICY_LAST))
2437    if (wd->scr)
2438      elm_smart_scroller_policy_set(wd->scr, policy_h, policy_v);
2439 }
2440
2441 EAPI void
2442 elm_list_scroller_policy_get(const Evas_Object *obj, Elm_Scroller_Policy *policy_h, Elm_Scroller_Policy *policy_v)
2443 {
2444    ELM_CHECK_WIDTYPE(obj, widtype);
2445    Widget_Data *wd = elm_widget_data_get(obj);
2446    Elm_Smart_Scroller_Policy s_policy_h, s_policy_v;
2447    if ((!wd) || (!wd->scr)) return;
2448    elm_smart_scroller_policy_get(wd->scr, &s_policy_h, &s_policy_v);
2449    if (policy_h) *policy_h = (Elm_Scroller_Policy) s_policy_h;
2450    if (policy_v) *policy_v = (Elm_Scroller_Policy) s_policy_v;
2451 }
2452
2453 /**
2454  * Sets the disabled/enabled state of a list item.
2455  *
2456  * A disabled item cannot be selected or unselected. It will also
2457  * change its appearance (generally greyed out). This sets the
2458  * disabled state (@c EINA_TRUE for disabled, @c EINA_FALSE for
2459  * enabled).
2460  *
2461  * @param it The item
2462  * @param disabled The disabled state
2463  *
2464  * @ingroup List
2465  */
2466 EAPI void
2467 elm_list_item_disabled_set(Elm_List_Item *it, Eina_Bool disabled)
2468 {
2469    ELM_LIST_ITEM_CHECK_DELETED_RETURN(it);
2470
2471    if (it->disabled == disabled)
2472      return;
2473
2474    it->disabled = !!disabled;
2475
2476    if (it->disabled)
2477      edje_object_signal_emit(it->base.view, "elm,state,disabled", "elm");
2478    else
2479      edje_object_signal_emit(it->base.view, "elm,state,enabled", "elm");
2480 }
2481
2482 /**
2483  * Get the disabled/enabled state of a list item
2484  *
2485  * @param it The item
2486  * @return The disabled state
2487  *
2488  * See elm_list_item_disabled_set().
2489  *
2490  * @ingroup List
2491  */
2492 EAPI Eina_Bool
2493 elm_list_item_disabled_get(const Elm_List_Item *it)
2494 {
2495    ELM_LIST_ITEM_CHECK_DELETED_RETURN(it, EINA_FALSE);
2496
2497    return it->disabled;
2498 }
2499