Merge branch 'master' of 165.213.180.234:/git/slp/pkgs/elementary
[framework/uifw/elementary.git] / src / lib / elm_label.c
1 #include <Elementary.h>
2 #include "elm_priv.h"
3
4 /**
5  * @defgroup Label Label
6  * @ingroup Elementary
7  *
8  * Display text, with simple html-like markup. The theme of course
9  * can invent new markup tags and style them any way it likes
10  */
11
12 typedef struct _Widget_Data Widget_Data;
13
14 struct _Widget_Data
15 {
16    Evas_Object *lbl;
17    Evas_Object *bg;
18    const char *label;
19    Evas_Coord lastw;
20    Ecore_Job *deferred_recalc_job;
21    Evas_Coord wrap_w;
22    Evas_Coord wrap_h;
23    Eina_Bool linewrap : 1;
24    Eina_Bool wrapmode : 1;
25    Eina_Bool slidingmode : 1;
26    Eina_Bool slidingellipsis : 1;
27    Eina_Bool changed : 1;
28    Eina_Bool bgcolor : 1;
29    Eina_Bool ellipsis : 1;
30    int slide_duration;
31 };
32
33 static const char *widtype = NULL;
34 static void _del_hook(Evas_Object *obj);
35 static void _theme_hook(Evas_Object *obj);
36 static void _sizing_eval(Evas_Object *obj);
37 static int _get_value_in_key_string(const char *oldstring, char *key, char **value);
38 static int _strbuf_key_value_replace(Eina_Strbuf *srcbuf, char *key, const char *value, int deleteflag);
39 static int _stringshare_key_value_replace(const char **srcstring, char *key, const char *value, int deleteflag);
40 static int _is_width_over(Evas_Object *obj, int linemode);
41 static void _ellipsis_label_to_width(Evas_Object *obj, int linemode);
42
43 static void
44 _elm_win_recalc_job(void *data)
45 {
46    Widget_Data *wd = elm_widget_data_get(data);
47    Evas_Coord minw = -1, minh = -1, maxh = -1;
48    Evas_Coord resw, resh, minminw, minminh;
49    if (!wd) return;
50    wd->deferred_recalc_job = NULL;
51    evas_object_geometry_get(wd->lbl, NULL, NULL, &resw, &resh);
52    resh = 0;
53    minminw = 0;
54    edje_object_size_min_restricted_calc(wd->lbl, &minw, &minh, 0, 0);
55    minminw = minw;
56    minminh = minh;
57    if (wd->wrap_w >= resw) 
58      {
59         resw = wd->wrap_w;
60         edje_object_size_min_restricted_calc(wd->lbl, &minw, &minh, resw, 0);
61         evas_object_size_hint_min_set(data, minw, minh);
62      }
63    else
64      {
65         if (wd->wrap_w > minminw) minminw = wd->wrap_w;
66         edje_object_size_min_restricted_calc(wd->lbl, &minw, &minh, resw, 0);
67         evas_object_size_hint_min_set(data, minminw, minh);
68      }
69
70    if (wd->ellipsis && wd->linewrap && wd->wrap_h > 0 && _is_width_over(data, 1) == 1) 
71      _ellipsis_label_to_width(data, 1);
72
73    maxh = minh;
74    evas_object_size_hint_max_set(data, -1, maxh);
75 }
76
77 static void
78 _del_hook(Evas_Object *obj)
79 {
80    Widget_Data *wd = elm_widget_data_get(obj);
81    if (!wd) return;
82    if (wd->deferred_recalc_job) ecore_job_del(wd->deferred_recalc_job);
83    if (wd->label) eina_stringshare_del(wd->label);
84    if (wd->bg) evas_object_del(wd->bg);
85    free(wd);
86 }
87
88 static void
89 _theme_change(Evas_Object *obj)
90 {
91    Widget_Data *wd = elm_widget_data_get(obj);
92    if (!wd) return;
93
94    if (wd->linewrap)
95      {
96        if (wd->ellipsis)
97          _elm_theme_object_set(obj, wd->lbl, "label", "base_wrap_ellipsis", elm_widget_style_get(obj));
98        else
99          _elm_theme_object_set(obj, wd->lbl, "label", "base_wrap", elm_widget_style_get(obj));
100      }
101    else
102      _elm_theme_object_set(obj, wd->lbl, "label", "base", elm_widget_style_get(obj));
103
104 }
105
106 static void
107 _theme_hook(Evas_Object *obj)
108 {
109    Widget_Data *wd = elm_widget_data_get(obj);
110    if (!wd) return;
111    _theme_change(obj);
112    edje_object_part_text_set(wd->lbl, "elm.text", wd->label);
113    edje_object_scale_set(wd->lbl, elm_widget_scale_get(obj) * 
114                          _elm_config->scale);
115    _sizing_eval(obj);
116 }
117
118 static void
119 _sizing_eval(Evas_Object *obj)
120 {
121    Widget_Data *wd = elm_widget_data_get(obj);
122    Evas_Coord minw = -1, minh = -1, maxw = -1, maxh = -1;
123    Evas_Coord resw, resh;
124    if (!wd) return;
125    if (wd->linewrap)
126      {
127         evas_object_geometry_get(wd->lbl, NULL, NULL, &resw, &resh);
128         if ((resw == wd->lastw) && (!wd->changed)) return;
129         wd->changed = EINA_FALSE;
130         wd->lastw = resw;
131         _elm_win_recalc_job(obj);
132 // FIXME: works ok. but NOT for genlist. what should genlist do?        
133 //        if (wd->deferred_recalc_job) ecore_job_del(wd->deferred_recalc_job);
134 //        wd->deferred_recalc_job = ecore_job_add(_elm_win_recalc_job, obj);
135      }
136    else
137      {
138         evas_object_geometry_get(wd->lbl, NULL, NULL, &resw, &resh);
139         edje_object_size_min_calc(wd->lbl, &minw, &minh);
140                 if (wd->wrap_w > 0 && minw > wd->wrap_w)
141                         minw = wd->wrap_w;
142         evas_object_size_hint_min_set(obj, minw, minh);
143         maxh = minh;
144         evas_object_size_hint_max_set(obj, maxw, maxh);
145
146         if (wd->ellipsis && _is_width_over(obj, 0) == 1) 
147           _ellipsis_label_to_width(obj, 0);
148      }
149 }
150
151 static void 
152 _resize(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
153 {
154    Widget_Data *wd = elm_widget_data_get(data);
155    if (!wd) return;
156    if (wd->linewrap) _sizing_eval(data);
157 }
158
159 static int
160 _get_value_in_key_string(const char *oldstring, char *key, char **value)
161 {
162    char *curlocater, *starttag, *endtag;
163    int firstindex = 0, foundflag = -1;
164
165    curlocater = strstr(oldstring, key);
166    if (curlocater)
167      {
168         starttag = curlocater;
169         endtag = curlocater + strlen(key);
170         if (endtag == NULL || *endtag != '=') 
171           {
172             foundflag = 0;
173             return -1;
174           }
175
176         firstindex = abs(oldstring - curlocater);
177         firstindex += strlen(key)+1; // strlen("key") + strlen("=")
178         *value = (char*)(oldstring + firstindex);
179
180         while (oldstring != starttag)
181           {
182             if (*starttag == '>')
183               {
184                 foundflag = 0;
185                 break;
186               }
187             if (*starttag == '<') 
188               break;
189             else 
190               starttag--;
191             if (starttag == NULL) break;
192           }
193
194         while (NULL != endtag)
195           {
196             if (*endtag == '<')
197               {
198                 foundflag = 0;
199                 break;
200               }
201             if (*endtag == '>') 
202               break;
203             else 
204               endtag++;
205             if (endtag == NULL) break;
206           }
207
208         if (foundflag != 0 && *starttag == '<' && *endtag == '>') 
209           foundflag = 1;
210         else 
211           foundflag = 0;
212      }
213    else
214      {
215        foundflag = 0;
216      }
217
218    if (foundflag == 1) return 0;
219
220    return -1;
221 }
222
223 static int
224 _strbuf_key_value_replace(Eina_Strbuf *srcbuf, char *key, const char *value, int deleteflag)
225 {
226    const char *srcstring = NULL;
227    Eina_Strbuf *repbuf = NULL, *diffbuf = NULL;
228    char *curlocater, *replocater;
229    char *starttag, *endtag;
230    int tagtxtlen = 0, insertflag = 0;
231
232    srcstring = eina_strbuf_string_get(srcbuf);
233    curlocater = strstr(srcstring, key);
234
235    if (curlocater == NULL)
236      {
237        insertflag = 1;
238      }
239    else
240      {
241        do 
242          {
243            starttag = strchr(srcstring, '<');
244            endtag = strchr(srcstring, '>');
245            tagtxtlen = endtag - starttag;
246            if (tagtxtlen <= 0) tagtxtlen = 0;
247            if (starttag < curlocater && curlocater < endtag) break;
248            if (endtag != NULL && endtag+1 != NULL)
249              srcstring = endtag+1;
250            else
251              break;
252          } while (strlen(srcstring) > 1);
253
254        if (starttag && endtag && tagtxtlen > strlen(key))
255          {
256            repbuf = eina_strbuf_new();
257            diffbuf = eina_strbuf_new();
258            eina_strbuf_append_n(repbuf, starttag, tagtxtlen);
259            srcstring = eina_strbuf_string_get(repbuf);
260            curlocater = strstr(srcstring, key);
261
262            if (curlocater != NULL)
263              {
264                replocater = curlocater + strlen(key) + 1;
265
266                while (*replocater == ' ' || *replocater == '=')
267                            {
268                  replocater++;
269                            }
270
271                 while (replocater != NULL && *replocater != ' ' && *replocater != '>')
272                   replocater++;
273
274                if (replocater-curlocater > strlen(key)+1)
275                  {
276                    replocater--;
277                    eina_strbuf_append_n(diffbuf, curlocater, replocater-curlocater+1);
278                  }
279                else
280                  insertflag = 1;
281              }
282            else
283              {
284                insertflag = 1;
285              }
286            eina_strbuf_reset(repbuf);
287          }
288        else
289          {
290            insertflag = 1; 
291          }
292      }
293
294    if (repbuf == NULL) repbuf = eina_strbuf_new();
295    if (diffbuf == NULL) diffbuf = eina_strbuf_new();
296
297    if (insertflag)
298      {
299        eina_strbuf_append_printf(repbuf, "<%s=%s>", key, value);
300        eina_strbuf_prepend(srcbuf, eina_strbuf_string_get(repbuf));
301      }
302    else
303      {
304         if (deleteflag)
305           {
306             eina_strbuf_prepend(diffbuf, "<");
307             eina_strbuf_append(diffbuf, ">");
308             eina_strbuf_replace_first(srcbuf, eina_strbuf_string_get(diffbuf), "");
309           }
310         else
311           {
312             eina_strbuf_append_printf(repbuf, "%s=%s", key, value);
313             eina_strbuf_replace_first(srcbuf, eina_strbuf_string_get(diffbuf), eina_strbuf_string_get(repbuf));
314           }
315      }
316
317    if (repbuf) eina_strbuf_free(repbuf);
318    if (diffbuf) eina_strbuf_free(diffbuf);
319   
320    return 0;           
321 }
322
323 static int
324 _stringshare_key_value_replace(const char **srcstring, char *key, const char *value, int deleteflag)
325 {
326    Eina_Strbuf *sharebuf = NULL;   
327    
328    sharebuf = eina_strbuf_new();
329    eina_strbuf_append(sharebuf, *srcstring);
330    _strbuf_key_value_replace(sharebuf, key, value, deleteflag);
331    eina_stringshare_del(*srcstring);
332    *srcstring = eina_stringshare_add(eina_strbuf_string_get(sharebuf));
333    eina_strbuf_free(sharebuf);
334
335    return 0;
336 }
337
338
339 // FIXME: move to some where(such as elm_util??).
340 //        copied from elm_entry for check pure text length w/o tags.
341 static char *
342 _str_append(char *str, const char *txt, int *len, int *alloc)
343 {
344    int txt_len = strlen(txt);
345
346    if (txt_len <= 0) return str;
347    if ((*len + txt_len) >= *alloc)
348      {
349         char *str2;
350         int alloc2;
351
352         alloc2 = *alloc + txt_len + 128;
353         str2 = realloc(str, alloc2);
354         if (!str2) return str;
355         *alloc = alloc2;
356         str = str2;
357      }
358    strcpy(str + *len, txt);
359    *len += txt_len;
360    return str;
361 }
362
363 // FIXME: move to some where(such as elm_util??).
364 //        copied from elm_entry for check pure text length w/o tags.
365 static char *
366 _strncpy(char* dest, const char* src, size_t count)
367 {
368    if (!dest) 
369      {
370         ERR( "dest is NULL" );
371         return NULL;
372      }
373    if (!src) 
374      {
375         ERR( "src is NULL" );
376         return NULL;
377      }
378    if (count < 0)
379      {
380         ERR( "count is smaller than 0" );
381         return NULL;
382      }
383
384    return strncpy( dest, src, count );
385 }
386
387 // FIXME: move to some where(such as elm_util??).
388 //        copied from elm_entry for check pure text length w/o tags.
389 static char *
390 _mkup_to_text(const char *mkup)
391 {
392    char *str = NULL;
393    int str_len = 0, str_alloc = 0;
394    char *s, *p;
395    char *tag_start, *tag_end, *esc_start, *esc_end, *ts;
396
397    if (!mkup) return NULL;
398    s=p=NULL;
399    tag_start = tag_end = esc_start = esc_end = NULL;
400    p = (char *)mkup;
401    s = p;
402    for (;;)
403      {
404         if (((p!=NULL)&&(*p == 0)) ||
405             (tag_end) || (esc_end) ||
406             (tag_start) || (esc_start))
407           {
408              if (tag_end)
409                {
410                   char *ttag;
411
412                   ttag = malloc(tag_end - tag_start);
413                   if (ttag)
414                     {
415                        _strncpy(ttag, tag_start + 1, tag_end - tag_start - 1);
416                        ttag[tag_end - tag_start - 1] = 0;
417                        if (!strcmp(ttag, "br"))
418                          str = _str_append(str, "\n", &str_len, &str_alloc);
419                        else if (!strcmp(ttag, "\n"))
420                          str = _str_append(str, "\n", &str_len, &str_alloc);
421                        else if (!strcmp(ttag, "\\n"))
422                          str = _str_append(str, "\n", &str_len, &str_alloc);
423                        else if (!strcmp(ttag, "\t"))
424                          str = _str_append(str, "\t", &str_len, &str_alloc);
425                        else if (!strcmp(ttag, "\\t"))
426                          str = _str_append(str, "\t", &str_len, &str_alloc);
427                        else if (!strcmp(ttag, "ps")) /* Unicode paragraph separator */
428                          str = _str_append(str, "\xE2\x80\xA9", &str_len, &str_alloc);
429                        free(ttag);
430                     }
431                   tag_start = tag_end = NULL;
432                }
433              else if (esc_end)
434                {
435                   ts = malloc(esc_end - esc_start + 1);
436                   if (ts)
437                     {
438                        const char *esc;
439                        _strncpy(ts, esc_start, esc_end - esc_start);
440                        ts[esc_end - esc_start] = 0;
441                        esc = evas_textblock_escape_string_get(ts);
442                        if (esc)
443                          str = _str_append(str, esc, &str_len, &str_alloc);
444                        free(ts);
445                     }
446                   esc_start = esc_end = NULL;
447                }
448              else if ((p!=NULL)&&(*p == 0))
449                {
450                   ts = malloc(p - s + 1);
451                   if (ts)
452                     {
453                        _strncpy(ts, s, p - s);
454                        ts[p - s] = 0;
455                        str = _str_append(str, ts, &str_len, &str_alloc);
456                        free(ts);
457                     }
458                   break;
459                }
460           }
461         if ((p!=NULL)&&(*p == '<'))
462           {
463              if (!esc_start)
464                {
465                   tag_start = p;
466                   tag_end = NULL;
467                   ts = malloc(p - s + 1);
468                   if (ts)
469                     {
470                        _strncpy(ts, s, p - s);
471                        ts[p - s] = 0;
472                        str = _str_append(str, ts, &str_len, &str_alloc);
473                        free(ts);
474                     }
475                   s = NULL;
476                }
477           }
478         else if ((p!=NULL)&&(*p == '>'))
479           {
480              if (tag_start)
481                {
482                   tag_end = p;
483                   s = p + 1;
484                }
485           }
486         else if ((p!=NULL)&&(*p == '&'))
487           {
488              if (!tag_start)
489                {
490                   esc_start = p;
491                   esc_end = NULL;
492                   ts = malloc(p - s + 1);
493                   if (ts)
494                     {
495                        _strncpy(ts, s, p - s);
496                        ts[p - s] = 0;
497                        str = _str_append(str, ts, &str_len, &str_alloc);
498                        free(ts);
499                     }
500                   s = NULL;
501                }
502           }
503         else if ((p!=NULL)&&(*p == ';'))
504           {
505              if (esc_start)
506                {
507                   esc_end = p;
508                   s = p + 1;
509                }
510           }
511         p++;
512      }
513    return str;
514 }
515
516 static int
517 _is_width_over(Evas_Object *obj, int linemode)
518 {
519    Evas_Coord x, y, w, h;
520    Evas_Coord vx, vy, vw, vh;
521    Widget_Data *wd = elm_widget_data_get(obj);
522    const char *ellipsis_string = "...";
523    size_t ellen = strlen(ellipsis_string)+1;
524
525    if (!wd) return 0;
526
527    // too short to ellipsis
528    char *plaintxt = _mkup_to_text(edje_object_part_text_get(wd->lbl, "elm.text"));
529    int plainlen = 0;
530    if (plaintxt != NULL)
531    {
532            plainlen = strlen(plaintxt);
533        free(plaintxt);
534    }
535    if (plainlen <= ellen) return 0;   
536
537    edje_object_part_geometry_get(wd->lbl, "elm.text", &x, &y, &w, &h);
538
539    evas_object_geometry_get(obj, &vx, &vy, &vw, &vh);
540
541 /*
542    fprintf(stderr, "## _is_width_over\n");
543    fprintf(stderr, "## x = %d, y = %d, w = %d, h = %d\n", x, y, w, h);
544    fprintf(stderr, "## vx = %d, vy = %d, vw = %d, vh = %d\n", vx, vy, vw, vh);
545    if (linemode)
546            fprintf(stderr, "## wd->wrap_w = %d, wd->wrap_h = %d\n", wd->wrap_w, wd->wrap_h);
547    else
548            fprintf(stderr, "## wd->wrap_w = %d\n", wd->wrap_w);
549    fprintf(stderr, "## check str = %s\n", edje_object_part_text_get(wd->lbl, "elm.text"));
550 */
551
552    if (linemode == 0) // single line
553      {
554        // skip if too early to check widget size
555        if (w < 0 && h < 0)
556          return 0;
557        // if string fits at widget
558        if ((x >= 0) && (y >= 0))
559            {
560                    if ((wd->wrap_w > 0) && (wd->wrap_w < w))
561                    {
562                            Evas_Coord minw, minh;
563                            edje_object_size_min_calc(wd->lbl, &minw, &minh);
564
565                            if (minw < wd->wrap_w)
566                            { // min insufficient
567                                    return 0;
568                            }
569                            else
570                                    return 1;
571                    }
572                    else
573                            return 0;
574            }
575
576        if (0 < wd->wrap_w && w > wd->wrap_w) return 1;
577      }
578    else // multiline
579      {
580 //       if (vy > h && h > wd->wrap_h) return 1;
581                  if ((x >= 0 || y >= 0) && h > wd->wrap_h) return 1;
582      }
583
584    return 0;
585 }
586
587 static void
588 _ellipsis_fontsize_set(Evas_Object *obj, int fontsize)
589 {
590    Widget_Data *wd = elm_widget_data_get(obj);
591    if (!wd) return;
592
593    Eina_Strbuf *fontbuf = NULL;
594    Eina_Strbuf *txtbuf = NULL;
595    txtbuf = eina_strbuf_new();
596    fontbuf = eina_strbuf_new();
597    eina_strbuf_append(txtbuf, edje_object_part_text_get(wd->lbl, "elm.text"));
598    eina_strbuf_append_printf(fontbuf, "%d", fontsize);
599    _strbuf_key_value_replace(txtbuf, "font_size", eina_strbuf_string_get(fontbuf), 0);
600    edje_object_part_text_set(wd->lbl, "elm.text", eina_strbuf_string_get(txtbuf));
601    eina_strbuf_free(fontbuf);
602    eina_strbuf_free(txtbuf);
603 }
604
605 static Eina_Bool
606 _ellipsis_cut_chars_to_widget(Evas_Object *obj, int fontsize, int linemode)
607 {
608    Widget_Data *wd = elm_widget_data_get(obj);
609    if (!wd) return EINA_FALSE;
610
611    const char *ellipsis_string = "...";
612    int minshowcount = strlen(ellipsis_string);
613    Evas_Coord w, h;
614    Evas_Textblock_Cursor *tc1, *tc2;
615    char *cutstr, *elstr;
616    int limitw = 0;
617    int lencutstr = 0;
618    int i = 0;
619
620    edje_object_part_geometry_get(wd->lbl,"elm.text", NULL, NULL, &w, &h);
621    if (w <= 0)
622            return EINA_FALSE;
623    tc1 = evas_object_textblock_cursor_new((Evas_Object*)edje_object_part_object_get(wd->lbl, "elm.text"));
624    tc2 = evas_object_textblock_cursor_new((Evas_Object*)edje_object_part_object_get(wd->lbl, "elm.text"));
625    
626    if (wd->wrap_w > 0 && wd->wrap_w < w)
627            limitw = wd->wrap_w;
628    else
629            limitw = w;
630    evas_textblock_cursor_pos_set(tc1, 0);
631    evas_textblock_cursor_char_coord_set(tc2, limitw, 0);
632    for (i = 0; i <= minshowcount; i++)
633            evas_textblock_cursor_char_prev(tc2);
634    cutstr = evas_textblock_cursor_range_text_get(tc1, tc2, EVAS_TEXTBLOCK_TEXT_PLAIN);
635
636 /*
637    int eolpos = evas_textblock_cursor_paragraph_text_length_get(tc1);
638    Evas_Coord cx, cy, cw, ch;
639    for (i = eolpos; i > minshowcount; i--)
640    {
641            evas_textblock_cursor_pos_set(tc2, i);
642            fprintf(stderr, "## tc2 = %d\n", evas_textblock_cursor_pos_get(tc2));
643            evas_textblock_cursor_char_geometry_get(tc2, &cx, &cy, &cw, &ch);
644            fprintf(stderr, "## limitw = %d\n", limitw);
645            fprintf(stderr, "## cx = %d, cy = %d, cw = %d, ch = %d\n", cx, cy, cw, ch);
646            if (cx <= limitw)
647                    break;
648    }
649 */
650
651    // FIXME: consider other unicode encoding, currently only care about utf-8
652    lencutstr = strlen(cutstr);
653    elstr = malloc(sizeof(char)*(lencutstr+minshowcount+1));
654    strcpy(elstr, cutstr);
655    strcat(elstr, ellipsis_string);
656
657    edje_object_part_text_set(wd->lbl, "elm.text", elstr);
658
659    free(elstr);
660    evas_textblock_cursor_free(tc1);
661    evas_textblock_cursor_free(tc2);
662         
663    return EINA_TRUE;
664 }
665
666 static Eina_Bool
667 _ellipsis_cut_lines_to_widget(Evas_Object *obj, int fontsize, int linemode)
668 {
669    Widget_Data *wd = elm_widget_data_get(obj);
670    if (!wd) return EINA_FALSE;
671
672    const char *ellipsis_string = "...";
673    int minshowcount = strlen(ellipsis_string);
674    Evas_Coord w, h;
675    Evas_Textblock_Cursor *tc1, *tc2;
676    int linenum = 0, cutline = 0;
677    double lineheight = 0.0;
678    char *cutstr, *elstr;
679    int lencutstr = 0;
680    int limith = 0;
681    int i;
682
683    edje_object_part_geometry_get(wd->lbl,"elm.text", NULL, NULL, &w, &h);
684
685    tc1 = evas_object_textblock_cursor_new((Evas_Object*)edje_object_part_object_get(wd->lbl, "elm.text"));
686    tc2 = evas_object_textblock_cursor_new((Evas_Object*)edje_object_part_object_get(wd->lbl, "elm.text"));
687    // goto last paragraph
688    while (evas_textblock_cursor_paragraph_next(tc2) == EINA_TRUE)
689            ;
690    evas_textblock_cursor_paragraph_last(tc2);
691    // get total linenumber
692    linenum = evas_textblock_cursor_line_geometry_get(tc2, NULL, NULL, NULL, NULL);
693    lineheight = h/linenum * 1.0;
694    if (wd->wrap_h > 0 && wd->wrap_h < h)
695            limith = wd->wrap_h;
696    else
697            limith = h;
698    cutline = limith / lineheight;
699    if (cutline < 1)
700            cutline = 1;
701
702    evas_textblock_cursor_pos_set(tc1, 0);
703    evas_textblock_cursor_line_set(tc2, cutline-1);
704    evas_textblock_cursor_line_char_last(tc2);
705    for (i = 0; i <= minshowcount; i++)
706            evas_textblock_cursor_char_prev(tc2);
707    cutstr = evas_textblock_cursor_range_text_get(tc1, tc2, EVAS_TEXTBLOCK_TEXT_PLAIN);
708
709    // FIXME: consider other unicode encoding, currently only care about utf-8
710    lencutstr = strlen(cutstr);
711    elstr = malloc(sizeof(char)*(lencutstr+minshowcount+1));
712    strcpy(elstr, cutstr);
713    strcat(elstr, ellipsis_string);
714
715    edje_object_part_text_set(wd->lbl, "elm.text", elstr);
716
717    free(elstr);
718    evas_textblock_cursor_free(tc1);
719    evas_textblock_cursor_free(tc2);
720
721    return EINA_TRUE;
722 }
723
724 static void
725 _ellipsis_label_to_width(Evas_Object *obj, int linemode)
726 {
727    Widget_Data *wd = elm_widget_data_get(obj);
728    if (!wd) return;
729
730    int cur_fontsize = 0;
731    char *kvalue;
732    const char *minfont, *deffont, *maxfont;
733    int minfontsize, maxfontsize;
734
735    minfont = edje_object_data_get(wd->lbl, "min_font_size");
736    if (minfont) minfontsize = atoi(minfont);
737    else minfontsize = 1;
738    maxfont = edje_object_data_get(wd->lbl, "max_font_size");
739    if (maxfont) maxfontsize = atoi(maxfont);
740    else maxfontsize = 1;
741    deffont = edje_object_data_get(wd->lbl, "default_font_size");
742    if (deffont) cur_fontsize = atoi(deffont);
743    else cur_fontsize = 1;
744    if (minfontsize > maxfontsize || cur_fontsize == 1) return; // theme is not ready for ellipsis
745    if (eina_stringshare_strlen(wd->label) <= 0) return;
746
747    if (_get_value_in_key_string(wd->label, "font_size", &kvalue) == 0)
748      {
749        if (kvalue != NULL) cur_fontsize = atoi(kvalue);
750            fprintf(stderr, "## cur_fontsize = %d\n", cur_fontsize);
751      }
752
753
754    while (_is_width_over(obj, linemode))
755      {
756        if (cur_fontsize > minfontsize)
757          {
758            cur_fontsize -= 3;
759                    if (cur_fontsize < minfontsize)
760              cur_fontsize = minfontsize;
761                    _ellipsis_fontsize_set(obj, cur_fontsize);
762          }
763        else
764          {
765            if (linemode == 0) // single line
766              {
767                _ellipsis_cut_chars_to_widget(obj, cur_fontsize, linemode);
768                            break;
769                          }
770            else // multiline
771              {
772                _ellipsis_cut_lines_to_widget(obj, cur_fontsize, linemode);
773                            break;
774                          }
775          }
776      }
777 }
778
779 /*
780  * setting internal state of mulitline label.
781  * singleline doesn't need it
782  */
783
784 void _label_state_change(Evas_Object *obj)
785 {
786    Widget_Data *wd = elm_widget_data_get(obj);
787    if (!wd) return;
788
789    if (wd->linewrap)
790    {
791            if (wd->wrapmode)
792                    edje_object_signal_emit(wd->lbl, "elm,state,wordwrap", "elm");
793            else
794                    edje_object_signal_emit(wd->lbl, "elm,state,default", "elm");
795    }
796 }
797
798 void _label_sliding_change(Evas_Object *obj)
799 {
800    Widget_Data *wd = elm_widget_data_get(obj);
801    if (!wd) return;
802
803    if (wd->linewrap)
804    {
805            wd->slidingmode = EINA_FALSE;
806            fprintf(stderr, "ERR: elm_label dosen't support multiline sliding effect!!!\n");
807            fprintf(stderr, "ERR: elm_label dosen't support multiline sliding effect!!!\n");
808            fprintf(stderr, "ERR: elm_label dosen't support multiline sliding effect!!!\n");
809            return;
810    }
811
812    char *plaintxt = _mkup_to_text(edje_object_part_text_get(wd->lbl, "elm.text"));
813    int plainlen = 0;
814    if (plaintxt != NULL)
815    {
816            plainlen = strlen(plaintxt);
817        free(plaintxt);
818    }
819    if (plainlen < 1) 
820    {
821            wd->slidingmode = EINA_TRUE;
822            fprintf(stderr, "ERR: too short to slide label!!!\n");
823            return;
824    }
825
826    if (wd->slidingmode)
827    {
828            if (wd->ellipsis)
829            {
830                    wd->slidingellipsis = EINA_TRUE;
831                    elm_label_ellipsis_set(obj, EINA_FALSE);
832            }
833            Edje_Message_Int_Set *msg = alloca(sizeof(Edje_Message_Int_Set) + (sizeof(int)));
834            
835            msg->count=1;
836            msg->val[0] = (int)wd->slide_duration;
837
838            edje_object_message_send(wd->lbl, EDJE_MESSAGE_INT_SET, 0, msg);
839            edje_object_signal_emit(wd->lbl, "elm,state,slide,start", "elm");
840    }
841    else
842    {
843            edje_object_signal_emit(wd->lbl, "elm,state,slide,stop", "elm");
844            if (wd->slidingellipsis)
845            {
846                    wd->slidingellipsis = EINA_FALSE;
847                    elm_label_ellipsis_set(obj, EINA_TRUE);
848            }
849    }
850 }
851
852 /**
853  * Add a new label to the parent
854  *
855  * @param parent The parent object
856  * @return The new object or NULL if it cannot be created
857  *
858  * @ingroup Label
859  */
860 EAPI Evas_Object *
861 elm_label_add(Evas_Object *parent)
862 {
863    Evas_Object *obj;
864    Evas *e;
865    Widget_Data *wd;
866
867    wd = ELM_NEW(Widget_Data);
868    e = evas_object_evas_get(parent);
869    wd->bg = evas_object_rectangle_add(e);
870    evas_object_color_set(wd->bg, 0, 0, 0, 0);
871    obj = elm_widget_add(e);
872    ELM_SET_WIDTYPE(widtype, "label");
873    elm_widget_type_set(obj, "label");
874    elm_widget_sub_object_add(parent, obj);
875    elm_widget_data_set(obj, wd);
876    elm_widget_del_hook_set(obj, _del_hook);
877    elm_widget_theme_hook_set(obj, _theme_hook);
878    elm_widget_can_focus_set(obj, 0);
879
880    wd->linewrap = EINA_FALSE;
881    wd->bgcolor = EINA_FALSE;
882    wd->ellipsis = EINA_FALSE;
883    wd->wrapmode = EINA_FALSE;
884    wd->slidingmode = EINA_FALSE;
885    wd->slidingellipsis = EINA_FALSE;
886    wd->wrap_w = 0;
887    wd->wrap_h = 0;
888    wd->slide_duration = 10;
889
890    wd->lbl = edje_object_add(e);
891    _elm_theme_object_set(obj, wd->lbl, "label", "base", "default");
892    wd->label = eina_stringshare_add("<br>");
893    edje_object_part_text_set(wd->lbl, "elm.text", "<br>");
894    elm_widget_resize_object_set(obj, wd->lbl);
895    
896    evas_object_event_callback_add(wd->lbl, EVAS_CALLBACK_RESIZE, _resize, obj);
897    
898    wd->changed = 1;
899    _sizing_eval(obj);
900    return obj;
901 }
902
903 /**
904  * Set the label on the label object
905  *
906  * @param obj The label object
907  * @param label The label will be used on the label object
908  *
909  * @ingroup Label
910  */
911 EAPI void
912 elm_label_label_set(Evas_Object *obj, const char *label)
913 {
914    ELM_CHECK_WIDTYPE(obj, widtype);
915    Widget_Data *wd = elm_widget_data_get(obj);
916    if (!wd) return;
917    if (!label) label = "";
918    eina_stringshare_replace(&wd->label, label);
919    edje_object_part_text_set(wd->lbl, "elm.text", label);
920    wd->changed = 1;
921    _sizing_eval(obj);
922 }
923
924 /**
925  * Get the label used on the label object
926  *
927  * @param obj The label object
928  * @return The string inside the label
929  * @ingroup Label
930  */
931 EAPI const char *
932 elm_label_label_get(const Evas_Object *obj)
933 {
934    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
935    Widget_Data *wd = elm_widget_data_get(obj);
936    if (!wd) return NULL;
937    return wd->label;
938 }
939
940 /**
941  * Set the wrapping behavior of the label
942  *
943  * @param obj The label object
944  * @param wrap To wrap text or not
945  * @ingroup Label
946  */
947 EAPI void
948 elm_label_line_wrap_set(Evas_Object *obj, Eina_Bool wrap)
949 {
950    ELM_CHECK_WIDTYPE(obj, widtype);
951    Widget_Data *wd = elm_widget_data_get(obj);
952    if (!wd) return;
953    const char *t;
954    if (wd->linewrap == wrap) return;
955    wd->linewrap = wrap;
956    t = eina_stringshare_add(elm_label_label_get(obj));
957    _theme_change(obj);
958    elm_label_label_set(obj, t);
959    eina_stringshare_del(t);
960    wd->changed = 1;
961    _sizing_eval(obj);
962 }
963
964 /**
965  * Get the wrapping behavior of the label
966  *
967  * @param obj The label object
968  * @return To wrap text or not
969  * @ingroup Label
970  */
971 EAPI Eina_Bool
972 elm_label_line_wrap_get(const Evas_Object *obj)
973 {
974    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
975    Widget_Data *wd = elm_widget_data_get(obj);
976    if (!wd) return EINA_FALSE;
977    return wd->linewrap;
978 }
979
980 /**
981  * Set wrap width of the label
982  *
983  * @param obj The label object
984  * @param w The wrap width in pixels at a minimum where words need to wrap
985  * @ingroup Label
986  */
987 EAPI void
988 elm_label_wrap_width_set(Evas_Object *obj, Evas_Coord w)
989 {
990    ELM_CHECK_WIDTYPE(obj, widtype);
991    Widget_Data *wd = elm_widget_data_get(obj);
992    if (!wd) return;
993    if (w < 0) w = 0;
994    if (wd->wrap_w == w) return;
995    if (wd->ellipsis) edje_object_part_text_set(wd->lbl, "elm.text", wd->label);
996    wd->wrap_w = w;
997    _sizing_eval(obj);
998 }
999
1000 /**
1001  * get wrap width of the label
1002  *
1003  * @param obj The label object
1004  * @return The wrap width in pixels at a minimum where words need to wrap
1005  * @ingroup Label
1006  */
1007 EAPI Evas_Coord
1008 elm_label_wrap_width_get(const Evas_Object *obj)
1009 {
1010    ELM_CHECK_WIDTYPE(obj, widtype) 0;
1011    Widget_Data *wd = elm_widget_data_get(obj);
1012    if (!wd) return 0;
1013    return wd->wrap_w;
1014 }
1015
1016 /**
1017  * Set wrap height of the label
1018  *
1019  * @param obj The label object
1020  * @param w The wrap width in pixels at a minimum where words need to wrap
1021  * @ingroup Label
1022  */
1023 EAPI void
1024 elm_label_wrap_height_set(Evas_Object *obj, Evas_Coord h)
1025 {
1026    ELM_CHECK_WIDTYPE(obj, widtype);
1027    Widget_Data *wd = elm_widget_data_get(obj);
1028    if (!wd) return;
1029    if (h < 0) h = 0;
1030    if (wd->wrap_h == h) return;
1031    if (wd->ellipsis) edje_object_part_text_set(wd->lbl, "elm.text", wd->label);
1032    wd->wrap_h = h;
1033    _sizing_eval(obj);
1034 }
1035
1036 /**
1037  * get wrap width of the label
1038  *
1039  * @param obj The label object
1040  * @return The wrap height in pixels at a minimum where words need to wrap
1041  * @ingroup Label
1042  */
1043 EAPI Evas_Coord
1044 elm_label_wrap_height_get(const Evas_Object *obj)
1045 {
1046    ELM_CHECK_WIDTYPE(obj, widtype) 0;
1047    Widget_Data *wd = elm_widget_data_get(obj);
1048    if (!wd) return 0;
1049    return wd->wrap_h;
1050 }
1051
1052 /**
1053  * Set the font size on the label object
1054  *
1055  * @param obj The label object
1056  * @param size font size
1057  *
1058  * @ingroup Label
1059  */
1060 EAPI void
1061 elm_label_fontsize_set(Evas_Object *obj, int fontsize)
1062 {
1063    ELM_CHECK_WIDTYPE(obj, widtype);
1064    Widget_Data *wd = elm_widget_data_get(obj);
1065    Eina_Strbuf *fontbuf = NULL;
1066    int len, removeflag = 0;
1067
1068    if (!wd) return;
1069    len = strlen(wd->label);
1070    if (len <= 0) return;
1071    fontbuf = eina_strbuf_new();
1072    eina_strbuf_append_printf(fontbuf, "%d", fontsize);
1073
1074    if (fontsize == 0) removeflag = 1; // remove fontsize tag
1075
1076    if (_stringshare_key_value_replace(&wd->label, "font_size", eina_strbuf_string_get(fontbuf), removeflag) == 0)
1077      {
1078        edje_object_part_text_set(wd->lbl, "elm.text", wd->label);
1079        wd->changed = 1;
1080        _sizing_eval(obj);
1081      }
1082    eina_strbuf_free(fontbuf);
1083 }
1084
1085 /**
1086  * Set the text align on the label object
1087  *
1088  * @param obj The label object
1089  * @param align align mode ("left", "center", "right")
1090  *
1091  * @ingroup Label
1092  */
1093 EAPI void
1094 elm_label_text_align_set(Evas_Object *obj, const char *alignmode)
1095 {
1096    ELM_CHECK_WIDTYPE(obj, widtype);
1097    Widget_Data *wd = elm_widget_data_get(obj);
1098    int len;
1099
1100    if (!wd) return;
1101    len = strlen(wd->label);
1102    if (len <= 0) return;
1103
1104    if (_stringshare_key_value_replace(&wd->label, "align", alignmode, 0) == 0)
1105      edje_object_part_text_set(wd->lbl, "elm.text", wd->label);
1106
1107    wd->changed = 1;
1108    _sizing_eval(obj);
1109 }
1110
1111 /**
1112  * Set the text color on the label object
1113  *
1114  * @param obj The label object
1115  * @param r Red property background color of The label object 
1116  * @param g Green property background color of The label object 
1117  * @param b Blue property background color of The label object 
1118  * @param a Alpha property background color of The label object 
1119  *
1120  * @ingroup Label
1121  */
1122 EAPI void
1123 elm_label_text_color_set(Evas_Object *obj, unsigned int r, unsigned int g, unsigned int b, unsigned int a)
1124 {
1125    ELM_CHECK_WIDTYPE(obj, widtype);
1126    Widget_Data *wd = elm_widget_data_get(obj);
1127    Eina_Strbuf *colorbuf = NULL;
1128    int len;
1129
1130    if (!wd) return;
1131    len = strlen(wd->label);
1132    if (len <= 0) return;
1133    colorbuf = eina_strbuf_new();
1134    eina_strbuf_append_printf(colorbuf, "#%02X%02X%02X%02X", r, g, b, a);
1135
1136    if (_stringshare_key_value_replace(&wd->label, "color", eina_strbuf_string_get(colorbuf), 0) == 0)
1137      {
1138        edje_object_part_text_set(wd->lbl, "elm.text", wd->label);
1139        wd->changed = 1;
1140        _sizing_eval(obj);
1141      }
1142    eina_strbuf_free(colorbuf);
1143 }
1144
1145
1146 /**
1147  * Set background color of the label
1148  *
1149  * @param obj The label object
1150  * @param r Red property background color of The label object 
1151  * @param g Green property background color of The label object 
1152  * @param b Blue property background color of The label object 
1153  * @param a Alpha property background color of The label object 
1154  * @ingroup Label
1155  */
1156 EAPI void
1157 elm_label_background_color_set(Evas_Object *obj, unsigned int r, unsigned int g, unsigned int b, unsigned int a)
1158 {
1159    ELM_CHECK_WIDTYPE(obj, widtype);
1160    Widget_Data *wd = elm_widget_data_get(obj);
1161    if (!wd) return;
1162    evas_object_color_set(wd->bg, r, g, b, a);
1163
1164    if (wd->bgcolor == EINA_FALSE)
1165      {
1166         wd->bgcolor = 1;
1167         edje_object_part_swallow(wd->lbl, "label.swallow.background", wd->bg);
1168      }
1169 }
1170
1171 /**
1172  * Set the ellipsis behavior of the label
1173  *
1174  * @param obj The label object
1175  * @param ellipsis To ellipsis text or not
1176  * @ingroup Label
1177  */
1178 EAPI void
1179 elm_label_ellipsis_set(Evas_Object *obj, Eina_Bool ellipsis)
1180 {
1181    ELM_CHECK_WIDTYPE(obj, widtype);
1182    Widget_Data *wd = elm_widget_data_get(obj);
1183    if (!wd) return;
1184    if (wd->ellipsis == ellipsis) return;
1185    wd->ellipsis = ellipsis;
1186    if (wd->linewrap) _theme_change(obj);
1187    edje_object_part_text_set(wd->lbl, "elm.text", wd->label);
1188    wd->changed = 1;
1189    _sizing_eval(obj);
1190 }
1191
1192 /**
1193  * Set the wrapmode of the label
1194  *
1195  * @param obj The label object
1196  * @param wrapmode 0 is charwrap, 1 is wordwrap
1197  * @ingroup Label
1198  */
1199 EAPI void
1200 elm_label_wrap_mode_set(Evas_Object *obj, Eina_Bool wrapmode)
1201 {
1202    ELM_CHECK_WIDTYPE(obj, widtype);
1203    Widget_Data *wd = elm_widget_data_get(obj);
1204    if (!wd) return;
1205    if (wd->wrapmode == wrapmode) return;
1206    wd->wrapmode = wrapmode;
1207    _label_state_change(obj);
1208    wd->changed = 1;
1209    _sizing_eval(obj);
1210 }
1211
1212 /**
1213  * Set the text slide of the label
1214  *
1215  * @param obj The label object
1216  * @param slide To start slide or stop
1217  * @ingroup Label
1218  */
1219 EAPI void
1220 elm_label_slide_set(Evas_Object *obj, Eina_Bool slide)
1221 {
1222    ELM_CHECK_WIDTYPE(obj, widtype);
1223    Widget_Data *wd = elm_widget_data_get(obj);
1224    if (!wd) return;
1225
1226    if (wd->slidingmode == slide) return;
1227    wd->slidingmode = slide;
1228    _label_sliding_change(obj);
1229    wd->changed = 1;
1230    _sizing_eval(obj);
1231 }
1232
1233 /**
1234  * get the text slide mode of the label
1235  *
1236  * @param obj The label object
1237  * @return slide slide mode value
1238  * @ingroup Label
1239  */
1240 EAPI Eina_Bool
1241 elm_label_slide_get(Evas_Object *obj)
1242 {
1243    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
1244    Widget_Data *wd = elm_widget_data_get(obj);
1245    if (!wd) return EINA_FALSE;
1246
1247    return wd->slidingmode;
1248 }
1249
1250 /**
1251  * set the slide duration(speed) of the label
1252  *
1253  * @param obj The label object
1254  * @return The duration time in moving text from slide begin position to slide end position
1255  * @ingroup Label
1256  */
1257 EAPI void
1258 elm_label_slide_duration_set(Evas_Object *obj, int duration)
1259 {
1260    ELM_CHECK_WIDTYPE(obj, widtype);
1261    Widget_Data *wd = elm_widget_data_get(obj);
1262    Edje_Message_Int_Set *msg = alloca(sizeof(Edje_Message_Int_Set) + (sizeof(int)));
1263    
1264    if (!wd) return;
1265    wd->slide_duration = duration;
1266
1267    msg->count=1;
1268    msg->val[0] = (int)wd->slide_duration;
1269
1270    edje_object_message_send(wd->lbl, EDJE_MESSAGE_INT_SET, 0, msg);
1271 }
1272
1273 /**
1274  * get the slide duration(speed) of the label
1275  *
1276  * @param obj The label object
1277  * @return The duration time in moving text from slide begin position to slide end position
1278  * @ingroup Label
1279  */
1280 EAPI int
1281 elm_label_slide_duration_get(Evas_Object *obj)
1282 {
1283    ELM_CHECK_WIDTYPE(obj, widtype) 0;
1284    Widget_Data *wd = elm_widget_data_get(obj);
1285    if (!wd) return 0;
1286    return wd->slide_duration;
1287 }