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