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