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