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