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