Merge "[Password]: New design based changes, a new style removed password mode contro...
[framework/uifw/elementary.git] / src / lib / elm_index.c
1 /*
2  * vim:ts=8:sw=3:sts=8:noexpandtab:cino=>5n-3f0^-2{2
3  */
4 #include <Elementary.h>
5 #include "elm_priv.h"
6
7 /**
8  * @defgroup Index Index
9  * @ingroup Elementary
10  *
11  * An index object is a type of list that categorizes items in it
12  * by letter.
13  */
14
15 #define MIN_GRP_SIZE 2 //for symmetry it is 2, otherwise it can be 1 and zero have no meaning.
16 #define MIN_PIXEL_VALUE 1 //Min pixel value is highly dependent on touch sensitivity support.
17 #define MIN_OBJ_HEIGHT 24 //should be taken from .edc file.
18 /*
19  *  use for find view toplevel
20  */
21 #define SET_VIEW_LEVEL(wd, view_level)\
22    view_level = wd->level;\
23    while ((!wd->tot_items_count[view_level]) && view_level)\
24      {\
25         view_level--; \
26      }
27
28 typedef struct _Widget_Data Widget_Data;
29
30 typedef struct _PlacementPart PlacementPart;
31
32 struct _Widget_Data
33 {
34    Evas_Object *base;
35    Evas_Object *event[2];
36    Evas_Object *bx[2]; // 2 - for now all that's supported
37    Eina_List *items; // 1 list. yes N levels, but only 2 for now and # of items will be small
38    char *popup_str[2];
39    int level;
40    int max_supp_items_count;
41    int tot_items_count[2];
42    int min_obj_height, max_grp_size;
43    int min_1st_level_obj_height;
44    int items_count;
45    Evas_Coord dx, dy;
46    Evas_Coord pwidth, pheight;
47    Ecore_Timer *delay;
48    const char *special_char;
49    Eina_Bool level_active[2];
50    Eina_Bool horizontal : 1;
51    Eina_Bool active : 1;
52    Eina_Bool down : 1;
53    Eina_Bool hide_button : 1;
54    double scale_factor;
55 };
56
57 struct _Elm_Index_Item
58 {
59    Evas_Object *obj;
60    Evas_Object *base;
61    const char *letter, *vis_letter;
62    const void *data;
63    int level, size;
64    Eina_Bool selected : 1;
65 };
66
67 struct _PlacementPart
68 {
69    int start;
70    int count;
71 };
72
73 static const char *widtype = NULL;
74
75 static void _del_hook(Evas_Object *obj);
76 static void _theme_hook(Evas_Object *obj);
77 static void _sizing_eval(Evas_Object *obj);
78 static void _index_box_auto_fill(Evas_Object *obj, Evas_Object *box, int level);
79 static void _index_box_clear(Evas_Object *obj, Evas_Object *box, int level);
80 static void _item_free(Elm_Index_Item *it);
81 static void _index_process(Evas_Object *obj);
82
83 /* Free a block allocated by `malloc', `realloc' or `calloc' one by one*/
84 static void
85 _del_hook(Evas_Object *obj)
86 {
87    Widget_Data *wd = elm_widget_data_get(obj);
88    Elm_Index_Item *it;
89    Eina_List *l, *clear = NULL;
90    if (!wd) return;
91    _index_box_clear(obj, wd->bx[wd->level], wd->level);
92    _index_box_clear(obj, wd->bx[0], 0);
93    EINA_LIST_FOREACH(wd->items, l, it) clear = eina_list_append(clear, it);
94    EINA_LIST_FREE(clear, it) _item_free(it);
95    if(wd->popup_str[0]) free(wd->popup_str[0]);
96    if(wd->popup_str[1]) free(wd->popup_str[1]);
97    if (wd->delay) ecore_timer_del(wd->delay);
98    free(wd);
99 }
100
101 static void
102 _layout(Evas_Object *o, Evas_Object_Box_Data *priv, void *data)
103 {
104    Widget_Data *wd = data;
105    if (!wd) return;
106    _els_box_layout(o, priv, wd->horizontal, 0, 0); /* making box layout non homogenous */
107 }
108
109 static void
110 _theme_hook(Evas_Object *obj)
111 {
112    Widget_Data *wd = elm_widget_data_get(obj);
113    if (!wd) return;
114    _index_box_clear(obj, wd->bx[0], 0);
115    _index_box_clear(obj, wd->bx[1], 1);
116    if (wd->horizontal)
117      _elm_theme_object_set(obj, wd->base, "index", "base/horizontal", elm_widget_style_get(obj));
118    else
119      _elm_theme_object_set(obj, wd->base, "index", "base/vertical", elm_widget_style_get(obj));
120    edje_object_part_swallow(wd->base, "elm.swallow.event.0", wd->event[0]);
121    edje_object_part_swallow(wd->base, "elm.swallow.index.0", wd->bx[0]);
122    if (edje_object_part_exists(wd->base, "elm.swallow.index.1"))
123      {
124         if (!wd->bx[1])
125           {
126              wd->bx[1] = evas_object_box_add(evas_object_evas_get(wd->base));
127              evas_object_box_layout_set(wd->bx[1], _layout, wd, NULL);
128              elm_widget_sub_object_add(obj, wd->bx[1]);
129           }
130         edje_object_part_swallow(wd->base, "elm.swallow.index.1", wd->bx[1]);
131         evas_object_show(wd->bx[1]);
132      }
133    else if (wd->bx[1])
134      {
135         evas_object_del(wd->bx[1]);
136         wd->bx[1] = NULL;
137      }
138    if (edje_object_part_exists(wd->base, "elm.swallow.event.1"))
139      {
140         if (!wd->event[1])
141           {
142              Evas_Coord minw = 0, minh = 0;
143
144              wd->event[1] = evas_object_rectangle_add(evas_object_evas_get(wd->base));
145              evas_object_color_set(wd->event[1], 0, 0, 0, 0);
146              evas_object_size_hint_min_set(wd->event[1], minw, minh);
147              minw = minh = 0;
148              elm_coords_finger_size_adjust(1, &minw, 1, &minh);
149              elm_widget_sub_object_add(obj, wd->event[1]);
150           }
151         edje_object_part_swallow(wd->base, "elm.swallow.event.1", wd->event[1]);
152      }
153    else if (wd->event[1])
154      {
155         evas_object_del(wd->event[1]);
156         wd->event[1] = NULL;
157      }
158    edje_object_message_signal_process(wd->base);
159    edje_object_scale_set(wd->base, elm_widget_scale_get(obj) * _elm_config->scale);
160    _sizing_eval(obj);
161    _index_box_auto_fill(obj, wd->bx[0], 0);
162    if (wd->active)
163      if (wd->level == 1)
164        _index_box_auto_fill(obj, wd->bx[1], 1);
165 }
166
167 static void
168 _sizing_eval(Evas_Object *obj)
169 {
170    Widget_Data *wd = elm_widget_data_get(obj);
171    Evas_Coord minw = -1, minh = -1, maxw = -1, maxh = -1;
172    if (!wd) return;
173    elm_coords_finger_size_adjust(1, &minw, 1, &minh);
174    edje_object_size_min_restricted_calc(wd->base, &minw, &minh, minw, minh);
175    elm_coords_finger_size_adjust(1, &minw, 1, &minh);
176    evas_object_size_hint_min_set(obj, minw, minh);
177    evas_object_size_hint_max_set(obj, maxw, maxh);
178 }
179
180 static Elm_Index_Item *
181 _item_new(Evas_Object *obj, const char *letter, const void *item)
182 {
183    Widget_Data *wd = elm_widget_data_get(obj);
184    Elm_Index_Item *it;
185    if (!wd) return NULL;
186    it = calloc(1, sizeof(Elm_Index_Item));
187    if (!it) return NULL;
188    it->obj = obj;
189    it->data = item;
190    it->level = wd->level;
191    if(wd->level == 0)
192      it->size =  wd->min_obj_height;
193    else
194      it->size =  wd->min_1st_level_obj_height;
195    if(letter)
196      {
197         it->letter = eina_stringshare_add(letter);
198         it->vis_letter = eina_stringshare_add(letter);
199      }
200    else
201      return NULL;
202    return it;
203 }
204
205 static Elm_Index_Item *
206 _item_find(Evas_Object *obj, const void *item)
207 {
208    Widget_Data *wd = elm_widget_data_get(obj);
209    Eina_List *l;
210    Elm_Index_Item *it;
211    if (!wd) return NULL;
212    EINA_LIST_FOREACH(wd->items, l, it)
213      if (it->data == item) return it;
214    return NULL;
215 }
216
217 static void
218 _item_free(Elm_Index_Item *it)
219 {
220    Widget_Data *wd = elm_widget_data_get(it->obj);
221    if (!wd) return;
222    wd->items = eina_list_remove(wd->items, it);
223    if (it->base) evas_object_del(it->base);
224    eina_stringshare_del(it->letter);
225    eina_stringshare_del(it->vis_letter);
226    free(it);
227 }
228
229 /* Automatically filling the box with index item*/
230 static void
231 _index_box_auto_fill(Evas_Object *obj, Evas_Object *box, int level)
232 {
233    Widget_Data *wd = elm_widget_data_get(obj);
234    Eina_List *l;
235    Elm_Index_Item *it;
236    Evas_Coord mw, mh, w, h;
237    int i = 0;
238    if (!wd) return;
239    if (wd->level_active[level]) return;
240    evas_object_geometry_get(box, NULL, NULL, &w, &h);
241    EINA_LIST_FOREACH(wd->items, l, it)
242      {
243         Evas_Object *o;
244         const char *stacking;
245
246         if (it->level != level) continue;
247         if(i > wd->max_supp_items_count) break;
248
249         o = edje_object_add(evas_object_evas_get(obj));
250         it->base = o;
251         if (i & 0x1)
252           _elm_theme_object_set(obj, o, "index", "item_odd/vertical", elm_widget_style_get(obj));
253         else
254           //_elm_theme_object_set(obj, o, "index", "item/vertical", "default");
255           _elm_theme_object_set(obj, o, "index", "item/vertical", elm_widget_style_get(obj));
256         edje_object_part_text_set(o, "elm.text", it->letter);
257         edje_object_size_min_restricted_calc(o, &mw, &mh, 0, 0);
258         evas_object_size_hint_weight_set(o, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
259         evas_object_size_hint_align_set(o, EVAS_HINT_FILL, EVAS_HINT_FILL);
260         edje_object_part_text_set(o, "elm.text", it->vis_letter);
261         evas_object_resize(o, mw, it->size);
262         evas_object_size_hint_min_set(o, mw, it->size);
263         evas_object_size_hint_max_set(o, mw, it->size);
264         elm_widget_sub_object_add(obj, o);
265         evas_object_box_append(box, o);
266         stacking = edje_object_data_get(o, "stacking");
267         if (stacking)
268           {
269              if (!strcmp(stacking, "below")) evas_object_lower(o);
270              else if (!strcmp(stacking, "above")) evas_object_raise(o);
271           }
272         evas_object_show(o);
273         i++;
274         if(level == 1)
275           wd->tot_items_count[1] = i;
276         evas_object_smart_calculate(box); // force a calc so we know the size
277         evas_object_size_hint_min_get(box, &mw, &mh);
278         if (mh > h)
279           {
280              _index_box_clear(obj, box, level);
281              if (i > 0)
282                {
283                   // FIXME: only i objects fit! try again. overflows right now
284                }
285           }
286      }
287    evas_object_smart_calculate(box);
288    wd->level_active[level] = 1;
289 }
290
291 static void
292 _index_box_clear(Evas_Object *obj, Evas_Object *box __UNUSED__, int level)
293 {
294    Widget_Data *wd = elm_widget_data_get(obj);
295    Eina_List *l;
296    Elm_Index_Item *it;
297    if (!wd) return;
298    if (!wd->level_active[level]) return;
299    EINA_LIST_FOREACH(wd->items, l, it)
300      {
301         if (!it->base) continue;
302         if (it->level != level) continue;
303         evas_object_del(it->base);
304         it->base = 0;
305      }
306    wd->level_active[level] = 0;
307 }
308
309 static Eina_Bool
310 _delay_change(void *data)
311 {
312    Widget_Data *wd = elm_widget_data_get(data);
313    void *d;
314    int view_level;
315    if (!wd) return ECORE_CALLBACK_CANCEL;
316    wd->delay = NULL;
317    SET_VIEW_LEVEL(wd, view_level);
318    d = (void *)elm_index_item_selected_get(data, view_level);
319    if (d) evas_object_smart_callback_call(data, "delay,changed", d);
320    return ECORE_CALLBACK_CANCEL;
321 }
322
323 static void
324 _sel_eval(Evas_Object *obj, Evas_Coord evx, Evas_Coord evy)
325 {
326    Widget_Data *wd = elm_widget_data_get(obj);
327    Elm_Index_Item *it, *it_closest, *it_last;
328    Eina_List *l;
329    Evas_Coord x, y, w, h, bx, by, bw, bh, xx, yy;
330    double cdv = 0.5;
331    double cdvv = 0.0;
332    double dmax = 0.0;
333    double dmin = 0.0;
334    Evas_Coord dist;
335    Eina_Bool change = EINA_FALSE;
336    char *label = NULL, *last = NULL;
337    int i;
338    int view_level;
339    if (!wd) return;
340
341    SET_VIEW_LEVEL(wd, view_level);
342    for (i = 0; i <= view_level; i++)
343      {
344         it_last = NULL;
345         it_closest  = NULL;
346         dist = 0x7fffffff;
347         evas_object_geometry_get(wd->bx[i], &bx, &by, &bw, &bh);
348         dmin = (double)(wd->min_1st_level_obj_height*wd->tot_items_count[1])/(2*(double)bh);
349         dmax = 1.0-dmin-0.08;
350         EINA_LIST_FOREACH(wd->items, l, it)
351           {
352              if (!((it->level == i) && (it->base))) continue;
353              if (it->selected)
354                {
355                   it_last = it;
356                   it->selected = 0;
357                }
358              evas_object_geometry_get(it->base, &x, &y, &w, &h);
359              xx = x + (w / 2);
360              yy = y + (h / 2);
361              x = evx - xx;
362              y = evy - yy;
363              x = (x * x) + (y * y);
364              if ((x < dist) || (!it_closest))
365                {
366                   if (wd->horizontal)
367                     cdv = (double)(xx - bx) / (double)bw;
368                   else
369                     cdv = (double)(yy - by) / (double)bh;
370                   it_closest = it;
371                   dist = x;
372                }
373           }
374           if ((i == 0) && (view_level == 0))
375             {
376                if(cdv > dmax || cdv < dmin)
377                  {
378                     if(cdv > dmax)
379                       {
380                          cdvv = dmax;
381                       }
382                     else
383                       {
384                          cdvv = dmin;
385                       }
386                     edje_object_part_drag_value_set(wd->base, "elm.dragable.index.1", cdv, cdvv);
387                  }
388                else
389                  {
390                     edje_object_part_drag_value_set(wd->base, "elm.dragable.index.1", cdv, cdv);
391                  }
392             }
393         if (it_closest) it_closest->selected = 1;
394         if (it_closest != it_last)
395           {
396              change = 1;
397              if (it_last)
398                {
399                   const char *stacking, *selectraise;
400
401                   it = it_last;
402                   if(view_level == it->level)
403                   edje_object_signal_emit(it->base, "elm,state,inactive", "elm");
404                   stacking = edje_object_data_get(it->base, "stacking");
405                   selectraise = edje_object_data_get(it->base, "selectraise");
406                   if ((selectraise) && (!strcmp(selectraise, "on")))
407                     {
408                        if ((stacking) && (!strcmp(stacking, "below")))
409                          evas_object_lower(it->base);
410                     }
411                }
412              if (it_closest)
413                {
414                   const char *selectraise;
415
416                   it = it_closest;
417                   if(view_level == it->level)
418                   edje_object_signal_emit(it->base, "elm,state,active", "elm");
419                   selectraise = edje_object_data_get(it->base, "selectraise");
420                   if ((selectraise) && (!strcmp(selectraise, "on")))
421                     evas_object_raise(it->base);
422                   evas_object_smart_callback_call((void *)obj, "changed", (void *)it->data);
423                   if (wd->delay) ecore_timer_del(wd->delay);
424                   wd->delay = ecore_timer_add(0.2, _delay_change, obj);
425                }
426           }
427         if (it_closest)
428           {
429              it = it_closest;
430              if (!last)
431                last = strdup(it->letter);
432              else
433                {
434                   if (!label) label = strdup(last);
435                   else
436                     {
437                        label = realloc(label, strlen(label) + strlen(last) + 1);
438                        strcat(label, last);
439                     }
440                   free(last);
441                   last = strdup(it->letter);
442                }
443           }
444      }
445    if (!label) label = strdup("");
446    if (!last) last = strdup("");
447    if(!wd->hide_button)
448      {
449         char *popup_text;
450
451         if(view_level == 0)
452           {
453              if (wd->popup_str[1]) wd->popup_str[1][0] = '\0';
454              wd->popup_str[0] = (char *)realloc(wd->popup_str[0], (sizeof(char) * strlen(last) + 1));
455
456              strcpy(wd->popup_str[0], last);
457              edje_object_signal_emit(wd->base, "hide_2nd_level", "");
458           }
459         if (view_level == 1 && wd->level_active[1])
460           {
461              wd->popup_str[1] = (char *)realloc(wd->popup_str[1], (sizeof(char) * strlen(last) + 1));
462
463              strcpy(wd->popup_str[1], last);
464              edje_object_signal_emit(wd->base, "hide_first_level", "");
465           }
466         popup_text = (char *)malloc(sizeof(char) * (strlen(wd->popup_str[0]) + strlen(wd->popup_str[1]) + 1));
467         sprintf(popup_text, "%s%s", wd->popup_str[0], wd->popup_str[1]);
468         edje_object_part_text_set(wd->base, "elm.text", popup_text);
469
470         free(popup_text);
471      }
472
473    if(label)
474      free(label);
475    if(last)
476      free(last);
477 }
478
479 static void
480 _wheel(void *data, Evas *e __UNUSED__, Evas_Object *o __UNUSED__, void *event_info __UNUSED__)
481 {
482    Widget_Data *wd = elm_widget_data_get(data);
483    if (!wd) return;
484 }
485
486 static void
487 _mouse_down(void *data, Evas *e __UNUSED__, Evas_Object *o __UNUSED__, void *event_info)
488 {
489    Widget_Data *wd = elm_widget_data_get(data);
490    Evas_Event_Mouse_Down *ev = event_info;
491    Evas_Coord x, y;
492    if (!wd) return;
493    if (ev->button != 1) return;
494    wd->down = 1;
495    evas_object_geometry_get(wd->base, &x, &y, NULL, NULL);
496    wd->dx = ev->canvas.x - x;
497    wd->dy = ev->canvas.y - y;
498    elm_index_active_set(data, 1);
499    _sel_eval(data, ev->canvas.x, ev->canvas.y);
500    edje_object_part_drag_value_set(wd->base, "elm.dragable.pointer", wd->dx, wd->dy);
501 }
502
503 static void
504 _mouse_up(void *data, Evas *e __UNUSED__, Evas_Object *o __UNUSED__, void *event_info)
505 {
506    Widget_Data *wd = elm_widget_data_get(data);
507    Evas_Event_Mouse_Up *ev = event_info;
508    void *d;
509    Elm_Index_Item *it;
510    Eina_List *l;
511    int view_level;
512
513    if (!wd) return;
514    if (ev->button != 1) return;
515    if (wd->level == 1 && wd->delay) ecore_timer_del(wd->delay);
516    wd->delay = NULL;
517    wd->down = 0;
518    SET_VIEW_LEVEL(wd, view_level);
519    d = (void *)elm_index_item_selected_get(data, view_level);
520    EINA_LIST_FOREACH(wd->items, l, it)
521      {
522         edje_object_signal_emit(it->base, "elm,state,inactive", "elm");
523      }
524    if (d) evas_object_smart_callback_call(data, "selected", d);
525    elm_index_active_set(data, 0);
526    edje_object_signal_emit(wd->base, "elm,state,level,0", "elm");
527 }
528
529 static void
530 _mouse_move(void *data, Evas *e __UNUSED__, Evas_Object *o __UNUSED__, void *event_info)
531 {
532    Widget_Data *wd = elm_widget_data_get(data);
533    Evas_Event_Mouse_Move *ev = event_info;
534    Evas_Coord minw = 0, minh = 0, x, y, dx, dy, adx, ady;
535    void *d;
536    char buf[1024];
537    if (!wd) return;
538    if (!wd->down) return;
539    elm_coords_finger_size_adjust(1, &minw, 1, &minh);
540    evas_object_geometry_get(wd->base, &x, &y, NULL, NULL);
541    x = ev->cur.canvas.x - x;
542    y = ev->cur.canvas.y - y;
543    dx = x - wd->dx;
544    adx = dx;
545    if (adx < 0) adx = -dx;
546    dy = y - wd->dy;
547    ady = dy;
548    if (ady < 0) ady = -dy;
549    edje_object_part_drag_value_set(wd->base, "elm.dragable.pointer", x, y);
550    if (wd->horizontal)
551      {
552      }
553    else
554      {
555         if (adx > minw)
556           {
557              if (wd->level == 0)
558                {
559                   wd->level = 1;
560                   snprintf(buf, sizeof(buf), "elm,state,level,%i", wd->level);
561                   edje_object_signal_emit(wd->base, buf, "elm");
562                   evas_object_smart_callback_call(data, "level,up", NULL);
563                }
564           }
565         else
566           {
567              if (wd->level == 1)
568                {
569                   wd->level = 0;
570                   snprintf(buf, sizeof(buf), "elm,state,level,%i", wd->level);
571                   edje_object_signal_emit(wd->base, buf, "elm");
572                   d = (void *)elm_index_item_selected_get(data, wd->level);
573                   evas_object_smart_callback_call(data, "changed", d);
574                   if (wd->delay) ecore_timer_del(wd->delay);
575                   wd->delay = ecore_timer_add(0.2, _delay_change, data);
576                   evas_object_smart_callback_call(data, "level,down", NULL);
577                }
578           }
579      }
580    _sel_eval(data, ev->cur.canvas.x, ev->cur.canvas.y);
581 }
582 static void
583 _index_box_refill_job(void *data)
584 {
585    Widget_Data *wd = elm_widget_data_get((Evas_Object *)data);
586    if (!wd) return;
587
588    const char *string;
589    Evas_Coord pw, ph;
590
591    evas_object_geometry_get(wd->base, NULL, NULL, &pw, &ph);
592    wd->scale_factor = elm_scale_get();
593    if ( wd->scale_factor == 0.0 ) {
594      wd->scale_factor = 1.0;
595    }
596    string = edje_object_data_get(wd->base, "min_obj_height");
597    if(string)
598      wd->min_obj_height = (int) (atoi(string))*wd->scale_factor;
599    else
600      wd->min_obj_height = MIN_OBJ_HEIGHT*wd->scale_factor;
601    if(!wd->min_obj_height) return;
602
603    wd->max_grp_size = wd->min_obj_height - 2*MIN_GRP_SIZE;
604    wd->items_count = ph/wd->min_obj_height;
605    wd->max_supp_items_count = wd->max_grp_size*(int)((wd->items_count-1)*0.5)+wd->items_count;
606
607    if(pw != wd->pwidth && ph != wd->pheight)
608      {
609         if(wd->down == 1)
610           {
611              wd->active = 0;
612              elm_index_active_set(data, 1);
613           }
614         _index_box_clear((Evas_Object *)data, wd->bx[0], 0);
615         evas_object_smart_calculate( wd->bx[0]);
616         elm_index_item_go((Evas_Object *)data, wd->level);
617         wd->pwidth = pw;
618         wd->pheight = ph;
619      }
620 }
621
622 static void _index_object_resize(void *data, Evas *e, Evas_Object *obj, void *event_info)
623 {
624    Widget_Data *wd;
625    if(!data) return;
626    wd = elm_widget_data_get((Evas_Object *)data);
627    if(!wd) return;
628    ecore_job_add(_index_box_refill_job, (Evas_Object *)data);
629 }
630
631 /**
632  * Add a new index to the parent
633  *
634  * @param parent The parent object
635  * @return The new object or NULL if it cannot be created
636  *
637  * @ingroup Index
638  */
639 EAPI Evas_Object *
640 elm_index_add(Evas_Object *parent)
641 {
642    Evas_Object *obj;
643    Evas_Object *o;
644    Evas *e;
645    Widget_Data *wd;
646    Evas_Coord minw, minh;
647    const char *string;
648
649    wd = ELM_NEW(Widget_Data);
650    e = evas_object_evas_get(parent);
651    if(!e) return NULL;
652    obj = elm_widget_add(e);
653    ELM_SET_WIDTYPE(widtype, "index");
654    elm_widget_type_set(obj, "index");
655    elm_widget_sub_object_add(parent, obj);
656    elm_widget_data_set(obj, wd);
657    elm_widget_del_hook_set(obj, _del_hook);
658    elm_widget_theme_hook_set(obj, _theme_hook);
659
660    wd->horizontal = EINA_FALSE;
661    wd->min_obj_height = 0;
662    wd->max_grp_size = 0;
663    wd->items_count = 0;
664    wd->max_supp_items_count = 0;
665    wd->tot_items_count[0] = 0;
666    wd->tot_items_count[1] = 0;
667    wd->hide_button = 0;
668    wd->special_char = edje_object_data_get(wd->base, "special_char");
669    if(wd->special_char == NULL)  wd->special_char = eina_stringshare_add("*");
670
671    wd->base = edje_object_add(e);
672    _elm_theme_object_set(obj, wd->base, "index", "base/vertical", "default");
673    elm_widget_resize_object_set(obj, wd->base);
674
675    o = evas_object_rectangle_add(e);
676    wd->event[0] = o;
677    evas_object_color_set(o, 0, 0, 0, 0);
678    minw = minh = 0;
679    elm_coords_finger_size_adjust(1, &minw, 1, &minh);
680    evas_object_size_hint_min_set(o, minw, minh);
681    edje_object_part_swallow(wd->base, "elm.swallow.event.0", o);
682    elm_widget_sub_object_add(obj, o);
683    evas_object_event_callback_add(obj, EVAS_CALLBACK_RESIZE, _index_object_resize, obj);
684    evas_object_event_callback_add(o, EVAS_CALLBACK_MOUSE_WHEEL, _wheel, obj);
685    evas_object_event_callback_add(o, EVAS_CALLBACK_MOUSE_DOWN, _mouse_down, obj);
686    evas_object_event_callback_add(o, EVAS_CALLBACK_MOUSE_UP, _mouse_up, obj);
687    evas_object_event_callback_add(o, EVAS_CALLBACK_MOUSE_MOVE, _mouse_move, obj);
688    evas_object_show(o);
689    if (edje_object_part_exists(wd->base, "elm.swallow.event.1"))
690      {
691         o = evas_object_rectangle_add(e);
692         wd->event[1] = o;
693         evas_object_color_set(o, 0, 0, 0, 0);
694         evas_object_size_hint_min_set(o, minw, minh);
695         edje_object_part_swallow(wd->base, "elm.swallow.event.1", o);
696         elm_widget_sub_object_add(obj, o);
697      }
698
699    wd->bx[0] = evas_object_box_add(e);
700    evas_object_box_layout_set(wd->bx[0], _layout, wd, NULL);
701    elm_widget_sub_object_add(obj, wd->bx[0]);
702    edje_object_part_swallow(wd->base, "elm.swallow.index.0", wd->bx[0]);
703    evas_object_show(wd->bx[0]);
704
705    if (edje_object_part_exists(wd->base, "elm.swallow.index.1"))
706      {
707         wd->bx[1] = evas_object_box_add(e);
708         evas_object_box_layout_set(wd->bx[1], _layout, wd, NULL);
709         elm_widget_sub_object_add(obj, wd->bx[1]);
710         edje_object_part_swallow(wd->base, "elm.swallow.index.1", wd->bx[1]);
711         evas_object_show(wd->bx[1]);
712      }
713
714    wd->scale_factor = elm_scale_get();
715    if ( wd->scale_factor == 0.0 )
716      {
717         wd->scale_factor = 1.0;
718      }
719    string = edje_object_data_get(wd->base, "min_1st_level_obj_height");
720    if(string)
721      wd->min_1st_level_obj_height = (int) (atoi(string))*wd->scale_factor;
722    else
723      wd->min_1st_level_obj_height = MIN_OBJ_HEIGHT*wd->scale_factor;
724    _sizing_eval(obj);
725    wd->popup_str[0] = calloc(1, sizeof(char) * 1);
726    wd->popup_str[1] = calloc(1, sizeof(char) * 1);
727    return obj;
728 }
729
730 static int
731 _group_count(Evas_Object *obj, int extraIndex, int adj_pos, int vis_pos)
732 {
733    Widget_Data *wd = elm_widget_data_get(obj);
734    if (!wd) return 0;
735    int group_count = MIN_GRP_SIZE;
736    while(group_count <= wd->max_grp_size)
737      {
738         if(extraIndex <= wd->max_grp_size*adj_pos)
739           {
740              if(group_count*adj_pos>=extraIndex) return group_count;
741           }
742         else
743           return wd->max_grp_size;
744
745         group_count += MIN_GRP_SIZE;
746      }
747    return group_count;
748 }
749 static void
750 _index_process(Evas_Object *obj)
751 {
752    int extraIndex;
753    int j,i, group_count;
754    Eina_List *l;
755    Elm_Index_Item *it;
756    int count;
757    int n;
758
759    Widget_Data *wd = elm_widget_data_get(obj);
760    if (!wd) return;
761
762    if (wd->items_count == 0) return;
763
764    const int adj_pos = (wd->items_count-1)*0.5;
765    if(wd->tot_items_count[wd->level] <= wd->max_supp_items_count)
766       n = wd->tot_items_count[wd->level];
767    else
768       n = wd->max_supp_items_count;
769    group_count = MIN_GRP_SIZE;
770
771    int *indx = (int*)calloc(n, sizeof(int));
772    if (!indx) return;
773
774    const int minh = wd->min_obj_height;
775    EINA_LIST_FOREACH(wd->items, l, it)
776      {
777         it->vis_letter = eina_stringshare_add(it->letter);
778         it->size =  minh;
779      }
780    int remainder;
781    int numberofparts;
782    int N = wd->items_count;
783
784    for (i=0;i<n;i++)
785      {
786         indx[i] = minh;
787      }
788    extraIndex=n-N;
789    if (extraIndex < 0) return;
790
791    group_count = _group_count(obj, extraIndex, adj_pos, N);
792    if (group_count <= 0) return;
793
794    PlacementPart place[adj_pos];
795    remainder = extraIndex%group_count;
796    numberofparts=(extraIndex/group_count)+(remainder == 0? 0: 1);
797
798    for (i=0;i<numberofparts; i++)
799      {
800         place[i].count=group_count+1;
801         count = (int)(((float)(i+1)/(float)(numberofparts+1))*N);
802         place[i].start= count +i*group_count-1;
803      }
804    if (remainder)
805      place[numberofparts-1].count=remainder+1;
806
807    for (i=0;i<numberofparts;i++)
808      {
809         for (j=0;j<place[i].count; j++)
810           {
811              indx[((place[i].start)+j)]= MIN_PIXEL_VALUE;
812           }
813         indx[(place[i].start+(place[i].count)/2)] = minh-place[i].count+1;
814      }
815    count = 0;
816    EINA_LIST_FOREACH(wd->items, l, it)
817      {
818         int size = indx[count];
819         count++;
820         if (size == minh)
821           {
822              it->vis_letter = eina_stringshare_add(it->letter);
823              continue;
824           }
825         else if (size == 1)
826           {
827              eina_stringshare_del(it->vis_letter);
828              it->vis_letter = eina_stringshare_add("");
829           }
830         else
831           {
832              eina_stringshare_del(it->vis_letter);
833              it->vis_letter = eina_stringshare_add(wd->special_char);
834           }
835         it->size = size*wd->scale_factor;
836      }
837    if (indx)
838      {
839         free(indx);
840         indx = NULL;
841      }
842 }
843 /**
844  * Set the active state of the index programatically
845  *
846  * @param obj The index object
847  * @param active The active starte
848  *
849  * @ingroup Index
850  */
851 EAPI void
852 elm_index_active_set(Evas_Object *obj, Eina_Bool active)
853 {
854    ELM_CHECK_WIDTYPE(obj, widtype);
855    Widget_Data *wd = elm_widget_data_get(obj);
856    if (!wd) return;
857    if (wd->active == active) return;
858    wd->active = active;
859    wd->level = 0;
860    if (wd->active)
861      {
862         _index_box_clear(obj, wd->bx[1], 1);
863         _index_process(obj);
864         _index_box_auto_fill(obj, wd->bx[0], 0);
865         edje_object_signal_emit(wd->base, "elm,state,active", "elm");
866      }
867    else
868      edje_object_signal_emit(wd->base, "elm,state,inactive", "elm");
869 }
870
871 /**
872  * Sets the level of the item.
873  *
874  * @param obj The index object.
875  * @param level To be documented.
876  *
877  * @ingroup Index
878  */
879 EAPI void
880 elm_index_item_level_set(Evas_Object *obj, int level)
881 {
882    ELM_CHECK_WIDTYPE(obj, widtype);
883    Widget_Data *wd = elm_widget_data_get(obj);
884    if (!wd) return;
885    if (wd->level == level) return;
886    wd->level = level;
887 }
888
889 /**
890  * Gets the level of the item.
891  *
892  * @param obj The index object
893  *
894  * @ingroup Index
895  */
896 EAPI int
897 elm_index_item_level_get(const Evas_Object *obj)
898 {
899    ELM_CHECK_WIDTYPE(obj, widtype) 0;
900    Widget_Data *wd = elm_widget_data_get(obj);
901    if (!wd) return 0;
902    return wd->level;
903 }
904
905 /**
906  * Returns the selected item.
907  *
908  * @param obj The index object.
909  * @param level to be documented.
910  *
911  * @ingroup Index
912  */
913 EAPI void *
914 elm_index_item_selected_get(const Evas_Object *obj, int level)
915 {
916    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
917    Widget_Data *wd = elm_widget_data_get(obj);
918    Eina_List *l;
919    Elm_Index_Item *it;
920    if (!wd) return NULL;
921    EINA_LIST_FOREACH(wd->items, l, it)
922      if ((it->selected) && (it->level == level)) return it->data;
923    return NULL;
924 }
925
926 /**
927  * Appends a new item.
928  *
929  * @param obj The index object.
930  * @param letter Letter under which the item should be indexed
931  * @param item The item to put in the index
932  *
933  * @ingroup Index
934  */
935 EAPI void
936 elm_index_item_append(Evas_Object *obj, const char *letter, const void *item)
937 {
938    ELM_CHECK_WIDTYPE(obj, widtype);
939    Widget_Data *wd = elm_widget_data_get(obj);
940    Elm_Index_Item *it;
941    if (!wd) return;
942    it = _item_new(obj, letter, item);
943    if (!it) return;
944    wd->items = eina_list_append(wd->items, it);
945    wd->tot_items_count[wd->level]++;
946    _index_box_clear(obj, wd->bx[wd->level], wd->level);
947 }
948
949 /**
950  * Prepends a new item.
951  *
952  * @param obj The index object.
953  * @param letter Letter under which the item should be indexed
954  * @param item The item to put in the index
955  *
956  * @ingroup Index
957  */
958 EAPI void
959 elm_index_item_prepend(Evas_Object *obj, const char *letter, const void *item)
960 {
961    ELM_CHECK_WIDTYPE(obj, widtype);
962    Widget_Data *wd = elm_widget_data_get(obj);
963    Elm_Index_Item *it;
964
965    if (!wd) return;
966    it = _item_new(obj, letter, item);
967    if (!it) return;
968    wd->items = eina_list_prepend(wd->items, it);
969    wd->tot_items_count[wd->level]++;
970    _index_box_clear(obj, wd->bx[wd->level], wd->level);
971 }
972
973 /**
974  * Append an item after @p relative in letter @p letter.
975  *
976  * @param obj The index object
977  * @param letter Letter under which the item should be indexed
978  * @param item The item to put in the index
979  * @param relative The item to put @p item after
980  *
981  * @ingroup Index
982  */
983 EAPI void
984 elm_index_item_append_relative(Evas_Object *obj, const char *letter, const void *item, const void *relative)
985 {
986    ELM_CHECK_WIDTYPE(obj, widtype);
987    Widget_Data *wd = elm_widget_data_get(obj);
988    Elm_Index_Item *it, *it_rel;
989    if (!wd) return;
990    if (!relative)
991      {
992         elm_index_item_append(obj, letter, item);
993         wd->tot_items_count[wd->level]++;
994         return;
995      }
996    it = _item_new(obj, letter, item);
997    it_rel = _item_find(obj, relative);
998    if (!it_rel)
999      {
1000         elm_index_item_append(obj, letter, item);
1001         wd->tot_items_count[wd->level]++;
1002         return;
1003      }
1004    if (!it) return;
1005    wd->items = eina_list_append_relative(wd->items, it, it_rel);
1006    wd->tot_items_count[wd->level]++;
1007    _index_box_clear(obj, wd->bx[wd->level], wd->level);
1008 }
1009
1010 /**
1011  * Prepend an item before @p relative in letter @p letter.
1012  *
1013  * @param obj The index object
1014  * @param letter Letter under which the item should be indexed
1015  * @param item The item to put in the index
1016  * @param relative The item to put @p item before
1017  *
1018  * @ingroup Index
1019  */
1020 EAPI void
1021 elm_index_item_prepend_relative(Evas_Object *obj, const char *letter, const void *item, const void *relative)
1022 {
1023    ELM_CHECK_WIDTYPE(obj, widtype);
1024    Widget_Data *wd = elm_widget_data_get(obj);
1025    Elm_Index_Item *it, *it_rel;
1026    if (!wd) return;
1027    if (!relative)
1028      {
1029         elm_index_item_prepend(obj, letter, item);
1030         wd->tot_items_count[wd->level]++;
1031         return;
1032      }
1033    it = _item_new(obj, letter, item);
1034    it_rel = _item_find(obj, relative);
1035    if (!it_rel)
1036      {
1037         elm_index_item_append(obj, letter, item);
1038         wd->tot_items_count[wd->level]++;
1039         return;
1040      }
1041    if (!it) return;
1042    wd->items = eina_list_prepend_relative(wd->items, it, it_rel);
1043    wd->tot_items_count[wd->level]++;
1044    _index_box_clear(obj, wd->bx[wd->level], wd->level);
1045 }
1046
1047 /**
1048  * Insert a new @p item into the sorted index @p obj in @p letter.
1049  *
1050  * @param obj The index object
1051  * @param letter Letter under which the item should be indexed
1052  * @param item The item to put in the index
1053  * @param cmp_func The function called for the sort of index items.
1054  * @param cmp_data_func The function called for the sort of the data. It will
1055  * be used when cmp_func return 0. It means the index item already exists.
1056  * So, to decide which data item should be pointed by the index item, a function
1057  * to compare them is needed. If this function is not provided, index items
1058  * will be duplicated. If cmp_data_func returns a non-negative value, the
1059  * previous index item data will be replaced by the inserted @p item. So
1060  * if the previous data need to be free, it should be done in this function,
1061  * because the reference will be lost.
1062  *
1063  * @ingroup Index
1064  */
1065 EAPI void
1066 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)
1067 {
1068    ELM_CHECK_WIDTYPE(obj, widtype);
1069    Widget_Data *wd = elm_widget_data_get(obj);
1070    Eina_List *lnear;
1071    Elm_Index_Item *it;
1072    int cmp;
1073
1074    if (!wd) return;
1075    if (!(wd->items))
1076      {
1077         elm_index_item_append(obj, letter, item);
1078         return;
1079      }
1080
1081    it = _item_new(obj, letter, item);
1082    if (!it) return;
1083
1084    lnear = eina_list_search_sorted_near_list(wd->items, cmp_func, it, &cmp);
1085    if (cmp < 0)
1086      wd->items =  eina_list_append_relative_list(wd->items, it, lnear);
1087    else if (cmp > 0)
1088      wd->items = eina_list_prepend_relative_list(wd->items, it, lnear);
1089    else
1090      {
1091         /* If cmp_data_func is not provided, append a duplicated item */
1092         if (!cmp_data_func)
1093           wd->items =  eina_list_append_relative_list(wd->items, it, lnear);
1094         else
1095           {
1096              Elm_Index_Item *p_it = eina_list_data_get(lnear);
1097              if (cmp_data_func(p_it->data, it->data) >= 0)
1098                p_it->data = it->data;
1099              _item_free(it);
1100           }
1101      }
1102
1103    _index_box_clear(obj, wd->bx[wd->level], wd->level);
1104 }
1105
1106 /**
1107  * Remove an item from the index.
1108  *
1109  * @param obj The index object
1110  * @param item The item to remove from the index
1111  *
1112  * @ingroup Index
1113  */
1114 EAPI void
1115 elm_index_item_del(Evas_Object *obj, const void *item)
1116 {
1117    ELM_CHECK_WIDTYPE(obj, widtype);
1118    Widget_Data *wd = elm_widget_data_get(obj);
1119    Elm_Index_Item *it;
1120    if (!wd) return;
1121    it = _item_find(obj, item);
1122    if (!it) return;
1123    _item_free(it);
1124    wd->tot_items_count[wd->level]--;
1125    _index_box_clear(obj, wd->bx[wd->level], wd->level);
1126 }
1127
1128 /**
1129  * Find an index item using item data.
1130  *
1131  * @param obj The index object
1132  * @param item The item pointed by index item
1133  * @return The index item pointing to @p item
1134  *
1135  * @ingroup Index
1136  */
1137 EAPI Elm_Index_Item *
1138 elm_index_item_find(Evas_Object *obj, const void *item)
1139 {
1140    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
1141    Widget_Data *wd = elm_widget_data_get(obj);
1142    if (!wd) return NULL;
1143    return _item_find(obj, item);
1144 }
1145
1146 /**
1147  * Clears an index of its items.
1148  *
1149  * @param obj The index object.
1150  *
1151  * @ingroup Index
1152  */
1153 EAPI void
1154 elm_index_item_clear(Evas_Object *obj)
1155 {
1156    ELM_CHECK_WIDTYPE(obj, widtype);
1157    Widget_Data *wd = elm_widget_data_get(obj);
1158    Elm_Index_Item *it;
1159    Eina_List *l, *clear = NULL;
1160    if (!wd) return;
1161    _index_box_clear(obj, wd->bx[wd->level], wd->level);
1162    EINA_LIST_FOREACH(wd->items, l, it)
1163      {
1164         if (it->level != wd->level) continue;
1165         clear = eina_list_append(clear, it);
1166      }
1167    EINA_LIST_FREE(clear, it)
1168      {
1169         _item_free(it);
1170         wd->tot_items_count[wd->level]--;
1171      }
1172 }
1173
1174 /**
1175  * Go to item at @p level
1176  *
1177  * @param obj The index object
1178  * @param level The index level
1179  *
1180  * @ingroup Index
1181  */
1182 EAPI void
1183 elm_index_item_go(Evas_Object *obj, int level)
1184 {
1185    ELM_CHECK_WIDTYPE(obj, widtype);
1186    Widget_Data *wd = elm_widget_data_get(obj);
1187    if (!wd) return;
1188    if(level==0)
1189    _index_process(obj);
1190    _index_box_auto_fill(obj, wd->bx[0], 0);
1191    if (wd->level == 1) _index_box_auto_fill(obj, wd->bx[1], 1);
1192 }
1193
1194 /**
1195  * Returns the data associated with the item.
1196  *
1197  * @param it The list item
1198  * @return The data associated with @p it
1199  *
1200  * @ingroup Index
1201  */
1202 EAPI void *
1203 elm_index_item_data_get(const Elm_Index_Item *it)
1204 {
1205    if (!it) return NULL;
1206    return (void *)it->data;
1207 }
1208
1209 /**
1210  * Set the data item from the index item
1211  *
1212  * This set a new data value.
1213  *
1214  * @param it The item
1215  * @param data The new data pointer to set
1216  *
1217  * @ingroup Index
1218  */
1219 EAPI void
1220 elm_index_item_data_set(Elm_Index_Item *it, const void *data)
1221 {
1222    if (!it) return;
1223    it->data = data;
1224 }
1225
1226 /**
1227  * Gets the letter of the item.
1228  *
1229  * @param it The list item
1230  * @return The letter of @p it
1231  *
1232  * @ingroup Index
1233  */
1234 EAPI const char *
1235 elm_index_item_letter_get(const Elm_Index_Item *it)
1236 {
1237    if (!it) return NULL;
1238    return it->letter;
1239 }
1240
1241 /**
1242  * Make the Central Button Image invisible.
1243  *
1244  * @param obj The Index.
1245  * @param invisible Whether button visible or not.
1246  * @return void.
1247  *
1248  * @ingroup Index
1249  */
1250 EAPI void
1251 elm_index_button_image_invisible_set(Evas_Object *obj, Eina_Bool invisible)
1252 {
1253    ELM_CHECK_WIDTYPE(obj, widtype);
1254    Widget_Data *wd = elm_widget_data_get(obj);
1255    wd->hide_button = invisible;
1256
1257    edje_object_signal_emit(wd->base, "elm,state,button,image,hide", "elm");
1258    return;
1259 }
1260