move elementary to trunk base. out of TMP/st.
[framework/uifw/elementary.git] / src / lib / elm_index.c
1 #include <Elementary.h>
2 #include "elm_priv.h"
3
4 /**
5  * @defgroup Index Index
6  *
7  * An index object is a type of list that categorizes items in it
8  * by letter.
9  */
10
11 typedef struct _Widget_Data Widget_Data;
12
13 struct _Widget_Data
14 {
15    Evas_Object *base;
16    Evas_Object *event[2];
17    Evas_Object *bx[2]; // 2 - for now all that's supported
18    Eina_List *items; // 1 list. yes N levels, but only 2 for now and # of items will be small
19    int level;
20    Evas_Coord dx, dy;
21    Ecore_Timer *delay;
22    Eina_Bool level_active[2];
23    Eina_Bool horizontal : 1;
24    Eina_Bool active : 1;
25    Eina_Bool down : 1;
26 };
27
28 struct _Elm_Index_Item
29 {
30    Elm_Widget_Item base;
31    const char *letter;
32    int level;
33    Eina_Bool selected : 1;
34 };
35
36 static const char *widtype = NULL;
37 static void _theme_hook(Evas_Object *obj);
38 static void _sizing_eval(Evas_Object *obj);
39 static void _index_box_auto_fill(Evas_Object *obj, Evas_Object *box, int level);
40 static void _index_box_clear(Evas_Object *obj, Evas_Object *box, int level);
41 static void _item_free(Elm_Index_Item *it);
42
43 static void
44 _del_pre_hook(Evas_Object *obj)
45 {
46    Widget_Data *wd = elm_widget_data_get(obj);
47    if (!wd) return;
48    _index_box_clear(obj, wd->bx[wd->level], wd->level);
49    _index_box_clear(obj, wd->bx[0], 0);
50    while (wd->items) _item_free(wd->items->data);
51    if (wd->delay) ecore_timer_del(wd->delay);
52 }
53
54 static void
55 _del_hook(Evas_Object *obj)
56 {
57    Widget_Data *wd = elm_widget_data_get(obj);
58    free(wd);
59 }
60
61 static void
62 _layout(Evas_Object *o, Evas_Object_Box_Data *priv, void *data)
63 {
64    Widget_Data *wd = data;
65    if (!wd) return;
66    _els_box_layout(o, priv, wd->horizontal, 1);
67 }
68
69 static void
70 _signal_emit_hook(Evas_Object *obj, const char *emission, const char *source)
71 {
72    Widget_Data *wd = elm_widget_data_get(obj);
73    if (!wd) return;
74    edje_object_signal_emit(wd->base, emission, source);
75 }
76
77 static void
78 _signal_callback_add_hook(Evas_Object *obj, const char *emission, const char *source, void (*func_cb) (void *data, Evas_Object *o, const char *emission, const char *source), void *data)
79 {
80    Widget_Data *wd = elm_widget_data_get(obj);
81    if (!wd) return;
82    edje_object_signal_callback_add(wd->base, emission, source, func_cb, data);
83 }
84
85 static void
86 _signal_callback_del_hook(Evas_Object *obj, const char *emission, const char *source, void (*func_cb) (void *data, Evas_Object *o, const char *emission, const char *source), void *data)
87 {
88    Widget_Data *wd = elm_widget_data_get(obj);
89    edje_object_signal_callback_del_full(wd->base, emission, source, func_cb,
90                                         data);
91 }
92
93 static void
94 _theme_hook(Evas_Object *obj)
95 {
96    Evas_Coord minw = 0, minh = 0;
97    Widget_Data *wd = elm_widget_data_get(obj);
98    if (!wd) return;
99    _index_box_clear(obj, wd->bx[0], 0);
100    _index_box_clear(obj, wd->bx[1], 1);
101    if (wd->horizontal)
102      _elm_theme_object_set(obj, wd->base, "index", "base/horizontal", elm_widget_style_get(obj));
103    else
104      _elm_theme_object_set(obj, wd->base, "index", "base/vertical", elm_widget_style_get(obj));
105    edje_object_part_swallow(wd->base, "elm.swallow.event.0", wd->event[0]);
106    elm_coords_finger_size_adjust(1, &minw, 1, &minh);
107    evas_object_size_hint_min_set(wd->event[0], minw, minh);
108    edje_object_part_swallow(wd->base, "elm.swallow.index.0", wd->bx[0]);
109    if (edje_object_part_exists(wd->base, "elm.swallow.index.1"))
110      {
111         if (!wd->bx[1])
112           {
113              wd->bx[1] = evas_object_box_add(evas_object_evas_get(wd->base));
114              evas_object_box_layout_set(wd->bx[1], _layout, wd, NULL);
115              elm_widget_sub_object_add(obj, wd->bx[1]);
116           }
117         edje_object_part_swallow(wd->base, "elm.swallow.index.1", wd->bx[1]);
118         evas_object_show(wd->bx[1]);
119      }
120    else if (wd->bx[1])
121      {
122         evas_object_del(wd->bx[1]);
123         wd->bx[1] = NULL;
124      }
125    if (edje_object_part_exists(wd->base, "elm.swallow.event.1"))
126      {
127         if (!wd->event[1])
128           {
129              wd->event[1] = evas_object_rectangle_add(evas_object_evas_get(wd->base));
130              evas_object_color_set(wd->event[1], 0, 0, 0, 0);
131              elm_widget_sub_object_add(obj, wd->event[1]);
132           }
133         edje_object_part_swallow(wd->base, "elm.swallow.event.1", wd->event[1]);
134         evas_object_size_hint_min_set(wd->event[1], minw, minh);
135      }
136    else if (wd->event[1])
137      {
138         evas_object_del(wd->event[1]);
139         wd->event[1] = NULL;
140      }
141    edje_object_message_signal_process(wd->base);
142    edje_object_scale_set(wd->base, elm_widget_scale_get(obj) * _elm_config->scale);
143    _sizing_eval(obj);
144    _index_box_auto_fill(obj, wd->bx[0], 0);
145    if (wd->active)
146      if (wd->level == 1)
147        _index_box_auto_fill(obj, wd->bx[1], 1);
148 }
149
150 static void
151 _sizing_eval(Evas_Object *obj)
152 {
153    Widget_Data *wd = elm_widget_data_get(obj);
154    Evas_Coord minw = -1, minh = -1, maxw = -1, maxh = -1;
155    if (!wd) return;
156    edje_object_size_min_calc(wd->base, &minw, &minh);
157    evas_object_size_hint_min_set(obj, minw, minh);
158    evas_object_size_hint_max_set(obj, maxw, maxh);
159 }
160
161 static Elm_Index_Item *
162 _item_new(Evas_Object *obj, const char *letter, const void *item)
163 {
164    Widget_Data *wd = elm_widget_data_get(obj);
165    Elm_Index_Item *it;
166    if (!wd) return NULL;
167    it = elm_widget_item_new(obj, Elm_Index_Item);
168    if (!it) return NULL;
169    it->letter = eina_stringshare_add(letter);
170    it->base.data = item;
171    it->level = wd->level;
172    return it;
173 }
174
175 static Elm_Index_Item *
176 _item_find(Evas_Object *obj, const void *item)
177 {
178    Widget_Data *wd = elm_widget_data_get(obj);
179    Eina_List *l;
180    Elm_Index_Item *it;
181    if (!wd) return NULL;
182    EINA_LIST_FOREACH(wd->items, l, it)
183      if (it->base.data == item) return it;
184    return NULL;
185 }
186
187 static void
188 _item_free(Elm_Index_Item *it)
189 {
190    Widget_Data *wd = elm_widget_data_get(it->base.widget);
191    if (!wd) return;
192    wd->items = eina_list_remove(wd->items, it);
193    elm_widget_item_pre_notify_del(it);
194    eina_stringshare_del(it->letter);
195    elm_widget_item_del(it);
196 }
197
198 // FIXME: always have index filled
199 static void
200 _index_box_auto_fill(Evas_Object *obj, Evas_Object *box, int level)
201 {
202    Widget_Data *wd = elm_widget_data_get(obj);
203    Eina_List *l;
204    Elm_Index_Item *it;
205    Evas_Coord mw, mh, w, h;
206    int i = 0;
207    if (!wd) return;
208    if (wd->level_active[level]) return;
209    evas_object_geometry_get(box, NULL, NULL, &w, &h);
210    EINA_LIST_FOREACH(wd->items, l, it)
211      {
212         Evas_Object *o;
213         const char *stacking;
214
215         if (it->level != level) continue;
216         o = edje_object_add(evas_object_evas_get(obj));
217         it->base.view = o;
218         if (i & 0x1)
219           _elm_theme_object_set(obj, o, "index", "item_odd/vertical", elm_widget_style_get(obj));
220         else
221           _elm_theme_object_set(obj, o, "index", "item/vertical", elm_widget_style_get(obj));
222         edje_object_part_text_set(o, "elm.text", it->letter);
223         edje_object_size_min_restricted_calc(o, &mw, &mh, 0, 0);
224         evas_object_size_hint_min_set(o, mw, mh);
225         evas_object_size_hint_weight_set(o, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
226         evas_object_size_hint_align_set(o, EVAS_HINT_FILL, EVAS_HINT_FILL);
227         elm_widget_sub_object_add(obj, o);
228         evas_object_box_append(box, o);
229         stacking = edje_object_data_get(o, "stacking");
230         if (stacking)
231           {
232              if (!strcmp(stacking, "below")) evas_object_lower(o);
233              else if (!strcmp(stacking, "above")) evas_object_raise(o);
234           }
235         evas_object_show(o);
236         i++;
237         evas_object_smart_calculate(box); // force a calc so we know the size
238         evas_object_size_hint_min_get(box, &mw, &mh);
239         if (mh > h)
240           {
241              _index_box_clear(obj, box, level);
242              if (i > 0)
243                {
244                   // FIXME: only i objects fit! try again. overflows right now
245                }
246           }
247      }
248    evas_object_smart_calculate(box);
249    wd->level_active[level] = 1;
250 }
251
252 static void
253 _index_box_clear(Evas_Object *obj, Evas_Object *box __UNUSED__, int level)
254 {
255    Widget_Data *wd = elm_widget_data_get(obj);
256    Eina_List *l;
257    Elm_Index_Item *it;
258    if (!wd) return;
259    if (!wd->level_active[level]) return;
260    EINA_LIST_FOREACH(wd->items, l, it)
261      {
262         if (!it->base.view) continue;
263         if (it->level != level) continue;
264         evas_object_del(it->base.view);
265         it->base.view = NULL;
266      }
267    wd->level_active[level] = 0;
268 }
269
270 static Eina_Bool
271 _delay_change(void *data)
272 {
273    Widget_Data *wd = elm_widget_data_get(data);
274    void *d;
275    if (!wd) return ECORE_CALLBACK_CANCEL;
276    wd->delay = NULL;
277    d = (void *)elm_index_item_selected_get(data, wd->level);
278    if (d) evas_object_smart_callback_call(data, "delay,changed", d);
279    return ECORE_CALLBACK_CANCEL;
280 }
281
282 static void
283 _sel_eval(Evas_Object *obj, Evas_Coord evx, Evas_Coord evy)
284 {
285    Widget_Data *wd = elm_widget_data_get(obj);
286    Elm_Index_Item *it, *it_closest, *it_last;
287    Eina_List *l;
288    Evas_Coord x, y, w, h, bx, by, bw, bh, xx, yy;
289    double cdv = 0.5;
290    Evas_Coord dist;
291    char *label = NULL, *last = NULL;
292    int i;
293    if (!wd) return;
294    for (i = 0; i <= wd->level; i++)
295      {
296         it_last = NULL;
297         it_closest  = NULL;
298         dist = 0x7fffffff;
299         evas_object_geometry_get(wd->bx[i], &bx, &by, &bw, &bh);
300         EINA_LIST_FOREACH(wd->items, l, it)
301           {
302              if (!((it->level == i) && (it->base.view))) continue;
303              if ((it->base.view) && (it->level != wd->level))
304                {
305                   if (it->selected)
306                     {
307                        it_closest = it;
308                        break;
309                     }
310                   continue;
311                }
312              if (it->selected)
313                {
314                   it_last = it;
315                   it->selected = 0;
316                }
317              evas_object_geometry_get(it->base.view, &x, &y, &w, &h);
318              xx = x + (w / 2);
319              yy = y + (h / 2);
320              x = evx - xx;
321              y = evy - yy;
322              x = (x * x) + (y * y);
323              if ((x < dist) || (!it_closest))
324                {
325                   if (wd->horizontal)
326                     cdv = (double)(xx - bx) / (double)bw; 
327                   else
328                     cdv = (double)(yy - by) / (double)bh;
329                   it_closest = it;
330                   dist = x;
331                }
332           }
333         if ((!i) && (!wd->level))
334           edje_object_part_drag_value_set(wd->base, "elm.dragable.index.1", 
335                                           cdv, cdv);
336         if (it_closest) it_closest->selected = 1;
337         if (it_closest != it_last)
338           {
339              if (it_last)
340                {
341                   const char *stacking, *selectraise;
342
343                   it = it_last;
344                   edje_object_signal_emit(it->base.view, "elm,state,inactive", "elm");
345                   stacking = edje_object_data_get(it->base.view, "stacking");
346                   selectraise = edje_object_data_get(it->base.view, "selectraise");
347                   if ((selectraise) && (!strcmp(selectraise, "on")))
348                     {
349                        if ((stacking) && (!strcmp(stacking, "below")))
350                          evas_object_lower(it->base.view);
351                     }
352                }
353              if (it_closest)
354                {
355                   const char *selectraise;
356
357                   it = it_closest;
358                   edje_object_signal_emit(it->base.view, "elm,state,active", "elm");
359                   selectraise = edje_object_data_get(it->base.view, "selectraise");
360                   if ((selectraise) && (!strcmp(selectraise, "on")))
361                     evas_object_raise(it->base.view);
362                   evas_object_smart_callback_call((void *)obj, "changed", (void *)it->base.data);
363                   if (wd->delay) ecore_timer_del(wd->delay);
364                   wd->delay = ecore_timer_add(0.2, _delay_change, obj);
365                }
366           }
367         if (it_closest)
368           {
369              it = it_closest;
370              if (!last)
371                last = strdup(it->letter);
372              else
373                {
374                   if (!label) label = strdup(last);
375                   else
376                     {
377                        /* FIXME: realloc return NULL if the request fails */
378                        label = realloc(label, strlen(label) + strlen(last) + 1);
379                        strcat(label, last);
380                     }
381                   free(last);
382                   last = strdup(it->letter);
383                }
384           }
385      }
386    if (!label) label = strdup("");
387    if (!last) last = strdup("");
388    edje_object_part_text_set(wd->base, "elm.text.body", label);
389    edje_object_part_text_set(wd->base, "elm.text", last);
390    free(label);
391    free(last);
392 }
393
394 static void 
395 _wheel(void *data, Evas *e __UNUSED__, Evas_Object *o __UNUSED__, void *event_info __UNUSED__)
396 {
397    Widget_Data *wd = elm_widget_data_get(data);
398 //   Evas_Event_Mouse_Wheel *ev = event_info;
399 //   Evas_Object *obj = o;
400    if (!wd) return;
401 }
402
403 static void 
404 _mouse_down(void *data, Evas *e __UNUSED__, Evas_Object *o __UNUSED__, void *event_info)
405 {
406    Widget_Data *wd = elm_widget_data_get(data);
407    Evas_Event_Mouse_Down *ev = event_info;
408    Evas_Coord x, y;
409    if (!wd) return;
410    if (ev->button != 1) return;
411    wd->down = 1;
412    evas_object_geometry_get(wd->base, &x, &y, NULL, NULL);
413    wd->dx = ev->canvas.x - x;
414    wd->dy = ev->canvas.y - y;
415    elm_index_active_set(data, 1);
416    _sel_eval(data, ev->canvas.x, ev->canvas.y);
417    edje_object_part_drag_value_set(wd->base, "elm.dragable.pointer", 
418                                    wd->dx, wd->dy);
419 }
420
421 static void 
422 _mouse_up(void *data, Evas *e __UNUSED__, Evas_Object *o __UNUSED__, void *event_info)
423 {
424    Widget_Data *wd = elm_widget_data_get(data);
425    Evas_Event_Mouse_Up *ev = event_info;
426    void *d;
427    if (!wd) return;
428    if (ev->button != 1) return;
429    wd->down = 0;
430    d = (void *)elm_index_item_selected_get(data, wd->level);
431    if (d) evas_object_smart_callback_call(data, "selected", d);
432    elm_index_active_set(data, 0);
433    edje_object_signal_emit(wd->base, "elm,state,level,0", "elm");
434 }
435
436 static void 
437 _mouse_move(void *data, Evas *e __UNUSED__, Evas_Object *o __UNUSED__, void *event_info)
438 {
439    Widget_Data *wd = elm_widget_data_get(data);
440    Evas_Event_Mouse_Move *ev = event_info;
441    Evas_Coord minw = 0, minh = 0, x, y, dx, adx;
442    char buf[1024];
443    if (!wd) return;
444    if (!wd->down) return;
445    elm_coords_finger_size_adjust(1, &minw, 1, &minh);
446    evas_object_geometry_get(wd->base, &x, &y, NULL, NULL);
447    x = ev->cur.canvas.x - x;
448    y = ev->cur.canvas.y - y;
449    dx = x - wd->dx;
450    adx = dx;
451    if (adx < 0) adx = -dx;
452    edje_object_part_drag_value_set(wd->base, "elm.dragable.pointer", x, y);
453    if (!wd->horizontal)
454      {
455         if (adx > minw)
456           {
457              if (!wd->level)
458                { 
459                   wd->level = 1;
460                   snprintf(buf, sizeof(buf), "elm,state,level,%i", wd->level);
461                   edje_object_signal_emit(wd->base, buf, "elm");
462                   evas_object_smart_callback_call(data, "level,up", NULL);
463                }
464           }
465         else
466           {
467              if (wd->level == 1)
468                {
469                   wd->level = 0;
470                   snprintf(buf, sizeof(buf), "elm,state,level,%i", wd->level);
471                   edje_object_signal_emit(wd->base, buf, "elm");
472                   evas_object_smart_callback_call(data, "level,down", NULL);
473                }
474           }
475      }
476    _sel_eval(data, ev->cur.canvas.x, ev->cur.canvas.y);
477 }
478
479 /**
480  * Add a new index to the parent
481  *
482  * @param parent The parent object
483  * @return The new object or NULL if it cannot be created
484  *
485  * @ingroup Index
486  */
487 EAPI Evas_Object *
488 elm_index_add(Evas_Object *parent)
489 {
490    Evas_Object *obj;
491    Evas_Object *o;
492    Evas *e;
493    Widget_Data *wd;
494    Evas_Coord minw, minh;
495
496    EINA_SAFETY_ON_NULL_RETURN_VAL(parent, NULL);
497
498    wd = ELM_NEW(Widget_Data);
499    e = evas_object_evas_get(parent);
500    if (!e) return NULL;
501    obj = elm_widget_add(e);
502    ELM_SET_WIDTYPE(widtype, "index");
503    elm_widget_type_set(obj, "index");
504    elm_widget_sub_object_add(parent, obj);
505    elm_widget_data_set(obj, wd);
506    elm_widget_del_hook_set(obj, _del_hook);
507    elm_widget_del_pre_hook_set(obj, _del_pre_hook);
508    elm_widget_theme_hook_set(obj, _theme_hook);
509    elm_widget_signal_emit_hook_set(obj, _signal_emit_hook);
510    elm_widget_signal_callback_add_hook_set(obj, _signal_callback_add_hook);
511    elm_widget_signal_callback_del_hook_set(obj, _signal_callback_del_hook);
512    elm_widget_can_focus_set(obj, EINA_FALSE);
513
514    wd->horizontal = EINA_FALSE;
515
516    wd->base = edje_object_add(e);
517    _elm_theme_object_set(obj, wd->base, "index", "base/vertical", "default");
518    elm_widget_resize_object_set(obj, wd->base);
519
520    o = evas_object_rectangle_add(e);
521    wd->event[0] = o;
522    evas_object_color_set(o, 0, 0, 0, 0);
523    minw = minh = 0;
524    elm_coords_finger_size_adjust(1, &minw, 1, &minh);
525    evas_object_size_hint_min_set(o, minw, minh);
526    edje_object_part_swallow(wd->base, "elm.swallow.event.0", o);
527    elm_widget_sub_object_add(obj, o);
528    evas_object_event_callback_add(o, EVAS_CALLBACK_MOUSE_WHEEL, _wheel, obj);
529    evas_object_event_callback_add(o, EVAS_CALLBACK_MOUSE_DOWN, _mouse_down, obj);
530    evas_object_event_callback_add(o, EVAS_CALLBACK_MOUSE_UP, _mouse_up, obj);
531    evas_object_event_callback_add(o, EVAS_CALLBACK_MOUSE_MOVE, _mouse_move, obj);
532    evas_object_show(o);
533    if (edje_object_part_exists(wd->base, "elm.swallow.event.1"))
534      {
535         o = evas_object_rectangle_add(e);
536         wd->event[1] = o;
537         evas_object_color_set(o, 0, 0, 0, 0);
538         evas_object_size_hint_min_set(o, minw, minh);
539         edje_object_part_swallow(wd->base, "elm.swallow.event.1", o);
540         elm_widget_sub_object_add(obj, o);
541      }
542
543    wd->bx[0] = evas_object_box_add(e);
544    evas_object_box_layout_set(wd->bx[0], _layout, wd, NULL);
545    elm_widget_sub_object_add(obj, wd->bx[0]);
546    edje_object_part_swallow(wd->base, "elm.swallow.index.0", wd->bx[0]);
547    evas_object_show(wd->bx[0]);
548
549    if (edje_object_part_exists(wd->base, "elm.swallow.index.1"))
550      {
551         wd->bx[1] = evas_object_box_add(e);
552         evas_object_box_layout_set(wd->bx[1], _layout, wd, NULL);
553         elm_widget_sub_object_add(obj, wd->bx[1]);
554         edje_object_part_swallow(wd->base, "elm.swallow.index.1", wd->bx[1]);
555         evas_object_show(wd->bx[1]);
556      }
557
558    _sizing_eval(obj);
559    return obj;
560 }
561
562 /**
563  * Set the active state of the index programatically
564  *
565  * @param obj The index object
566  * @param active The active starte
567  *
568  * @ingroup Index
569  */
570 EAPI void
571 elm_index_active_set(Evas_Object *obj, Eina_Bool active)
572 {
573    ELM_CHECK_WIDTYPE(obj, widtype);
574    Widget_Data *wd = elm_widget_data_get(obj);
575    if (!wd) return;
576    if (wd->active == active) return;
577    wd->active = active;
578    wd->level = 0;
579    if (wd->active)
580      {
581         _index_box_clear(obj, wd->bx[1], 1);
582         _index_box_auto_fill(obj, wd->bx[0], 0);
583         edje_object_signal_emit(wd->base, "elm,state,active", "elm");
584      }
585    else
586      edje_object_signal_emit(wd->base, "elm,state,inactive", "elm");
587 }
588
589 /**
590  * Sets the level of the item.
591  *
592  * @param obj The index object.
593  * @param level To be documented.
594  *
595  * @ingroup Index
596  */
597 EAPI void
598 elm_index_item_level_set(Evas_Object *obj, int level)
599 {
600    ELM_CHECK_WIDTYPE(obj, widtype);
601    Widget_Data *wd = elm_widget_data_get(obj);
602    if (!wd) return;
603    if (wd->level == level) return;
604    wd->level = level;
605 }
606
607 /**
608  * Gets the level of the item.
609  *
610  * @param obj The index object
611  *
612  * @ingroup Index
613  */
614 EAPI int
615 elm_index_item_level_get(const Evas_Object *obj)
616 {
617    ELM_CHECK_WIDTYPE(obj, widtype) 0;
618    Widget_Data *wd = elm_widget_data_get(obj);
619    if (!wd) return 0;
620    return wd->level;
621 }
622
623 /**
624  * Returns the selected item.
625  *
626  * @param obj The index object.
627  * @param level to be documented.
628  *
629  * @ingroup Index
630  */
631 EAPI void *
632 elm_index_item_selected_get(const Evas_Object *obj, int level)
633 {
634    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
635    Widget_Data *wd = elm_widget_data_get(obj);
636    Eina_List *l;
637    Elm_Index_Item *it;
638    if (!wd) return NULL;
639    EINA_LIST_FOREACH(wd->items, l, it)
640      if ((it->selected) && (it->level == level))
641        return elm_widget_item_data_get(it);
642    return NULL;
643 }
644
645 /**
646  * Appends a new item.
647  *
648  * @param obj The index object.
649  * @param letter Letter under which the item should be indexed
650  * @param item The item to put in the index
651  *
652  * @ingroup Index
653  */
654 EAPI void
655 elm_index_item_append(Evas_Object *obj, const char *letter, const void *item)
656 {
657    ELM_CHECK_WIDTYPE(obj, widtype);
658    Widget_Data *wd = elm_widget_data_get(obj);
659    Elm_Index_Item *it;
660    if (!wd) return;
661    it = _item_new(obj, letter, item);
662    if (!it) return;
663    wd->items = eina_list_append(wd->items, it);
664    _index_box_clear(obj, wd->bx[wd->level], wd->level);
665 }
666
667 /**
668  * Prepends a new item.
669  *
670  * @param obj The index object.
671  * @param letter Letter under which the item should be indexed
672  * @param item The item to put in the index
673  *
674  * @ingroup Index
675  */
676 EAPI void
677 elm_index_item_prepend(Evas_Object *obj, const char *letter, const void *item)
678 {
679    ELM_CHECK_WIDTYPE(obj, widtype);
680    Widget_Data *wd = elm_widget_data_get(obj);
681    Elm_Index_Item *it;
682
683    if (!wd) return;
684    it = _item_new(obj, letter, item);
685    if (!it) return;
686    wd->items = eina_list_prepend(wd->items, it);
687    _index_box_clear(obj, wd->bx[wd->level], wd->level);
688 }
689
690 /**
691  * Append an item after @p relative in letter @p letter.
692  *
693  * @param obj The index object
694  * @param letter Letter under which the item should be indexed
695  * @param item The item to put in the index
696  * @param relative The item to put @p item after
697  *
698  * @ingroup Index
699  */
700 EAPI void
701 elm_index_item_append_relative(Evas_Object *obj, const char *letter, const void *item, const void *relative)
702 {
703    ELM_CHECK_WIDTYPE(obj, widtype);
704    Widget_Data *wd = elm_widget_data_get(obj);
705    Elm_Index_Item *it, *it_rel;
706    if (!wd) return;
707    if (!relative)
708      {
709         elm_index_item_append(obj, letter, item);
710         return;
711      }
712    it = _item_new(obj, letter, item);
713    it_rel = _item_find(obj, relative);
714    if (!it_rel)
715      {
716         elm_index_item_append(obj, letter, item);
717         return;
718      }
719    if (!it) return;
720    wd->items = eina_list_append_relative(wd->items, it, it_rel);
721    _index_box_clear(obj, wd->bx[wd->level], wd->level);
722 }
723
724 /**
725  * Prepend an item before @p relative in letter @p letter.
726  *
727  * @param obj The index object
728  * @param letter Letter under which the item should be indexed
729  * @param item The item to put in the index
730  * @param relative The item to put @p item before
731  *
732  * @ingroup Index
733  */
734 EAPI void
735 elm_index_item_prepend_relative(Evas_Object *obj, const char *letter, const void *item, const void *relative)
736 {
737    ELM_CHECK_WIDTYPE(obj, widtype);
738    Widget_Data *wd = elm_widget_data_get(obj);
739    Elm_Index_Item *it, *it_rel;
740    if (!wd) return;
741    if (!relative)
742      {
743         elm_index_item_prepend(obj, letter, item);
744         return;
745      }
746    it = _item_new(obj, letter, item);
747    it_rel = _item_find(obj, relative);
748    if (!it_rel)
749      {
750         elm_index_item_append(obj, letter, item);
751         return;
752      }
753    if (!it) return;
754    wd->items = eina_list_prepend_relative(wd->items, it, it_rel);
755    _index_box_clear(obj, wd->bx[wd->level], wd->level);
756 }
757
758 /**
759  * Insert a new @p item into the sorted index @p obj in @p letter.
760  *
761  * @param obj The index object
762  * @param letter Letter under which the item should be indexed
763  * @param item The item to put in the index
764  * @param cmp_func The function called for the sort of index items.
765  * @param cmp_data_func The function called for the sort of the data. It will
766  * be used when cmp_func return 0. It means the index item already exists.
767  * So, to decide which data item should be pointed by the index item, a function
768  * to compare them is needed. If this function is not provided, index items
769  * will be duplicated. If cmp_data_func returns a non-negative value, the
770  * previous index item data will be replaced by the inserted @p item. So
771  * if the previous data need to be free, it should be done in this function,
772  * because the reference will be lost.
773  *
774  * @ingroup Index
775  */
776 EAPI void
777 elm_index_item_sorted_insert(Evas_Object *obj, const char *letter, const void *item, Eina_Compare_Cb cmp_func, Eina_Compare_Cb cmp_data_func)
778 {
779    ELM_CHECK_WIDTYPE(obj, widtype);
780    Widget_Data *wd = elm_widget_data_get(obj);
781    Eina_List *lnear;
782    Elm_Index_Item *it;
783    int cmp;
784
785    if (!wd) return;
786    if (!(wd->items))
787      {
788         elm_index_item_append(obj, letter, item);
789         return;
790      }
791
792    it = _item_new(obj, letter, item);
793    if (!it) return;
794
795    lnear = eina_list_search_sorted_near_list(wd->items, cmp_func, it, &cmp);
796    if (cmp < 0)
797      wd->items =  eina_list_append_relative_list(wd->items, it, lnear);
798    else if (cmp > 0)
799      wd->items = eina_list_prepend_relative_list(wd->items, it, lnear);
800    else
801      {
802         /* If cmp_data_func is not provided, append a duplicated item */
803         if (!cmp_data_func)
804           wd->items =  eina_list_append_relative_list(wd->items, it, lnear);
805         else
806           {
807              Elm_Index_Item *p_it = eina_list_data_get(lnear);
808              if (cmp_data_func(p_it->base.data, it->base.data) >= 0)
809                p_it->base.data = it->base.data;
810              _item_free(it);
811           }
812      }
813
814    _index_box_clear(obj, wd->bx[wd->level], wd->level);
815 }
816
817 /**
818  * Remove an item from the index.
819  *
820  * @param obj The index object
821  * @param item The item to remove from the index
822  *
823  * @ingroup Index
824  */
825 EAPI void
826 elm_index_item_del(Evas_Object *obj, const void *item)
827 {
828    ELM_CHECK_WIDTYPE(obj, widtype);
829    Widget_Data *wd = elm_widget_data_get(obj);
830    Elm_Index_Item *it;
831    if (!wd) return;
832    it = _item_find(obj, item);
833    if (!it) return;
834    _item_free(it);
835    _index_box_clear(obj, wd->bx[wd->level], wd->level);
836 }
837
838 /**
839  * Find an index item using item data.
840  *
841  * @param obj The index object
842  * @param item The item pointed by index item
843  * @return The index item pointing to @p item
844  *
845  * @ingroup Index
846  */
847 EAPI Elm_Index_Item *
848 elm_index_item_find(Evas_Object *obj, const void *item)
849 {
850    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
851    Widget_Data *wd = elm_widget_data_get(obj);
852    if (!wd) return NULL;
853    return _item_find(obj, item);
854 }
855
856 /**
857  * Clears an index of its items.
858  *
859  * @param obj The index object.
860  *
861  * @ingroup Index
862  */
863 EAPI void
864 elm_index_item_clear(Evas_Object *obj)
865 {
866    ELM_CHECK_WIDTYPE(obj, widtype);
867    Widget_Data *wd = elm_widget_data_get(obj);
868    Elm_Index_Item *it;
869    Eina_List *l, *clear = NULL;
870    if (!wd) return;
871    _index_box_clear(obj, wd->bx[wd->level], wd->level);
872    EINA_LIST_FOREACH(wd->items, l, it)
873      {
874         if (it->level != wd->level) continue;
875         clear = eina_list_append(clear, it);
876      }
877    EINA_LIST_FREE(clear, it) _item_free(it);
878 }
879
880 /**
881  * Go to item at @p level
882  *
883  * @param obj The index object
884  * @param level The index level
885  *
886  * @ingroup Index
887  */
888 EAPI void
889 elm_index_item_go(Evas_Object *obj, int level __UNUSED__)
890 {
891    ELM_CHECK_WIDTYPE(obj, widtype);
892    Widget_Data *wd = elm_widget_data_get(obj);
893    if (!wd) return;
894    _index_box_auto_fill(obj, wd->bx[0], 0);
895    if (wd->level == 1) _index_box_auto_fill(obj, wd->bx[1], 1);
896 }
897
898 /**
899  * Returns the data associated with the item.
900  *
901  * @param it The list item
902  * @return The data associated with @p it
903  *
904  * @ingroup Index
905  */
906 EAPI void *
907 elm_index_item_data_get(const Elm_Index_Item *it)
908 {
909    ELM_WIDGET_ITEM_WIDTYPE_CHECK_OR_RETURN(it, NULL);
910    return elm_widget_item_data_get(it);
911 }
912
913 /**
914  * Set the data item from the index item
915  *
916  * This set a new data value.
917  *
918  * @param it The item
919  * @param data The new data pointer to set
920  *
921  * @ingroup Index
922  */
923 EAPI void
924 elm_index_item_data_set(Elm_Index_Item *it, const void *data)
925 {
926    ELM_WIDGET_ITEM_WIDTYPE_CHECK_OR_RETURN(it);
927    elm_widget_item_data_set(it, data);
928 }
929
930 /**
931  * Set the function called when a index item is freed.
932  *
933  * @param it The item to set the callback on
934  * @param func The function called
935  *
936  * @ingroup Index
937  */
938 EAPI void
939 elm_index_item_del_cb_set(Elm_Index_Item *it, Evas_Smart_Cb func)
940 {
941    ELM_WIDGET_ITEM_WIDTYPE_CHECK_OR_RETURN(it);
942    elm_widget_item_del_cb_set(it, func);
943 }
944
945 /**
946  * Gets the letter of the item.
947  *
948  * @param it The list item
949  * @return The letter of @p it
950  *
951  * @ingroup Index
952  */
953 EAPI const char *
954 elm_index_item_letter_get(const Elm_Index_Item *it)
955 {
956    ELM_WIDGET_ITEM_WIDTYPE_CHECK_OR_RETURN(it, NULL);
957    return it->letter;
958 }
959