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