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