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