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