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