modify popup text parts ( 3 parts -> 1 part ) for text align
[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 #define POPUPTEXTLEN 5
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    char popup_str[2][POPUPTEXTLEN];
40    int level;
41    int max_supp_items_count;
42    int tot_items_count[2];
43    int min_obj_height, max_grp_size;
44    int min_1st_level_obj_height;
45    int items_count;
46    Evas_Coord dx, dy;
47    Evas_Coord pwidth, pheight;
48    Ecore_Timer *delay;
49    const char *special_char;
50    Eina_Bool level_active[2];
51    Eina_Bool horizontal : 1;
52    Eina_Bool active : 1;
53    Eina_Bool down : 1;
54    Eina_Bool hide_button : 1;
55    double scale_factor;
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         char popup_text[POPUPTEXTLEN * 2 + 1];
449         if(view_level == 0)
450           {
451              strcpy(wd->popup_str[1], "");
452              if(last)
453                {
454                   strncpy(wd->popup_str[0], last, POPUPTEXTLEN - 1);
455                   wd->popup_str[0][POPUPTEXTLEN - 1] = '\0';
456                   edje_object_signal_emit(wd->base, "hide_2nd_level", "");
457                }
458           }
459         if(view_level == 1 && wd->level_active[1])
460           {
461              strncpy(wd->popup_str[1], last, POPUPTEXTLEN);
462              wd->popup_str[1][POPUPTEXTLEN - 1] = '\0';
463              edje_object_signal_emit(wd->base, "hide_first_level", "");
464           }
465         snprintf(popup_text, POPUPTEXTLEN * 2 + 1, "%s%s", wd->popup_str[0], wd->popup_str[1]);
466         edje_object_part_text_set(wd->base, "elm.text", popup_text);
467      }
468
469    if(label)
470      free(label);
471    if(last)
472      free(last);
473 }
474
475 static void
476 _wheel(void *data, Evas *e __UNUSED__, Evas_Object *o __UNUSED__, void *event_info __UNUSED__)
477 {
478    Widget_Data *wd = elm_widget_data_get(data);
479    if (!wd) return;
480 }
481
482 static void
483 _mouse_down(void *data, Evas *e __UNUSED__, Evas_Object *o __UNUSED__, void *event_info)
484 {
485    Widget_Data *wd = elm_widget_data_get(data);
486    Evas_Event_Mouse_Down *ev = event_info;
487    Evas_Coord x, y;
488    if (!wd) return;
489    if (ev->button != 1) return;
490    wd->down = 1;
491    evas_object_geometry_get(wd->base, &x, &y, NULL, NULL);
492    wd->dx = ev->canvas.x - x;
493    wd->dy = ev->canvas.y - y;
494    elm_index_active_set(data, 1);
495    _sel_eval(data, ev->canvas.x, ev->canvas.y);
496    edje_object_part_drag_value_set(wd->base, "elm.dragable.pointer", wd->dx, wd->dy);
497 }
498
499 static void
500 _mouse_up(void *data, Evas *e __UNUSED__, Evas_Object *o __UNUSED__, void *event_info)
501 {
502    Widget_Data *wd = elm_widget_data_get(data);
503    Evas_Event_Mouse_Up *ev = event_info;
504    void *d;
505    Elm_Index_Item *it;
506    Eina_List *l;
507    int view_level;
508
509    if (!wd) return;
510    if (ev->button != 1) return;
511    if (wd->level == 1 && wd->delay) ecore_timer_del(wd->delay);
512    wd->delay = NULL;
513    wd->down = 0;
514    SET_VIEW_LEVEL(wd, view_level);
515    d = (void *)elm_index_item_selected_get(data, view_level);
516    EINA_LIST_FOREACH(wd->items, l, it)
517      {
518         edje_object_signal_emit(it->base, "elm,state,inactive", "elm");
519      }
520    if (d) evas_object_smart_callback_call(data, "selected", d);
521    elm_index_active_set(data, 0);
522    edje_object_signal_emit(wd->base, "elm,state,level,0", "elm");
523 }
524
525 static void
526 _mouse_move(void *data, Evas *e __UNUSED__, Evas_Object *o __UNUSED__, void *event_info)
527 {
528    Widget_Data *wd = elm_widget_data_get(data);
529    Evas_Event_Mouse_Move *ev = event_info;
530    Evas_Coord minw = 0, minh = 0, x, y, dx, dy, adx, ady;
531    void *d;
532    char buf[1024];
533    if (!wd) return;
534    if (!wd->down) return;
535    elm_coords_finger_size_adjust(1, &minw, 1, &minh);
536    evas_object_geometry_get(wd->base, &x, &y, NULL, NULL);
537    x = ev->cur.canvas.x - x;
538    y = ev->cur.canvas.y - y;
539    dx = x - wd->dx;
540    adx = dx;
541    if (adx < 0) adx = -dx;
542    dy = y - wd->dy;
543    ady = dy;
544    if (ady < 0) ady = -dy;
545    edje_object_part_drag_value_set(wd->base, "elm.dragable.pointer", x, y);
546    if (wd->horizontal)
547      {
548      }
549    else
550      {
551         if (adx > minw)
552           {
553              if (wd->level == 0)
554                {
555                   wd->level = 1;
556                   snprintf(buf, sizeof(buf), "elm,state,level,%i", wd->level);
557                   edje_object_signal_emit(wd->base, buf, "elm");
558                   evas_object_smart_callback_call(data, "level,up", NULL);
559                }
560           }
561         else
562           {
563              if (wd->level == 1)
564                {
565                   wd->level = 0;
566                   snprintf(buf, sizeof(buf), "elm,state,level,%i", wd->level);
567                   edje_object_signal_emit(wd->base, buf, "elm");
568                   d = (void *)elm_index_item_selected_get(data, wd->level);
569                   evas_object_smart_callback_call(data, "changed", d);
570                   if (wd->delay) ecore_timer_del(wd->delay);
571                   wd->delay = ecore_timer_add(0.2, _delay_change, data);
572                   evas_object_smart_callback_call(data, "level,down", NULL);
573                }
574           }
575      }
576    _sel_eval(data, ev->cur.canvas.x, ev->cur.canvas.y);
577 }
578 static void
579 _index_box_refill_job(void *data)
580 {
581    Widget_Data *wd = elm_widget_data_get((Evas_Object *)data);
582    if (!wd) return;
583
584    const char *string;
585    Evas_Coord pw, ph;
586
587    evas_object_geometry_get(wd->base, NULL, NULL, &pw, &ph);
588    wd->scale_factor = elm_scale_get();
589    if ( wd->scale_factor == 0.0 ) {
590      wd->scale_factor = 1.0;
591    }
592    string = edje_object_data_get(wd->base, "min_obj_height");
593    if(string)
594      wd->min_obj_height = (int) (atoi(string))*wd->scale_factor;
595    else
596      wd->min_obj_height = MIN_OBJ_HEIGHT*wd->scale_factor;
597    if(!wd->min_obj_height) return;
598
599    wd->max_grp_size = wd->min_obj_height - 2*MIN_GRP_SIZE;
600    wd->items_count = ph/wd->min_obj_height;
601    wd->max_supp_items_count = wd->max_grp_size*(int)((wd->items_count-1)*0.5)+wd->items_count;
602
603    if(pw != wd->pwidth && ph != wd->pheight)
604      {
605         if(wd->down == 1)
606           {
607              wd->active = 0;
608              elm_index_active_set(data, 1);
609           }
610         _index_box_clear((Evas_Object *)data, wd->bx[0], 0);
611         evas_object_smart_calculate( wd->bx[0]);
612         elm_index_item_go((Evas_Object *)data, wd->level);
613         wd->pwidth = pw;
614         wd->pheight = ph;
615      }
616 }
617
618 static void _index_object_resize(void *data, Evas *e, Evas_Object *obj, void *event_info)
619 {
620    Widget_Data *wd;
621    if(!data) return;
622    wd = elm_widget_data_get((Evas_Object *)data);
623    if(!wd) return;
624    ecore_job_add(_index_box_refill_job, (Evas_Object *)data);
625 }
626
627 /**
628  * Add a new index to the parent
629  *
630  * @param parent The parent object
631  * @return The new object or NULL if it cannot be created
632  *
633  * @ingroup Index
634  */
635 EAPI Evas_Object *
636 elm_index_add(Evas_Object *parent)
637 {
638    Evas_Object *obj;
639    Evas_Object *o;
640    Evas *e;
641    Widget_Data *wd;
642    Evas_Coord minw, minh;
643    const char *string;
644
645    wd = ELM_NEW(Widget_Data);
646    e = evas_object_evas_get(parent);
647    if(!e) return NULL;
648    obj = elm_widget_add(e);
649    ELM_SET_WIDTYPE(widtype, "index");
650    elm_widget_type_set(obj, "index");
651    elm_widget_sub_object_add(parent, obj);
652    elm_widget_data_set(obj, wd);
653    elm_widget_del_hook_set(obj, _del_hook);
654    elm_widget_theme_hook_set(obj, _theme_hook);
655
656    wd->horizontal = EINA_FALSE;
657    wd->min_obj_height = 0;
658    wd->max_grp_size = 0;
659    wd->items_count = 0;
660    wd->max_supp_items_count = 0;
661    wd->tot_items_count[0] = 0;
662    wd->tot_items_count[1] = 0;
663    wd->hide_button = 0;
664    wd->special_char = edje_object_data_get(wd->base, "special_char");
665    if(wd->special_char == NULL)  wd->special_char = eina_stringshare_add("*");
666
667    wd->base = edje_object_add(e);
668    _elm_theme_object_set(obj, wd->base, "index", "base/vertical", "default");
669    elm_widget_resize_object_set(obj, wd->base);
670
671    o = evas_object_rectangle_add(e);
672    wd->event[0] = o;
673    evas_object_color_set(o, 0, 0, 0, 0);
674    minw = minh = 0;
675    elm_coords_finger_size_adjust(1, &minw, 1, &minh);
676    evas_object_size_hint_min_set(o, minw, minh);
677    edje_object_part_swallow(wd->base, "elm.swallow.event.0", o);
678    elm_widget_sub_object_add(obj, o);
679    evas_object_event_callback_add(obj, EVAS_CALLBACK_RESIZE, _index_object_resize, obj);
680    evas_object_event_callback_add(o, EVAS_CALLBACK_MOUSE_WHEEL, _wheel, obj);
681    evas_object_event_callback_add(o, EVAS_CALLBACK_MOUSE_DOWN, _mouse_down, obj);
682    evas_object_event_callback_add(o, EVAS_CALLBACK_MOUSE_UP, _mouse_up, obj);
683    evas_object_event_callback_add(o, EVAS_CALLBACK_MOUSE_MOVE, _mouse_move, obj);
684    evas_object_show(o);
685    if (edje_object_part_exists(wd->base, "elm.swallow.event.1"))
686      {
687         o = evas_object_rectangle_add(e);
688         wd->event[1] = o;
689         evas_object_color_set(o, 0, 0, 0, 0);
690         evas_object_size_hint_min_set(o, minw, minh);
691         edje_object_part_swallow(wd->base, "elm.swallow.event.1", o);
692         elm_widget_sub_object_add(obj, o);
693      }
694
695    wd->bx[0] = evas_object_box_add(e);
696    evas_object_box_layout_set(wd->bx[0], _layout, wd, NULL);
697    elm_widget_sub_object_add(obj, wd->bx[0]);
698    edje_object_part_swallow(wd->base, "elm.swallow.index.0", wd->bx[0]);
699    evas_object_show(wd->bx[0]);
700
701    if (edje_object_part_exists(wd->base, "elm.swallow.index.1"))
702      {
703         wd->bx[1] = evas_object_box_add(e);
704         evas_object_box_layout_set(wd->bx[1], _layout, wd, NULL);
705         elm_widget_sub_object_add(obj, wd->bx[1]);
706         edje_object_part_swallow(wd->base, "elm.swallow.index.1", wd->bx[1]);
707         evas_object_show(wd->bx[1]);
708      }
709
710    wd->scale_factor = elm_scale_get();
711    if ( wd->scale_factor == 0.0 )
712      {
713         wd->scale_factor = 1.0;
714      }
715    string = edje_object_data_get(wd->base, "min_1st_level_obj_height");
716    if(string)
717      wd->min_1st_level_obj_height = (int) (atoi(string))*wd->scale_factor;
718    else
719      wd->min_1st_level_obj_height = MIN_OBJ_HEIGHT*wd->scale_factor;
720    _sizing_eval(obj);
721    return obj;
722 }
723
724 static int
725 _group_count(Evas_Object *obj, int extraIndex, int adj_pos, int vis_pos)
726 {
727    Widget_Data *wd = elm_widget_data_get(obj);
728    if (!wd) return 0;
729    int group_count = MIN_GRP_SIZE;
730    while(group_count <= wd->max_grp_size)
731      {
732         if(extraIndex <= wd->max_grp_size*adj_pos)
733           {
734              if(group_count*adj_pos>=extraIndex) return group_count;
735           }
736         else
737           return wd->max_grp_size;
738
739         group_count += MIN_GRP_SIZE;
740      }
741    return group_count;
742 }
743 static void
744 _index_process(Evas_Object *obj)
745 {
746    int extraIndex;
747    int j,i, group_count;
748    Eina_List *l;
749    Elm_Index_Item *it;
750    int count;
751    int n;
752
753    Widget_Data *wd = elm_widget_data_get(obj);
754    if (!wd) return;
755
756    if (wd->items_count == 0) return;
757
758    const int adj_pos = (wd->items_count-1)*0.5;
759    if(wd->tot_items_count[wd->level] <= wd->max_supp_items_count)
760       n = wd->tot_items_count[wd->level];
761    else
762       n = wd->max_supp_items_count;
763    group_count = MIN_GRP_SIZE;
764
765    int *indx = (int*)calloc(n, sizeof(int));
766    if (!indx) return;
767
768    const int minh = wd->min_obj_height;
769    EINA_LIST_FOREACH(wd->items, l, it)
770      {
771         it->vis_letter = eina_stringshare_add(it->letter);
772         it->size =  minh;
773      }
774    int remainder;
775    int numberofparts;
776    int N = wd->items_count;
777
778    for (i=0;i<n;i++)
779      {
780         indx[i] = minh;
781      }
782    extraIndex=n-N;
783    if (extraIndex < 0) return;
784
785    group_count = _group_count(obj, extraIndex, adj_pos, N);
786    if (group_count <= 0) return;
787
788    PlacementPart place[adj_pos];
789    remainder = extraIndex%group_count;
790    numberofparts=(extraIndex/group_count)+(remainder == 0? 0: 1);
791
792    for (i=0;i<numberofparts; i++)
793      {
794         place[i].count=group_count+1;
795         count = (int)(((float)(i+1)/(float)(numberofparts+1))*N);
796         place[i].start= count +i*group_count-1;
797      }
798    if (remainder)
799      place[numberofparts-1].count=remainder+1;
800
801    for (i=0;i<numberofparts;i++)
802      {
803         for (j=0;j<place[i].count; j++)
804           {
805              indx[((place[i].start)+j)]= MIN_PIXEL_VALUE;
806           }
807         indx[(place[i].start+(place[i].count)/2)] = minh-place[i].count+1;
808      }
809    count = 0;
810    EINA_LIST_FOREACH(wd->items, l, it)
811      {
812         int size = indx[count];
813         count++;
814         if (size == minh)
815           {
816              it->vis_letter = eina_stringshare_add(it->letter);
817              continue;
818           }
819         else if (size == 1)
820           {
821              eina_stringshare_del(it->vis_letter);
822              it->vis_letter = eina_stringshare_add("");
823           }
824         else
825           {
826              eina_stringshare_del(it->vis_letter);
827              it->vis_letter = eina_stringshare_add(wd->special_char);
828           }
829         it->size = size*wd->scale_factor;
830      }
831    if (indx)
832      {
833         free(indx);
834         indx = NULL;
835      }
836 }
837 /**
838  * Set the active state of the index programatically
839  *
840  * @param obj The index object
841  * @param active The active starte
842  *
843  * @ingroup Index
844  */
845 EAPI void
846 elm_index_active_set(Evas_Object *obj, Eina_Bool active)
847 {
848    ELM_CHECK_WIDTYPE(obj, widtype);
849    Widget_Data *wd = elm_widget_data_get(obj);
850    if (!wd) return;
851    if (wd->active == active) return;
852    wd->active = active;
853    wd->level = 0;
854    if (wd->active)
855      {
856         _index_box_clear(obj, wd->bx[1], 1);
857         _index_process(obj);
858         _index_box_auto_fill(obj, wd->bx[0], 0);
859         edje_object_signal_emit(wd->base, "elm,state,active", "elm");
860      }
861    else
862      edje_object_signal_emit(wd->base, "elm,state,inactive", "elm");
863 }
864
865 /**
866  * Sets the level of the item.
867  *
868  * @param obj The index object.
869  * @param level To be documented.
870  *
871  * @ingroup Index
872  */
873 EAPI void
874 elm_index_item_level_set(Evas_Object *obj, int level)
875 {
876    ELM_CHECK_WIDTYPE(obj, widtype);
877    Widget_Data *wd = elm_widget_data_get(obj);
878    if (!wd) return;
879    if (wd->level == level) return;
880    wd->level = level;
881 }
882
883 /**
884  * Gets the level of the item.
885  *
886  * @param obj The index object
887  *
888  * @ingroup Index
889  */
890 EAPI int
891 elm_index_item_level_get(const Evas_Object *obj)
892 {
893    ELM_CHECK_WIDTYPE(obj, widtype) 0;
894    Widget_Data *wd = elm_widget_data_get(obj);
895    if (!wd) return 0;
896    return wd->level;
897 }
898
899 /**
900  * Returns the selected item.
901  *
902  * @param obj The index object.
903  * @param level to be documented.
904  *
905  * @ingroup Index
906  */
907 EAPI const void *
908 elm_index_item_selected_get(const Evas_Object *obj, int level)
909 {
910    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
911    Widget_Data *wd = elm_widget_data_get(obj);
912    Eina_List *l;
913    Elm_Index_Item *it;
914    if (!wd) return NULL;
915    EINA_LIST_FOREACH(wd->items, l, it)
916      if ((it->selected) && (it->level == level)) return it->data;
917    return NULL;
918 }
919
920 /**
921  * Appends a new item.
922  *
923  * @param obj The index object.
924  * @param letter Letter under which the item should be indexed
925  * @param item The item to put in the index
926  *
927  * @ingroup Index
928  */
929 EAPI void
930 elm_index_item_append(Evas_Object *obj, const char *letter, const void *item)
931 {
932    ELM_CHECK_WIDTYPE(obj, widtype);
933    Widget_Data *wd = elm_widget_data_get(obj);
934    Elm_Index_Item *it;
935    if (!wd) return;
936    it = _item_new(obj, letter, item);
937    if (!it) return;
938    wd->items = eina_list_append(wd->items, it);
939    wd->tot_items_count[wd->level]++;
940    _index_box_clear(obj, wd->bx[wd->level], wd->level);
941 }
942
943 /**
944  * Prepends a new item.
945  *
946  * @param obj The index object.
947  * @param letter Letter under which the item should be indexed
948  * @param item The item to put in the index
949  *
950  * @ingroup Index
951  */
952 EAPI void
953 elm_index_item_prepend(Evas_Object *obj, const char *letter, const void *item)
954 {
955    ELM_CHECK_WIDTYPE(obj, widtype);
956    Widget_Data *wd = elm_widget_data_get(obj);
957    Elm_Index_Item *it;
958
959    if (!wd) return;
960    it = _item_new(obj, letter, item);
961    if (!it) return;
962    wd->items = eina_list_prepend(wd->items, it);
963    wd->tot_items_count[wd->level]++;
964    _index_box_clear(obj, wd->bx[wd->level], wd->level);
965 }
966
967 /**
968  * Append an item after @p relative in letter @p letter.
969  *
970  * @param obj The index object
971  * @param letter Letter under which the item should be indexed
972  * @param item The item to put in the index
973  * @param relative The item to put @p item after
974  *
975  * @ingroup Index
976  */
977 EAPI void
978 elm_index_item_append_relative(Evas_Object *obj, const char *letter, const void *item, const void *relative)
979 {
980    ELM_CHECK_WIDTYPE(obj, widtype);
981    Widget_Data *wd = elm_widget_data_get(obj);
982    Elm_Index_Item *it, *it_rel;
983    if (!wd) return;
984    if (!relative)
985      {
986         elm_index_item_append(obj, letter, item);
987         wd->tot_items_count[wd->level]++;
988         return;
989      }
990    it = _item_new(obj, letter, item);
991    it_rel = _item_find(obj, relative);
992    if (!it_rel)
993      {
994         elm_index_item_append(obj, letter, item);
995         wd->tot_items_count[wd->level]++;
996         return;
997      }
998    if (!it) return;
999    wd->items = eina_list_append_relative(wd->items, it, it_rel);
1000    wd->tot_items_count[wd->level]++;
1001    _index_box_clear(obj, wd->bx[wd->level], wd->level);
1002 }
1003
1004 /**
1005  * Prepend an item before @p relative in letter @p letter.
1006  *
1007  * @param obj The index object
1008  * @param letter Letter under which the item should be indexed
1009  * @param item The item to put in the index
1010  * @param relative The item to put @p item before
1011  *
1012  * @ingroup Index
1013  */
1014 EAPI void
1015 elm_index_item_prepend_relative(Evas_Object *obj, const char *letter, const void *item, const void *relative)
1016 {
1017    ELM_CHECK_WIDTYPE(obj, widtype);
1018    Widget_Data *wd = elm_widget_data_get(obj);
1019    Elm_Index_Item *it, *it_rel;
1020    if (!wd) return;
1021    if (!relative)
1022      {
1023         elm_index_item_prepend(obj, letter, item);
1024         wd->tot_items_count[wd->level]++;
1025         return;
1026      }
1027    it = _item_new(obj, letter, item);
1028    it_rel = _item_find(obj, relative);
1029    if (!it_rel)
1030      {
1031         elm_index_item_append(obj, letter, item);
1032         wd->tot_items_count[wd->level]++;
1033         return;
1034      }
1035    if (!it) return;
1036    wd->items = eina_list_prepend_relative(wd->items, it, it_rel);
1037    wd->tot_items_count[wd->level]++;
1038    _index_box_clear(obj, wd->bx[wd->level], wd->level);
1039 }
1040
1041 /**
1042  * Insert a new @p item into the sorted index @p obj in @p letter.
1043  *
1044  * @param obj The index object
1045  * @param letter Letter under which the item should be indexed
1046  * @param item The item to put in the index
1047  * @param cmp_func The function called for the sort of index items.
1048  * @param cmp_data_func The function called for the sort of the data. It will
1049  * be used when cmp_func return 0. It means the index item already exists.
1050  * So, to decide which data item should be pointed by the index item, a function
1051  * to compare them is needed. If this function is not provided, index items
1052  * will be duplicated. If cmp_data_func returns a non-negative value, the
1053  * previous index item data will be replaced by the inserted @p item. So
1054  * if the previous data need to be free, it should be done in this function,
1055  * because the reference will be lost.
1056  *
1057  * @ingroup Index
1058  */
1059 EAPI void
1060 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)
1061 {
1062    ELM_CHECK_WIDTYPE(obj, widtype);
1063    Widget_Data *wd = elm_widget_data_get(obj);
1064    Eina_List *lnear;
1065    Elm_Index_Item *it;
1066    int cmp;
1067
1068    if (!wd) return;
1069    if (!(wd->items))
1070      {
1071         elm_index_item_append(obj, letter, item);
1072         return;
1073      }
1074
1075    it = _item_new(obj, letter, item);
1076    if (!it) return;
1077
1078    lnear = eina_list_search_sorted_near_list(wd->items, cmp_func, it, &cmp);
1079    if (cmp < 0)
1080      wd->items =  eina_list_append_relative_list(wd->items, it, lnear);
1081    else if (cmp > 0)
1082      wd->items = eina_list_prepend_relative_list(wd->items, it, lnear);
1083    else
1084      {
1085         /* If cmp_data_func is not provided, append a duplicated item */
1086         if (!cmp_data_func)
1087           wd->items =  eina_list_append_relative_list(wd->items, it, lnear);
1088         else
1089           {
1090              Elm_Index_Item *p_it = eina_list_data_get(lnear);
1091              if (cmp_data_func(p_it->data, it->data) >= 0)
1092                p_it->data = it->data;
1093              _item_free(it);
1094           }
1095      }
1096
1097    _index_box_clear(obj, wd->bx[wd->level], wd->level);
1098 }
1099
1100 /**
1101  * Remove an item from the index.
1102  *
1103  * @param obj The index object
1104  * @param item The item to remove from the index
1105  *
1106  * @ingroup Index
1107  */
1108 EAPI void
1109 elm_index_item_del(Evas_Object *obj, const void *item)
1110 {
1111    ELM_CHECK_WIDTYPE(obj, widtype);
1112    Widget_Data *wd = elm_widget_data_get(obj);
1113    Elm_Index_Item *it;
1114    if (!wd) return;
1115    it = _item_find(obj, item);
1116    if (!it) return;
1117    _item_free(it);
1118    wd->tot_items_count[wd->level]--;
1119    _index_box_clear(obj, wd->bx[wd->level], wd->level);
1120 }
1121
1122 /**
1123  * Find an index item using item data.
1124  *
1125  * @param obj The index object
1126  * @param item The item pointed by index item
1127  * @return The index item pointing to @p item
1128  *
1129  * @ingroup Index
1130  */
1131 EAPI Elm_Index_Item *
1132 elm_index_item_find(Evas_Object *obj, const void *item)
1133 {
1134    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
1135    Widget_Data *wd = elm_widget_data_get(obj);
1136    if (!wd) return NULL;
1137    return _item_find(obj, item);
1138 }
1139
1140 /**
1141  * Clears an index of its items.
1142  *
1143  * @param obj The index object.
1144  *
1145  * @ingroup Index
1146  */
1147 EAPI void
1148 elm_index_item_clear(Evas_Object *obj)
1149 {
1150    ELM_CHECK_WIDTYPE(obj, widtype);
1151    Widget_Data *wd = elm_widget_data_get(obj);
1152    Elm_Index_Item *it;
1153    Eina_List *l, *clear = NULL;
1154    if (!wd) return;
1155    _index_box_clear(obj, wd->bx[wd->level], wd->level);
1156    EINA_LIST_FOREACH(wd->items, l, it)
1157      {
1158         if (it->level != wd->level) continue;
1159         clear = eina_list_append(clear, it);
1160      }
1161    EINA_LIST_FREE(clear, it)
1162      {
1163         _item_free(it);
1164         wd->tot_items_count[wd->level]--;
1165      }
1166 }
1167
1168 /**
1169  * Go to item at @p level
1170  *
1171  * @param obj The index object
1172  * @param level The index level
1173  *
1174  * @ingroup Index
1175  */
1176 EAPI void
1177 elm_index_item_go(Evas_Object *obj, int level)
1178 {
1179    ELM_CHECK_WIDTYPE(obj, widtype);
1180    Widget_Data *wd = elm_widget_data_get(obj);
1181    if (!wd) return;
1182    if(level==0)
1183    _index_process(obj);
1184    _index_box_auto_fill(obj, wd->bx[0], 0);
1185    if (wd->level == 1) _index_box_auto_fill(obj, wd->bx[1], 1);
1186 }
1187
1188 /**
1189  * Returns the data associated with the item.
1190  *
1191  * @param it The list item
1192  * @return The data associated with @p it
1193  *
1194  * @ingroup Index
1195  */
1196 EAPI void *
1197 elm_index_item_data_get(const Elm_Index_Item *it)
1198 {
1199    if (!it) return NULL;
1200    return (void *)it->data;
1201 }
1202
1203 /**
1204  * Set the data item from the index item
1205  *
1206  * This set a new data value.
1207  *
1208  * @param it The item
1209  * @param data The new data pointer to set
1210  *
1211  * @ingroup Index
1212  */
1213 EAPI void
1214 elm_index_item_data_set(Elm_Index_Item *it, const void *data)
1215 {
1216    if (!it) return;
1217    it->data = data;
1218 }
1219
1220 /**
1221  * Gets the letter of the item.
1222  *
1223  * @param it The list item
1224  * @return The letter of @p it
1225  *
1226  * @ingroup Index
1227  */
1228 EAPI const char *
1229 elm_index_item_letter_get(const Elm_Index_Item *it)
1230 {
1231    if (!it) return NULL;
1232    return it->letter;
1233 }
1234
1235 /**
1236  * Make the Central Button Image invisible.
1237  *
1238  * @param obj The Index.
1239  * @param invisible Whether button visible or not.
1240  * @return void.
1241  *
1242  * @ingroup Index
1243  */
1244 EAPI void
1245 elm_index_button_image_invisible_set(Evas_Object *obj, Eina_Bool invisible)
1246 {
1247    ELM_CHECK_WIDTYPE(obj, widtype);
1248    Widget_Data *wd = elm_widget_data_get(obj);
1249    wd->hide_button = invisible;
1250
1251    edje_object_signal_emit(wd->base, "elm,state,button,image,hide", "elm");
1252    return;
1253 }
1254