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