[Gengrid/List/Genlist] "clicked" callback name change to "clicked,double" on upstream.
[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      {
852         evas_object_smart_callback_call(it->base.widget, "clicked,double", it);
853         evas_object_smart_callback_call(it->base.widget, "clicked", it); // will be removed
854      }
855    wd->swipe = EINA_FALSE;
856    wd->movements = 0;
857 }
858
859 static void
860 _mouse_up(void *data, Evas *evas __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info)
861 {
862    Elm_List_Item *it = data;
863    Widget_Data *wd = elm_widget_data_get(it->base.widget);
864    Evas_Event_Mouse_Up *ev = event_info;
865
866    if (!wd) return;
867    ELM_LIST_ITEM_CHECK_DELETED_RETURN(it);
868    if (ev->button != 1) return;
869    if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) wd->on_hold = EINA_TRUE;
870    else wd->on_hold = EINA_FALSE;
871    wd->longpressed = EINA_FALSE;
872    if (it->long_timer)
873      {
874         ecore_timer_del(it->long_timer);
875         it->long_timer = NULL;
876      }
877    if (it->swipe_timer)
878      {
879         ecore_timer_del(it->swipe_timer);
880         it->swipe_timer = NULL;
881      }
882    if (wd->on_hold)
883      {
884         if (wd->swipe) _swipe(data);
885         wd->on_hold = EINA_FALSE;
886         return;
887      }
888    if (wd->longpressed)
889      {
890         if (!wd->wasselected) _item_unselect(it);
891         wd->wasselected = 0;
892         return;
893      }
894
895    if (it->disabled)
896      return;
897    if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return;
898   
899    _elm_list_walk(wd); // watch out "return" before unwalk!
900
901    if (wd->multi)
902      {
903         if (!it->selected)
904           {
905              _item_hilight(it);
906              _item_select(it);
907           }
908         else _item_unselect(it);
909      }
910    else
911      {
912         if (!it->selected)
913           {
914              while (wd->selected)
915                 _item_unselect(wd->selected->data);
916              _item_hilight(it);
917              _item_select(it);
918           }
919         else
920           {
921              const Eina_List *l, *l_next;
922              Elm_List_Item *it2;
923
924              EINA_LIST_FOREACH_SAFE(wd->selected, l, l_next, it2)
925                 if (it2 != it) _item_unselect(it2);
926              _item_hilight(it);
927              _item_select(it);
928           }
929      }
930
931    _elm_list_unwalk(wd);
932 }
933
934 static Elm_List_Item *
935 _item_new(Evas_Object *obj, const char *label, Evas_Object *icon, Evas_Object *end, Evas_Smart_Cb func, const void *data)
936 {
937    Widget_Data *wd = elm_widget_data_get(obj);
938    Elm_List_Item *it;
939
940    if (!wd) return NULL;
941    it = elm_widget_item_new(obj, Elm_List_Item);
942    it->wd = wd;
943    it->label = eina_stringshare_add(label);
944    it->icon = icon;
945    it->end = end;
946    it->func = func;
947    it->base.data = data;
948    it->base.view = edje_object_add(evas_object_evas_get(obj));
949    evas_object_event_callback_add(it->base.view, EVAS_CALLBACK_MOUSE_DOWN,
950                                   _mouse_down, it);
951    evas_object_event_callback_add(it->base.view, EVAS_CALLBACK_MOUSE_UP,
952                                   _mouse_up, it);
953    evas_object_event_callback_add(it->base.view, EVAS_CALLBACK_MOUSE_MOVE,
954                                   _mouse_move, it);
955    evas_object_size_hint_weight_set(it->base.view, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
956    evas_object_size_hint_align_set(it->base.view, EVAS_HINT_FILL, EVAS_HINT_FILL);
957    if (it->icon)
958      {
959         elm_widget_sub_object_add(obj, it->icon);
960         evas_object_event_callback_add(it->icon, EVAS_CALLBACK_CHANGED_SIZE_HINTS,
961                                        _changed_size_hints, obj);
962      }
963    if (it->end)
964      {
965         elm_widget_sub_object_add(obj, it->end);
966         evas_object_event_callback_add(it->end, EVAS_CALLBACK_CHANGED_SIZE_HINTS,
967                                        _changed_size_hints, obj);
968      }
969    return it;
970 }
971
972 static void
973 _elm_list_mode_set_internal(Widget_Data *wd)
974 {
975    if (!wd->scr)
976      return;
977
978    if (wd->mode == ELM_LIST_LIMIT)
979      {
980         if (!wd->h_mode)
981           {
982              wd->scr_minw = EINA_TRUE;
983              wd->scr_minh = EINA_FALSE;
984           }
985         else
986           {
987              wd->scr_minw = EINA_FALSE;
988              wd->scr_minh = EINA_TRUE;
989           }
990      }
991    else if (wd->mode == ELM_LIST_EXPAND)
992      {
993         wd->scr_minw = EINA_TRUE;
994         wd->scr_minh = EINA_TRUE;
995      }
996    else
997      {
998         wd->scr_minw = EINA_FALSE;
999         wd->scr_minh = EINA_FALSE;
1000      }
1001
1002    _sizing_eval(wd->self);
1003 }
1004
1005 static void
1006 _fix_items(Evas_Object *obj)
1007 {
1008    Widget_Data *wd = elm_widget_data_get(obj);
1009    const Eina_List *l;
1010    Elm_List_Item *it;
1011    Evas_Coord minw[2] = { 0, 0 }, minh[2] = { 0, 0 };
1012    Evas_Coord mw, mh;
1013    int i, redo = 0;
1014    const char *style = elm_widget_style_get(obj);
1015    const char *it_plain = wd->h_mode ? "h_item" : "item";
1016    const char *it_odd = wd->h_mode ? "h_item_odd" : "item_odd";
1017    const char *it_compress = wd->h_mode ? "h_item_compress" : "item_compress";
1018    const char *it_compress_odd = wd->h_mode ? "h_item_compress_odd" : "item_compress_odd";
1019
1020    if (!wd) return;
1021    if (wd->walking)
1022      {
1023         wd->fix_pending = EINA_TRUE;
1024         return;
1025      }
1026
1027    _elm_list_walk(wd); // watch out "return" before unwalk!
1028
1029    EINA_LIST_FOREACH(wd->items, l, it)
1030      {
1031         if (it->deleted) continue;
1032         if (it->icon)
1033           {
1034              evas_object_size_hint_min_get(it->icon, &mw, &mh);
1035              if (mw > minw[0]) minw[0] = mw;
1036              if (mh > minh[0]) minh[0] = mh;
1037           }
1038         if (it->end)
1039           {
1040              evas_object_size_hint_min_get(it->end, &mw, &mh);
1041              if (mw > minw[1]) minw[1] = mw;
1042              if (mh > minh[1]) minh[1] = mh;
1043           }
1044      }
1045
1046    if ((minw[0] != wd->minw[0]) || (minw[1] != wd->minw[1]) ||
1047        (minw[0] != wd->minh[0]) || (minh[1] != wd->minh[1]))
1048      {
1049         wd->minw[0] = minw[0];
1050         wd->minw[1] = minw[1];
1051         wd->minh[0] = minh[0];
1052         wd->minh[1] = minh[1];
1053         redo = 1;
1054      }
1055    i = 0;
1056    EINA_LIST_FOREACH(wd->items, l, it)
1057      {
1058         if (it->deleted)
1059           continue;
1060
1061         it->even = i & 0x1;
1062         if ((it->even != it->is_even) || (!it->fixed) || (redo))
1063           {
1064              const char *stacking;
1065
1066              /* FIXME: separators' themes seem to be b0rked */
1067              if (it->is_separator)
1068                _elm_theme_object_set(obj, it->base.view, "separator",
1069                                      wd->h_mode ? "horizontal" : "vertical",
1070                                      style);
1071              else if (wd->mode == ELM_LIST_COMPRESS)
1072                {
1073                   if (it->even)
1074                     _elm_theme_object_set(obj, it->base.view, "list",
1075                                           it_compress, style);
1076                   else
1077                     _elm_theme_object_set(obj, it->base.view, "list",
1078                                           it_compress_odd, style);
1079                }
1080              else
1081                {
1082                   if (it->even)
1083                     _elm_theme_object_set(obj, it->base.view, "list", it_plain,
1084                                           style);
1085                   else
1086                     _elm_theme_object_set(obj, it->base.view, "list", it_odd,
1087                                           style);
1088                }
1089              stacking = edje_object_data_get(it->base.view, "stacking");
1090              if (stacking)
1091                {
1092                   if (!strcmp(stacking, "below"))
1093                     evas_object_lower(it->base.view);
1094                   else if (!strcmp(stacking, "above"))
1095                     evas_object_raise(it->base.view);
1096                }
1097              edje_object_part_text_set(it->base.view, "elm.text", it->label);
1098
1099              if ((!it->icon) && (minh[0] > 0))
1100                {
1101                   it->icon = evas_object_rectangle_add(evas_object_evas_get(it->base.view));
1102                   evas_object_color_set(it->icon, 0, 0, 0, 0);
1103                   it->dummy_icon = EINA_TRUE;
1104                }
1105              if ((!it->end) && (minh[1] > 0))
1106                {
1107                   it->end = evas_object_rectangle_add(evas_object_evas_get(it->base.view));
1108                   evas_object_color_set(it->end, 0, 0, 0, 0);
1109                   it->dummy_end = EINA_TRUE;
1110                }
1111              if (it->icon)
1112                {
1113                   evas_object_size_hint_min_set(it->icon, minw[0], minh[0]);
1114                   evas_object_size_hint_max_set(it->icon, 99999, 99999);
1115                   edje_object_part_swallow(it->base.view, "elm.swallow.icon", it->icon);
1116                }
1117              if (it->end)
1118                {
1119                   evas_object_size_hint_min_set(it->end, minw[1], minh[1]);
1120                   evas_object_size_hint_max_set(it->end, 99999, 99999);
1121                   edje_object_part_swallow(it->base.view, "elm.swallow.end", it->end);
1122                }
1123              if (!it->fixed)
1124                {
1125                   // this may call up user and it may modify the list item
1126                   // but we're safe as we're flagged as walking.
1127                   // just don't process further
1128                   edje_object_message_signal_process(it->base.view);
1129                   if (it->deleted)
1130                     continue;
1131                   mw = mh = -1;
1132                   elm_coords_finger_size_adjust(1, &mw, 1, &mh);
1133                   edje_object_size_min_restricted_calc(it->base.view, &mw, &mh, mw, mh);
1134                   elm_coords_finger_size_adjust(1, &mw, 1, &mh);
1135                   evas_object_size_hint_min_set(it->base.view, mw, mh);
1136                   evas_object_show(it->base.view);
1137                }
1138              if ((it->selected) || (it->hilighted))
1139                {
1140                   const char *selectraise;
1141
1142                   // this may call up user and it may modify the list item
1143                   // but we're safe as we're flagged as walking.
1144                   // just don't process further
1145                   edje_object_signal_emit(it->base.view, "elm,state,selected", "elm");
1146                   if (it->deleted)
1147                     continue;
1148
1149                   selectraise = edje_object_data_get(it->base.view, "selectraise");
1150                   if ((selectraise) && (!strcmp(selectraise, "on")))
1151                     evas_object_raise(it->base.view);
1152                }
1153              if (it->disabled)
1154                edje_object_signal_emit(it->base.view, "elm,state,disabled",
1155                                        "elm");
1156
1157              it->fixed = EINA_TRUE;
1158              it->is_even = it->even;
1159           }
1160         i++;
1161      }
1162
1163    _elm_list_unwalk(wd);
1164
1165    mw = 0; mh = 0;
1166    evas_object_size_hint_min_get(wd->box, &mw, &mh);
1167
1168    _elm_list_mode_set_internal(wd);
1169 }
1170
1171 static void
1172 _hold_on(void *data __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__)
1173 {
1174    Widget_Data *wd = elm_widget_data_get(obj);
1175    if (!wd) return;
1176    if (wd->scr)
1177      elm_smart_scroller_hold_set(wd->scr, EINA_TRUE);
1178 }
1179
1180 static void
1181 _hold_off(void *data __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__)
1182 {
1183    Widget_Data *wd = elm_widget_data_get(obj);
1184    if (!wd) return;
1185    if (wd->scr)
1186      elm_smart_scroller_hold_set(wd->scr, EINA_FALSE);
1187 }
1188
1189 static void
1190 _freeze_on(void *data __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__)
1191 {
1192    Widget_Data *wd = elm_widget_data_get(obj);
1193    if (!wd) return;
1194    if (wd->scr)
1195      elm_smart_scroller_freeze_set(wd->scr, EINA_TRUE);
1196 }
1197
1198 static void
1199 _freeze_off(void *data __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__)
1200 {
1201    Widget_Data *wd = elm_widget_data_get(obj);
1202    if (!wd) return;
1203    if (wd->scr)
1204      elm_smart_scroller_freeze_set(wd->scr, EINA_FALSE);
1205 }
1206
1207 static void
1208 _resize(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
1209 {
1210    _sizing_eval(data);
1211 }
1212
1213 /**
1214  * Adds a list object.
1215  *
1216  * @param parent The parent object
1217  * @return The created object or NULL upon failure
1218  *
1219  * @ingroup List
1220  */
1221 EAPI Evas_Object *
1222 elm_list_add(Evas_Object *parent)
1223 {
1224    Evas_Object *obj;
1225    Evas *e;
1226    Widget_Data *wd;
1227    Evas_Coord minw, minh;
1228
1229    EINA_SAFETY_ON_NULL_RETURN_VAL(parent, NULL);
1230
1231    wd = ELM_NEW(Widget_Data);
1232    e = evas_object_evas_get(parent);
1233    if (!e) return NULL;
1234    wd->self = obj = elm_widget_add(e);
1235    ELM_SET_WIDTYPE(widtype, "list");
1236    elm_widget_type_set(obj, "list");
1237    elm_widget_sub_object_add(parent, obj);
1238    elm_widget_on_focus_hook_set(obj, _on_focus_hook, NULL);
1239    elm_widget_data_set(obj, wd);
1240    elm_widget_del_hook_set(obj, _del_hook);
1241    elm_widget_theme_hook_set(obj, _theme_hook);
1242    elm_widget_disable_hook_set(obj, _disable_hook);
1243    elm_widget_can_focus_set(obj, EINA_TRUE);
1244    elm_widget_signal_emit_hook_set(obj, _signal_emit_hook);
1245    elm_widget_signal_callback_add_hook_set(obj, _signal_callback_add_hook);
1246    elm_widget_signal_callback_del_hook_set(obj, _signal_callback_del_hook);
1247    elm_widget_event_hook_set(obj, _event_hook);
1248
1249    wd->scr = elm_smart_scroller_add(e);
1250    elm_smart_scroller_widget_set(wd->scr, obj);
1251    elm_widget_resize_object_set(obj, wd->scr);
1252    evas_object_event_callback_add(wd->scr, EVAS_CALLBACK_CHANGED_SIZE_HINTS,
1253                                   _changed_size_hints, obj);
1254    edje_object_size_min_calc(elm_smart_scroller_edje_object_get(wd->scr), &minw, &minh);
1255    evas_object_size_hint_min_set(obj, minw, minh);
1256    evas_object_event_callback_add(obj, EVAS_CALLBACK_RESIZE, _resize, obj);
1257
1258    elm_smart_scroller_bounce_allow_set(wd->scr, EINA_FALSE,
1259                                        _elm_config->thumbscroll_bounce_enable);
1260
1261    wd->box = elm_box_add(parent);
1262    elm_box_homogenous_set(wd->box, 1);
1263    evas_object_size_hint_weight_set(wd->box, EVAS_HINT_EXPAND, 0.0);
1264    evas_object_size_hint_align_set(wd->box, EVAS_HINT_FILL, 0.0);
1265    elm_widget_on_show_region_hook_set(wd->box, _show_region_hook, obj);
1266    elm_widget_sub_object_add(obj, wd->box);
1267    elm_smart_scroller_child_set(wd->scr, wd->box);
1268    evas_object_event_callback_add(wd->box, EVAS_CALLBACK_CHANGED_SIZE_HINTS,
1269                                   _changed_size_hints, obj);
1270
1271    evas_object_show(wd->box);
1272
1273    _theme_hook(obj);
1274
1275    wd->mode = ELM_LIST_SCROLL;
1276
1277    evas_object_smart_callback_add(wd->scr, "edge,left", _scroll_edge_left, obj);
1278    evas_object_smart_callback_add(wd->scr, "edge,right", _scroll_edge_right, obj);
1279    evas_object_smart_callback_add(wd->scr, "edge,top", _scroll_edge_top, obj);
1280    evas_object_smart_callback_add(wd->scr, "edge,bottom", _scroll_edge_bottom, obj);
1281
1282    evas_object_smart_callback_add(obj, "sub-object-del", _sub_del, obj);
1283    evas_object_smart_callback_add(obj, "scroll-hold-on", _hold_on, obj);
1284    evas_object_smart_callback_add(obj, "scroll-hold-off", _hold_off, obj);
1285    evas_object_smart_callback_add(obj, "scroll-freeze-on", _freeze_on, obj);
1286    evas_object_smart_callback_add(obj, "scroll-freeze-off", _freeze_off, obj);
1287
1288    _sizing_eval(obj);
1289    return obj;
1290 }
1291
1292 /**
1293  * Appends an item to the list object.
1294  *
1295  * @param obj The list object
1296  * @param label The label of the list item
1297  * @param icon The icon object to use for the left side of the item
1298  * @param end The icon object to use for the right side of the item
1299  * @param func The function to call when the item is clicked
1300  * @param data The data to associate with the item for related callbacks
1301  *
1302  * @return The created item or NULL upon failure
1303  *
1304  * @ingroup List
1305  */
1306 EAPI Elm_List_Item *
1307 elm_list_item_append(Evas_Object *obj, const char *label, Evas_Object *icon, Evas_Object *end, Evas_Smart_Cb func, const void *data)
1308 {
1309    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
1310    Widget_Data *wd = elm_widget_data_get(obj);
1311    Elm_List_Item *it = _item_new(obj, label, icon, end, func, data);
1312
1313    wd->items = eina_list_append(wd->items, it);
1314    it->node = eina_list_last(wd->items);
1315    elm_box_pack_end(wd->box, it->base.view);
1316    return it;
1317 }
1318
1319 /**
1320  * Prepends an item to the list object.
1321  *
1322  * @param obj The list object
1323  * @param label The label of the list item
1324  * @param icon The icon object to use for the left side of the item
1325  * @param end The icon object to use for the right side of the item
1326  * @param func The function to call when the item is clicked
1327  * @param data The data to associate with the item for related callbacks
1328  *
1329  * @return The created item or NULL upon failure
1330  *
1331  * @ingroup List
1332  */
1333 EAPI Elm_List_Item *
1334 elm_list_item_prepend(Evas_Object *obj, const char *label, Evas_Object *icon, Evas_Object *end, Evas_Smart_Cb func, const void *data)
1335 {
1336    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
1337    Widget_Data *wd = elm_widget_data_get(obj);
1338    Elm_List_Item *it = _item_new(obj, label, icon, end, func, data);
1339
1340    wd->items = eina_list_prepend(wd->items, it);
1341    it->node = wd->items;
1342    elm_box_pack_start(wd->box, it->base.view);
1343    return it;
1344 }
1345
1346 /**
1347  * Inserts an item into the list object before @p before.
1348  *
1349  * @param obj The list object
1350  * @param before The list item to insert before
1351  * @param label The label of the list item
1352  * @param icon The icon object to use for the left side of the item
1353  * @param end The icon object to use for the right side of the item
1354  * @param func The function to call when the item is clicked
1355  * @param data The data to associate with the item for related callbacks
1356  *
1357  * @return The created item or NULL upon failure
1358  *
1359  * @ingroup List
1360  */
1361 EAPI Elm_List_Item *
1362 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)
1363 {
1364    Widget_Data *wd;
1365    Elm_List_Item *it;
1366
1367    EINA_SAFETY_ON_NULL_RETURN_VAL(before, NULL);
1368    if (!before->node) return NULL;
1369    ELM_LIST_ITEM_CHECK_DELETED_RETURN(before, NULL);
1370
1371    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
1372    wd = elm_widget_data_get(obj);
1373    if (!wd) return NULL;
1374    it = _item_new(obj, label, icon, end, func, data);
1375    wd->items = eina_list_prepend_relative_list(wd->items, it, before->node);
1376    it->node = before->node->prev;
1377    elm_box_pack_before(wd->box, it->base.view, before->base.view);
1378    return it;
1379 }
1380
1381 /**
1382  * Inserts an item into the list object after @p after.
1383  *
1384  * @param obj The list object
1385  * @param after The list item to insert after
1386  * @param label The label of the list item
1387  * @param icon The icon object to use for the left side of the item
1388  * @param end The icon object to use for the right side of the item
1389  * @param func The function to call when the item is clicked
1390  * @param data The data to associate with the item for related callbacks
1391  *
1392  * @return The created item or NULL upon failure
1393  *
1394  * @ingroup List
1395  */
1396 EAPI Elm_List_Item *
1397 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)
1398 {
1399    Widget_Data *wd;
1400    Elm_List_Item *it;
1401
1402    EINA_SAFETY_ON_NULL_RETURN_VAL(after, NULL);
1403    if (!after->node) return NULL;
1404    ELM_LIST_ITEM_CHECK_DELETED_RETURN(after, NULL);
1405
1406    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
1407    wd = elm_widget_data_get(obj);
1408    if (!wd) return NULL;
1409    it = _item_new(obj, label, icon, end, func, data);
1410    wd->items = eina_list_append_relative_list(wd->items, it, after->node);
1411    it->node = after->node->next;
1412    elm_box_pack_after(wd->box, it->base.view, after->base.view);
1413    return it;
1414 }
1415
1416 /**
1417  * Insert a new item into the sorted list object.
1418  *
1419  * @param obj The list object
1420  * @param label The label of the list item
1421  * @param icon The icon object to use for the left side of the item
1422  * @param end The icon object to use for the right side of the item
1423  * @param func The function to call when the item is clicked
1424  * @param data The data to associate with the item for related callbacks
1425  * @param cmp_func The function called for the sort.
1426  *
1427  * @return The created item or NULL upon failure
1428  *
1429  * @ingroup List
1430  */
1431 EAPI Elm_List_Item *
1432 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)
1433 {
1434    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
1435    Widget_Data *wd = elm_widget_data_get(obj);
1436    Elm_List_Item *it = _item_new(obj, label, icon, end, func, data);
1437    Eina_List *l;
1438
1439    wd->items = eina_list_sorted_insert(wd->items, cmp_func, it);
1440    l = eina_list_data_find_list(wd->items, it);
1441    l = eina_list_next(l);
1442    if (!l)
1443      {
1444         it->node = eina_list_last(wd->items);
1445         elm_box_pack_end(wd->box, it->base.view);
1446      }
1447    else
1448      {
1449         Elm_List_Item *before = eina_list_data_get(l);
1450         it->node = before->node->prev;
1451         elm_box_pack_before(wd->box, it->base.view, before->base.view);
1452      }
1453    return it;
1454 }
1455
1456 /**
1457  * Clears a list of all items.
1458  *
1459  * @param obj The list object
1460  *
1461  * @ingroup List
1462  */
1463 EAPI void
1464 elm_list_clear(Evas_Object *obj)
1465 {
1466    ELM_CHECK_WIDTYPE(obj, widtype);
1467    Widget_Data *wd = elm_widget_data_get(obj);
1468    Elm_List_Item *it;
1469
1470    if (!wd) return;
1471    if (!wd->items) return;
1472
1473    eina_list_free(wd->selected);
1474    wd->selected = NULL;
1475
1476    if (wd->walking > 0)
1477      {
1478         Eina_List *n;
1479
1480         EINA_LIST_FOREACH(wd->items, n, it)
1481           {
1482              if (it->deleted) continue;
1483              it->deleted = EINA_TRUE;
1484              wd->to_delete = eina_list_append(wd->to_delete, it);
1485           }
1486         return;
1487      }
1488
1489    _elm_list_walk(wd);
1490
1491    EINA_LIST_FREE(wd->items, it)
1492      {
1493         elm_widget_item_pre_notify_del(it);
1494         _elm_list_item_free(it);
1495      }
1496
1497    _elm_list_unwalk(wd);
1498
1499    _fix_items(obj);
1500    _sizing_eval(obj);
1501 }
1502
1503 /**
1504  * Starts the list.  Call before running show() on the list object.
1505  *
1506  * @param obj The list object
1507  *
1508  * @ingroup List
1509  */
1510 EAPI void
1511 elm_list_go(Evas_Object *obj)
1512 {
1513    ELM_CHECK_WIDTYPE(obj, widtype);
1514    Widget_Data *wd = elm_widget_data_get(obj);
1515    if (!wd) return;
1516    _fix_items(obj);
1517 }
1518
1519 /**
1520  * Enables/disables the state of multi-select on the list object.
1521  *
1522  * @param obj The list object
1523  * @param multi If true, multi-select is enabled
1524  *
1525  * @ingroup List
1526  */
1527 EAPI void
1528 elm_list_multi_select_set(Evas_Object *obj, Eina_Bool multi)
1529 {
1530    ELM_CHECK_WIDTYPE(obj, widtype);
1531    Widget_Data *wd = elm_widget_data_get(obj);
1532    if (!wd) return;
1533    wd->multi = multi;
1534 }
1535
1536 /**
1537  * Gets the state of multi-select on the list object.
1538  *
1539  * @param obj The list object
1540  * @return If true, multi-select is enabled
1541  *
1542  * @ingroup List
1543  */
1544 EAPI Eina_Bool
1545 elm_list_multi_select_get(const Evas_Object *obj)
1546 {
1547    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
1548    Widget_Data *wd = elm_widget_data_get(obj);
1549    if (!wd) return EINA_FALSE;
1550    return wd->multi;
1551 }
1552
1553 /**
1554  * Set which mode to use for the list with.
1555  *
1556  * @param obj The list object
1557  * @param mode One of @c ELM_LIST_COMPRESS, @c ELM_LIST_SCROLL, @c
1558  *             ELM_LIST_LIMIT or @c ELM_LIST_EXPAND.
1559  *
1560  * @note Default value is @c ELM_LIST_SCROLL. At this mode, the list
1561  * object won't set any of its size hints to inform how a possible
1562  * container should resize it. Then, if it's not created as a "resize
1563  * object", it might end with zero dimensions. The list will respect
1564  * the container's geometry and, if any of its items won't fit into
1565  * its transverse axis, one will be able to scroll it in that
1566  * direction. @c ELM_LIST_COMPRESS is the same as the previous, except
1567  * that it <b>won't</b> let one scroll in the transverse axis, on
1568  * those cases (large items will get cropped). @c ELM_LIST_LIMIT will
1569  * actually set a minimun size hint on the list object, so that
1570  * containers may respect it (and resize itself to fit the child
1571  * properly). More specifically, a minimum size hint will be set for
1572  * its transverse axis, so that the <b>largest</b> item in that
1573  * direction fits well. @c ELM_LIST_EXPAND, besides setting a minimum
1574  * size on the transverse axis, just like the previous mode, will set
1575  * a minimum size on the longitudinal axis too, trying to reserve
1576  * space to all its children to be visible at a time. The last two
1577  * modes can always have effects bounded by setting the list object's
1578  * maximum size hints, though.
1579  *
1580  * @ingroup List
1581  */
1582 EAPI void
1583 elm_list_mode_set(Evas_Object *obj, Elm_List_Mode mode)
1584 {
1585    ELM_CHECK_WIDTYPE(obj, widtype);
1586
1587    Widget_Data *wd;
1588
1589    wd = elm_widget_data_get(obj);
1590    if (!wd)
1591      return;
1592    if (wd->mode == mode)
1593      return;
1594    wd->mode = mode;
1595
1596    _elm_list_mode_set_internal(wd);
1597 }
1598
1599 /**
1600  * Get the mode the list is at.
1601  *
1602  * @param obj The list object
1603  * @return mode One of @c ELM_LIST_COMPRESS, @c ELM_LIST_SCROLL or @c
1604  *         ELM_LIST_LIMIT (@c ELM_LIST_LAST on errors).
1605  *
1606  * @note see elm_list_mode_set() for more information.
1607  *
1608  * @ingroup List
1609  */
1610 EAPI Elm_List_Mode
1611 elm_list_mode_get(const Evas_Object *obj)
1612 {
1613    ELM_CHECK_WIDTYPE(obj, widtype) ELM_LIST_LAST;
1614    Widget_Data *wd = elm_widget_data_get(obj);
1615    if (!wd) return ELM_LIST_LAST;
1616    return wd->mode;
1617 }
1618
1619 /**
1620  * Enables/disables horizontal mode of the list.
1621  *
1622  * @param obj The list object
1623  * @param mode If true, horizontale mode is enabled
1624  *
1625  * @note Bounce options for the list will be reset to default values
1626  * with this funcion. Re-call elm_list_bounce_set() once more after
1627  * this one, if you had custom values.
1628  *
1629  * @ingroup List
1630  */
1631 EAPI void
1632 elm_list_horizontal_set(Evas_Object *obj, Eina_Bool horizontal)
1633 {
1634    ELM_CHECK_WIDTYPE(obj, widtype);
1635
1636    Widget_Data *wd;
1637    Eina_Bool bounce = _elm_config->thumbscroll_bounce_enable;
1638
1639    wd = elm_widget_data_get(obj);
1640    if (!wd)
1641      return;
1642
1643    if (wd->h_mode == horizontal)
1644      return;
1645
1646    wd->h_mode = horizontal;
1647    elm_box_horizontal_set(wd->box, horizontal);
1648
1649    if (horizontal)
1650      {
1651         evas_object_size_hint_weight_set(wd->box, 0.0, EVAS_HINT_EXPAND);
1652         evas_object_size_hint_align_set(wd->box, 0.0, EVAS_HINT_FILL);
1653         elm_smart_scroller_bounce_allow_set(wd->scr, bounce, EINA_FALSE);
1654      }
1655    else
1656      {
1657         evas_object_size_hint_weight_set(wd->box, EVAS_HINT_EXPAND, 0.0);
1658         evas_object_size_hint_align_set(wd->box, EVAS_HINT_FILL, 0.0);
1659         elm_smart_scroller_bounce_allow_set(wd->scr, EINA_FALSE, bounce);
1660      }
1661
1662    _elm_list_mode_set_internal(wd);
1663 }
1664
1665 /**
1666  * Retrieve whether horizontal mode is enabled for a list.
1667  *
1668  * @param obj The list object
1669  * @return @c EINA_TRUE, if horizontal mode is enabled and @c
1670  *            EINA_FALSE, otherwise.
1671  *
1672  * @note see elm_list_horizontal_set() for more information.
1673  *
1674  * @ingroup List
1675  */
1676 EAPI Eina_Bool
1677 elm_list_horizontal_get(const Evas_Object *obj)
1678 {
1679    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
1680
1681    Widget_Data *wd;
1682
1683    wd = elm_widget_data_get(obj);
1684    if (!wd)
1685      return EINA_FALSE;
1686
1687    return wd->h_mode;
1688 }
1689
1690 /**
1691  * Enables/disables the state of always_select, meaning that
1692  * an item will always be selected.
1693  *
1694  * @param obj The list object
1695  * @param always_select If true, always_select is enabled
1696  *
1697  * @ingroup List
1698  */
1699 EAPI void
1700 elm_list_always_select_mode_set(Evas_Object *obj, Eina_Bool always_select)
1701 {
1702    ELM_CHECK_WIDTYPE(obj, widtype);
1703    Widget_Data *wd = elm_widget_data_get(obj);
1704    if (!wd) return;
1705    wd->always_select = always_select;
1706 }
1707
1708 /**
1709  * Gets the state of always_select.
1710  * See also elm_list_always_select_mode_set()
1711  *
1712  * @param obj The list object
1713  * @return If true, always_select is enabled
1714  *
1715  * @ingroup List
1716  */
1717 EAPI Eina_Bool
1718 elm_list_always_select_mode_get(const Evas_Object *obj)
1719 {
1720    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
1721    Widget_Data *wd = elm_widget_data_get(obj);
1722    if (!wd) return EINA_FALSE;
1723    return wd->always_select;
1724 }
1725
1726 /**
1727  * Returns a list of all the list items.
1728  *
1729  * @param obj The list object
1730  * @return An Eina_List* of the list items, or NULL on failure
1731  *
1732  * @ingroup List
1733  */
1734 EAPI const Eina_List *
1735 elm_list_items_get(const Evas_Object *obj)
1736 {
1737    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
1738    Widget_Data *wd = elm_widget_data_get(obj);
1739    if (!wd) return NULL;
1740    return wd->items;
1741 }
1742
1743 /**
1744  * Returns the currently selected list item.
1745  *
1746  * @param obj The list object
1747  * @return The selected list item, or NULL on failure
1748  *
1749  * @ingroup List
1750  */
1751 EAPI Elm_List_Item *
1752 elm_list_selected_item_get(const Evas_Object *obj)
1753 {
1754    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
1755    Widget_Data *wd = elm_widget_data_get(obj);
1756    if (!wd) return NULL;
1757    if (wd->selected) return wd->selected->data;
1758    return NULL;
1759 }
1760
1761 /**
1762  * Returns a list of the currently selected list items.
1763  *
1764  * @param obj The list object
1765  * @return An Eina_List* of the selected list items, or NULL on failure
1766  *
1767  * @ingroup List
1768  */
1769 EAPI const Eina_List *
1770 elm_list_selected_items_get(const Evas_Object *obj)
1771 {
1772    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
1773    Widget_Data *wd = elm_widget_data_get(obj);
1774    if (!wd) return NULL;
1775    return wd->selected;
1776 }
1777
1778 /**
1779  * Sets if item is a separator.
1780  *
1781  * @param it The list item object
1782  * @param setting
1783  */
1784 EAPI void
1785 elm_list_item_separator_set(Elm_List_Item *it, Eina_Bool setting)
1786 {
1787    ELM_LIST_ITEM_CHECK_DELETED_RETURN(it);
1788    it->is_separator = !!setting;
1789 }
1790
1791 /**
1792  * Returns EINA_TRUE if Elm_List_Item is a separator.
1793  *
1794  * @param it The list item object
1795  */
1796 EAPI Eina_Bool
1797 elm_list_item_separator_get(const Elm_List_Item *it)
1798 {
1799    ELM_LIST_ITEM_CHECK_DELETED_RETURN(it, EINA_FALSE);
1800    return it->is_separator;
1801 }
1802
1803
1804 /**
1805  * Sets the selected state of @p it.
1806  *
1807  * @param it The list item
1808  * @param selected Enables/disables the selected state
1809  *
1810  * @ingroup List
1811  */
1812 EAPI void
1813 elm_list_item_selected_set(Elm_List_Item *it, Eina_Bool selected)
1814 {
1815    ELM_LIST_ITEM_CHECK_DELETED_RETURN(it);
1816
1817    Widget_Data *wd = elm_widget_data_get(it->base.widget);
1818    if (!wd) return;
1819
1820    selected = !!selected;
1821    if (it->selected == selected) return;
1822
1823    _elm_list_walk(wd);
1824
1825    if (selected)
1826      {
1827         if (!wd->multi)
1828           {
1829              while (wd->selected)
1830                 _item_unselect(wd->selected->data);
1831           }
1832         _item_hilight(it);
1833         _item_select(it);
1834      }
1835    else
1836       _item_unselect(it);
1837
1838    _elm_list_unwalk(wd);
1839 }
1840
1841 /**
1842  * Gets the selected state of @p it.
1843  *
1844  * @param it The list item
1845  * @return If true, the item is selected
1846  *
1847  * @ingroup List
1848  */
1849 EAPI Eina_Bool
1850 elm_list_item_selected_get(const Elm_List_Item *it)
1851 {
1852    ELM_LIST_ITEM_CHECK_DELETED_RETURN(it, EINA_FALSE);
1853    return it->selected;
1854 }
1855
1856 /**
1857  * Brings @p it to the center of the list view.
1858  *
1859  * @param it The list item
1860  *
1861  * @ingroup List
1862  */
1863 EAPI void
1864 elm_list_item_show(Elm_List_Item *it)
1865 {
1866    ELM_LIST_ITEM_CHECK_DELETED_RETURN(it);
1867    Widget_Data *wd = elm_widget_data_get(it->base.widget);
1868    Evas_Coord bx, by, bw, bh;
1869    Evas_Coord x, y, w, h;
1870
1871    evas_object_geometry_get(wd->box, &bx, &by, &bw, &bh);
1872    evas_object_geometry_get(it->base.view, &x, &y, &w, &h);
1873    x -= bx;
1874    y -= by;
1875    if (wd->scr)
1876      elm_smart_scroller_child_region_show(wd->scr, x, y, w, h);
1877 }
1878
1879 /**
1880  * Bring in the given item
1881  *
1882  * This causes list to jump to the given item @p it and show it (by scrolling),
1883  * if it is not fully visible. This may use animation to do so and take a
1884  * period of time
1885  *
1886  * @param it The item
1887  *
1888  * @ingroup List
1889  */
1890 EAPI void
1891 elm_list_item_bring_in(Elm_List_Item *it)
1892 {
1893    ELM_LIST_ITEM_CHECK_DELETED_RETURN(it);
1894    Widget_Data *wd = elm_widget_data_get(it->base.widget);
1895    Evas_Coord bx, by, bw, bh;
1896    Evas_Coord x, y, w, h;
1897
1898    evas_object_geometry_get(wd->box, &bx, &by, &bw, &bh);
1899    evas_object_geometry_get(it->base.view, &x, &y, &w, &h);
1900    x -= bx;
1901    y -= by;
1902    if (wd->scr)
1903      elm_smart_scroller_region_bring_in(wd->scr, x, y, w, h);
1904 }
1905
1906 /**
1907  * Deletes item @p it from the list.
1908  *
1909  * @param it The list item to delete
1910  *
1911  * @ingroup List
1912  */
1913 EAPI void
1914 elm_list_item_del(Elm_List_Item *it)
1915 {
1916    ELM_LIST_ITEM_CHECK_DELETED_RETURN(it);
1917    Widget_Data *wd = elm_widget_data_get(it->base.widget);
1918    if (!wd) return;
1919
1920    if (it->selected) _item_unselect(it);
1921
1922    if (wd->walking > 0)
1923      {
1924         if (it->deleted) return;
1925         it->deleted = EINA_TRUE;
1926         wd->to_delete = eina_list_append(wd->to_delete, it);
1927         return;
1928      }
1929
1930    wd->items = eina_list_remove_list(wd->items, it->node);
1931
1932    _elm_list_walk(wd);
1933
1934    elm_widget_item_pre_notify_del(it);
1935    _elm_list_item_free(it);
1936
1937    _elm_list_unwalk(wd);
1938 }
1939
1940 /**
1941  * Set the function called when a list item is freed.
1942  *
1943  * @param it The item to set the callback on
1944  * @param func The function called
1945  *
1946  * @ingroup List
1947  */
1948 EAPI void
1949 elm_list_item_del_cb_set(Elm_List_Item *it, Evas_Smart_Cb func)
1950 {
1951    ELM_LIST_ITEM_CHECK_DELETED_RETURN(it);
1952    elm_widget_item_del_cb_set(it, func);
1953 }
1954
1955 /**
1956  * Returns the data associated with the item.
1957  *
1958  * @param it The list item
1959  * @return The data associated with @p it
1960  *
1961  * @ingroup List
1962  */
1963 EAPI void *
1964 elm_list_item_data_get(const Elm_List_Item *it)
1965 {
1966    ELM_LIST_ITEM_CHECK_DELETED_RETURN(it, NULL);
1967    return elm_widget_item_data_get(it);
1968 }
1969
1970 /**
1971  * Returns the left side icon associated with the item.
1972  *
1973  * @param it The list item
1974  * @return The left side icon associated with @p it
1975  *
1976  * @ingroup List
1977  */
1978 EAPI Evas_Object *
1979 elm_list_item_icon_get(const Elm_List_Item *it)
1980 {
1981    ELM_LIST_ITEM_CHECK_DELETED_RETURN(it, NULL);
1982    if (it->dummy_icon) return NULL;
1983    return it->icon;
1984 }
1985
1986 /**
1987  * Sets the left side icon associated with the item.
1988  *
1989  * Once the icon object is set, a previously set one will be deleted.
1990  * You probably don't want, then, to have the <b>same</b> icon object set
1991  * for more than one item of the list.
1992  *
1993  * @param it The list item
1994  * @param icon The left side icon object to associate with @p it
1995  *
1996  * @ingroup List
1997  */
1998 EAPI void
1999 elm_list_item_icon_set(Elm_List_Item *it, Evas_Object *icon)
2000 {
2001    ELM_LIST_ITEM_CHECK_DELETED_RETURN(it);
2002    if (it->icon == icon) return;
2003    if ((it->dummy_icon) && (!icon)) return;
2004    if (it->dummy_icon)
2005      {
2006         evas_object_del(it->icon);
2007         it->dummy_icon = EINA_FALSE;
2008      }
2009    if (!icon)
2010      {
2011         icon = evas_object_rectangle_add(evas_object_evas_get(it->base.widget));
2012         evas_object_color_set(icon, 0, 0, 0, 0);
2013         it->dummy_icon = EINA_TRUE;
2014      }
2015    if (it->icon)
2016      {
2017         evas_object_del(it->icon);
2018         it->icon = NULL;
2019      }
2020    it->icon = icon;
2021    if (it->base.view)
2022      edje_object_part_swallow(it->base.view, "elm.swallow.icon", icon);
2023 }
2024
2025 /**
2026  * Gets the right side icon associated with the item.
2027  *
2028  * @param it The list item
2029  * @return The right side icon object associated with @p it
2030  *
2031  * @ingroup List
2032  */
2033 EAPI Evas_Object *
2034 elm_list_item_end_get(const Elm_List_Item *it)
2035 {
2036    ELM_LIST_ITEM_CHECK_DELETED_RETURN(it, NULL);
2037    if (it->dummy_end) return NULL;
2038    return it->end;
2039 }
2040
2041 /**
2042  * Sets the right side icon associated with the item.
2043  *
2044  * Once the icon object is set, a previously set one will be deleted.
2045  * You probably don't want, then, to have the <b>same</b> icon object set
2046  * for more than one item of the list.
2047  *
2048  * @param it The list item
2049  * @param icon The right side icon object to associate with @p it
2050  *
2051  * @ingroup List
2052  */
2053 EAPI void
2054 elm_list_item_end_set(Elm_List_Item *it, Evas_Object *end)
2055 {
2056    ELM_LIST_ITEM_CHECK_DELETED_RETURN(it);
2057    if (it->end == end) return;
2058    if ((it->dummy_end) && (!end)) return;
2059    if (it->dummy_end)
2060      {
2061         evas_object_del(it->end);
2062         it->dummy_icon = EINA_FALSE;
2063      }
2064    if (!end)
2065      {
2066         end = evas_object_rectangle_add(evas_object_evas_get(it->base.widget));
2067         evas_object_color_set(end, 0, 0, 0, 0);
2068         it->dummy_end = EINA_TRUE;
2069      }
2070    if (it->end)
2071      {
2072         evas_object_del(it->end);
2073         it->end = NULL;
2074      }
2075    it->end = end;
2076    if (it->base.view)
2077      edje_object_part_swallow(it->base.view, "elm.swallow.end", end);
2078 }
2079
2080 /**
2081  * Gets the base object of the item.
2082  *
2083  * @param it The list item
2084  * @return The base object associated with @p it
2085  *
2086  * @ingroup List
2087  */
2088 EAPI Evas_Object *
2089 elm_list_item_base_get(const Elm_List_Item *it)
2090 {
2091    ELM_LIST_ITEM_CHECK_DELETED_RETURN(it, NULL);
2092    return it->base.view;
2093 }
2094
2095 /**
2096  * Gets the label of the item.
2097  *
2098  * @param it The list item
2099  * @return The label of @p it
2100  *
2101  * @ingroup List
2102  */
2103 EAPI const char *
2104 elm_list_item_label_get(const Elm_List_Item *it)
2105 {
2106    ELM_LIST_ITEM_CHECK_DELETED_RETURN(it, NULL);
2107    return it->label;
2108 }
2109
2110 /**
2111  * Sets the label of the item.
2112  *
2113  * @param it The list item
2114  * @param text The label of @p it
2115  *
2116  * @ingroup List
2117  */
2118 EAPI void
2119 elm_list_item_label_set(Elm_List_Item *it, const char *text)
2120 {
2121    ELM_LIST_ITEM_CHECK_DELETED_RETURN(it);
2122    if (!eina_stringshare_replace(&it->label, text)) return;
2123    if (it->base.view)
2124      edje_object_part_text_set(it->base.view, "elm.text", it->label);
2125 }
2126
2127 /**
2128  * Gets the item before @p it in the list.
2129  *
2130  * @param it The list item
2131  * @return The item before @p it, or NULL on failure
2132  *
2133  * @ingroup List
2134  */
2135 EAPI Elm_List_Item *
2136 elm_list_item_prev(const Elm_List_Item *it)
2137 {
2138    ELM_LIST_ITEM_CHECK_DELETED_RETURN(it, NULL);
2139    if (it->node->prev) return it->node->prev->data;
2140    else return NULL;
2141 }
2142
2143 /**
2144  * Gets the item after @p it in the list.
2145  *
2146  * @param it The list item
2147  * @return The item after @p it, or NULL on failure
2148  *
2149  * @ingroup List
2150  */
2151 EAPI Elm_List_Item *
2152 elm_list_item_next(const Elm_List_Item *it)
2153 {
2154    ELM_LIST_ITEM_CHECK_DELETED_RETURN(it, NULL);
2155    if (it->node->next) return it->node->next->data;
2156    else return NULL;
2157 }
2158
2159 /**
2160  * Set the text to be shown in the list item.
2161  *
2162  * @param item Target item
2163  * @param text The text to set in the content
2164  *
2165  * Setup the text as tooltip to object. The item can have only one tooltip,
2166  * so any previous tooltip data is removed.
2167  *
2168  * @ingroup List
2169  */
2170 EAPI void
2171 elm_list_item_tooltip_text_set(Elm_List_Item *item, const char *text)
2172 {
2173    ELM_LIST_ITEM_CHECK_DELETED_RETURN(item);
2174    elm_widget_item_tooltip_text_set(item, text);
2175 }
2176
2177 /**
2178  * Set the content to be shown in the tooltip item
2179  *
2180  * Setup the tooltip to item. The item can have only one tooltip,
2181  * so any previous tooltip data is removed. @p func(with @p data) will
2182  * be called every time that need show the tooltip and it should
2183  * return a valid Evas_Object. This object is then managed fully by
2184  * tooltip system and is deleted when the tooltip is gone.
2185  *
2186  * @param item the list item being attached a tooltip.
2187  * @param func the function used to create the tooltip contents.
2188  * @param data what to provide to @a func as callback data/context.
2189  * @param del_cb called when data is not needed anymore, either when
2190  *        another callback replaces @func, the tooltip is unset with
2191  *        elm_list_item_tooltip_unset() or the owner @a item
2192  *        dies. This callback receives as the first parameter the
2193  *        given @a data, and @c event_info is the item.
2194  *
2195  * @ingroup List
2196  */
2197 EAPI void
2198 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)
2199 {
2200    ELM_LIST_ITEM_CHECK_DELETED_RETURN(item);
2201    elm_widget_item_tooltip_content_cb_set(item, func, data, del_cb);
2202 }
2203
2204 /**
2205  * Unset tooltip from item
2206  *
2207  * @param item list item to remove previously set tooltip.
2208  *
2209  * Remove tooltip from item. The callback provided as del_cb to
2210  * elm_list_item_tooltip_content_cb_set() will be called to notify
2211  * it is not used anymore.
2212  *
2213  * @see elm_list_item_tooltip_content_cb_set()
2214  *
2215  * @ingroup List
2216  */
2217 EAPI void
2218 elm_list_item_tooltip_unset(Elm_List_Item *item)
2219 {
2220    ELM_LIST_ITEM_CHECK_DELETED_RETURN(item);
2221    elm_widget_item_tooltip_unset(item);
2222 }
2223
2224 /**
2225  * Sets a different style for this item tooltip.
2226  *
2227  * @note before you set a style you should define a tooltip with
2228  *       elm_list_item_tooltip_content_cb_set() or
2229  *       elm_list_item_tooltip_text_set()
2230  *
2231  * @param item list item with tooltip already set.
2232  * @param style the theme style to use (default, transparent, ...)
2233  *
2234  * @ingroup List
2235  */
2236 EAPI void
2237 elm_list_item_tooltip_style_set(Elm_List_Item *item, const char *style)
2238 {
2239    ELM_LIST_ITEM_CHECK_DELETED_RETURN(item);
2240    elm_widget_item_tooltip_style_set(item, style);
2241 }
2242
2243 /**
2244  * Get the style for this item tooltip.
2245  *
2246  * @param item list item with tooltip already set.
2247  * @return style the theme style in use, defaults to "default". If the
2248  *         object does not have a tooltip set, then NULL is returned.
2249  *
2250  * @ingroup List
2251  */
2252 EAPI const char *
2253 elm_list_item_tooltip_style_get(const Elm_List_Item *item)
2254 {
2255    ELM_LIST_ITEM_CHECK_DELETED_RETURN(item, NULL);
2256    return elm_widget_item_tooltip_style_get(item);
2257 }
2258
2259 /**
2260  * Set the cursor to be shown when mouse is over the list item
2261  *
2262  * @param item Target item
2263  * @param cursor the cursor name to be used.
2264  *
2265  * @see elm_object_cursor_set()
2266  * @ingroup List
2267  */
2268 EAPI void
2269 elm_list_item_cursor_set(Elm_List_Item *item, const char *cursor)
2270 {
2271    ELM_LIST_ITEM_CHECK_DELETED_RETURN(item);
2272    elm_widget_item_cursor_set(item, cursor);
2273 }
2274
2275 /**
2276  * Get the cursor to be shown when mouse is over the list item
2277  *
2278  * @param item list item with cursor already set.
2279  * @return the cursor name.
2280  *
2281  * @ingroup List
2282  */
2283 EAPI const char *
2284 elm_list_item_cursor_get(const Elm_List_Item *item)
2285 {
2286    ELM_LIST_ITEM_CHECK_DELETED_RETURN(item, NULL);
2287    return elm_widget_item_cursor_get(item);
2288 }
2289
2290 /**
2291  * Unset the cursor to be shown when mouse is over the list item
2292  *
2293  * @param item Target item
2294  *
2295  * @see elm_object_cursor_unset()
2296  * @ingroup List
2297  */
2298 EAPI void
2299 elm_list_item_cursor_unset(Elm_List_Item *item)
2300 {
2301    ELM_LIST_ITEM_CHECK_DELETED_RETURN(item);
2302    elm_widget_item_cursor_unset(item);
2303 }
2304
2305 /**
2306  * Sets a different style for this item cursor.
2307  *
2308  * @note before you set a style you should define a cursor with
2309  *       elm_list_item_cursor_set()
2310  *
2311  * @param item list item with cursor already set.
2312  * @param style the theme style to use (default, transparent, ...)
2313  *
2314  * @ingroup List
2315  */
2316 EAPI void
2317 elm_list_item_cursor_style_set(Elm_List_Item *item, const char *style)
2318 {
2319    ELM_LIST_ITEM_CHECK_DELETED_RETURN(item);
2320    elm_widget_item_cursor_style_set(item, style);
2321 }
2322
2323 /**
2324  * Get the style for this item cursor.
2325  *
2326  * @param item list item with cursor already set.
2327  * @return style the theme style in use, defaults to "default". If the
2328  *         object does not have a cursor set, then NULL is returned.
2329  *
2330  * @ingroup List
2331  */
2332 EAPI const char *
2333 elm_list_item_cursor_style_get(const Elm_List_Item *item)
2334 {
2335    ELM_LIST_ITEM_CHECK_DELETED_RETURN(item, NULL);
2336    return elm_widget_item_cursor_style_get(item);
2337 }
2338
2339 /**
2340  * Set if the cursor set should be searched on the theme or should use
2341  * the provided by the engine, only.
2342  *
2343  * @note before you set if should look on theme you should define a cursor
2344  * with elm_object_cursor_set(). By default it will only look for cursors
2345  * provided by the engine.
2346  *
2347  * @param item widget item with cursor already set.
2348  * @param engine_only boolean to define it cursors should be looked only
2349  * between the provided by the engine or searched on widget's theme as well.
2350  *
2351  * @ingroup List
2352  */
2353 EAPI void
2354 elm_list_item_cursor_engine_only_set(Elm_List_Item *item, Eina_Bool engine_only)
2355 {
2356    ELM_LIST_ITEM_CHECK_DELETED_RETURN(item);
2357    elm_widget_item_cursor_engine_only_set(item, engine_only);
2358 }
2359
2360 /**
2361  * Get the cursor engine only usage for this item cursor.
2362  *
2363  * @param item widget item with cursor already set.
2364  * @return engine_only boolean to define it cursors should be looked only
2365  * between the provided by the engine or searched on widget's theme as well. If
2366  *         the object does not have a cursor set, then EINA_FALSE is returned.
2367  *
2368  * @ingroup List
2369  */
2370 EAPI Eina_Bool
2371 elm_list_item_cursor_engine_only_get(const Elm_List_Item *item)
2372 {
2373    ELM_LIST_ITEM_CHECK_DELETED_RETURN(item, EINA_FALSE);
2374    return elm_widget_item_cursor_engine_only_get(item);
2375 }
2376
2377 /**
2378  * Set bounce mode
2379  *
2380  * This will enable or disable the scroller bounce mode for the list. See
2381  * elm_scroller_bounce_set() for details
2382  *
2383  * @param obj The list object
2384  * @param h_bounce Allow bounce horizontally
2385  * @param v_bounce Allow bounce vertically
2386  *
2387  * @ingroup List
2388  */
2389 EAPI void
2390 elm_list_bounce_set(Evas_Object *obj, Eina_Bool h_bounce, Eina_Bool v_bounce)
2391 {
2392    ELM_CHECK_WIDTYPE(obj, widtype);
2393    Widget_Data *wd = elm_widget_data_get(obj);
2394    if (!wd) return;
2395    if (wd->scr)
2396      elm_smart_scroller_bounce_allow_set(wd->scr, h_bounce, v_bounce);
2397 }
2398
2399 /**
2400  * Get the bounce mode
2401  *
2402  * @param obj The List object
2403  * @param h_bounce Allow bounce horizontally
2404  * @param v_bounce Allow bounce vertically
2405  *
2406  * @ingroup List
2407  */
2408 EAPI void
2409 elm_list_bounce_get(const Evas_Object *obj, Eina_Bool *h_bounce, Eina_Bool *v_bounce)
2410 {
2411    ELM_CHECK_WIDTYPE(obj, widtype);
2412    Widget_Data *wd = elm_widget_data_get(obj);
2413    if (!wd) return;
2414    elm_smart_scroller_bounce_allow_get(wd->scr, h_bounce, v_bounce);
2415 }
2416
2417 /**
2418  * Set the scrollbar policy
2419  *
2420  * This sets the scrollbar visibility policy for the given list scroller.
2421  * ELM_SMART_SCROLLER_POLICY_AUTO means the scrollber is made visible if it
2422  * is needed, and otherwise kept hidden. ELM_SMART_SCROLLER_POLICY_ON turns
2423  * it on all the time, and ELM_SMART_SCROLLER_POLICY_OFF always keeps it off.
2424  * This applies respectively for the horizontal and vertical scrollbars.
2425  *
2426  * @param obj The list object
2427  * @param policy_h Horizontal scrollbar policy
2428  * @param policy_v Vertical scrollbar policy
2429  *
2430  * @ingroup List
2431  */
2432 EAPI void
2433 elm_list_scroller_policy_set(Evas_Object *obj, Elm_Scroller_Policy policy_h, Elm_Scroller_Policy policy_v)
2434 {
2435    ELM_CHECK_WIDTYPE(obj, widtype);
2436    Widget_Data *wd = elm_widget_data_get(obj);
2437    if (!wd) return;
2438    if ((policy_h >= ELM_SCROLLER_POLICY_LAST) ||
2439        (policy_v >= ELM_SCROLLER_POLICY_LAST))
2440    if (wd->scr)
2441      elm_smart_scroller_policy_set(wd->scr, policy_h, policy_v);
2442 }
2443
2444 EAPI void
2445 elm_list_scroller_policy_get(const Evas_Object *obj, Elm_Scroller_Policy *policy_h, Elm_Scroller_Policy *policy_v)
2446 {
2447    ELM_CHECK_WIDTYPE(obj, widtype);
2448    Widget_Data *wd = elm_widget_data_get(obj);
2449    Elm_Smart_Scroller_Policy s_policy_h, s_policy_v;
2450    if ((!wd) || (!wd->scr)) return;
2451    elm_smart_scroller_policy_get(wd->scr, &s_policy_h, &s_policy_v);
2452    if (policy_h) *policy_h = (Elm_Scroller_Policy) s_policy_h;
2453    if (policy_v) *policy_v = (Elm_Scroller_Policy) s_policy_v;
2454 }
2455
2456 /**
2457  * Sets the disabled/enabled state of a list item.
2458  *
2459  * A disabled item cannot be selected or unselected. It will also
2460  * change its appearance (generally greyed out). This sets the
2461  * disabled state (@c EINA_TRUE for disabled, @c EINA_FALSE for
2462  * enabled).
2463  *
2464  * @param it The item
2465  * @param disabled The disabled state
2466  *
2467  * @ingroup List
2468  */
2469 EAPI void
2470 elm_list_item_disabled_set(Elm_List_Item *it, Eina_Bool disabled)
2471 {
2472    ELM_LIST_ITEM_CHECK_DELETED_RETURN(it);
2473
2474    if (it->disabled == disabled)
2475      return;
2476
2477    it->disabled = !!disabled;
2478
2479    if (it->disabled)
2480      edje_object_signal_emit(it->base.view, "elm,state,disabled", "elm");
2481    else
2482      edje_object_signal_emit(it->base.view, "elm,state,enabled", "elm");
2483 }
2484
2485 /**
2486  * Get the disabled/enabled state of a list item
2487  *
2488  * @param it The item
2489  * @return The disabled state
2490  *
2491  * See elm_list_item_disabled_set().
2492  *
2493  * @ingroup List
2494  */
2495 EAPI Eina_Bool
2496 elm_list_item_disabled_get(const Elm_List_Item *it)
2497 {
2498    ELM_LIST_ITEM_CHECK_DELETED_RETURN(it, EINA_FALSE);
2499
2500    return it->disabled;
2501 }
2502