From: Hyoyoung Chang <hyoyoung@gmail.com>
[framework/uifw/elementary.git] / src / lib / elm_label.c
1 #include <Elementary.h>
2 #include "elm_priv.h"
3
4 typedef struct _Widget_Data Widget_Data;
5
6 struct _Widget_Data
7 {
8    Evas_Object *lbl;
9    const char *label;
10    const char *format;
11    Ecore_Job *deferred_recalc_job;
12    double slide_duration;
13    Evas_Coord lastw;
14    Evas_Coord wrap_w;
15    Elm_Wrap_Type linewrap;
16    Eina_Bool changed : 1;
17    Eina_Bool ellipsis : 1;
18    Eina_Bool slidingmode : 1;
19    Eina_Bool slidingellipsis : 1;
20 };
21
22 static const char *widtype = NULL;
23 static void _del_hook(Evas_Object *obj);
24 static void _mirrored_set(Evas_Object *obj, Eina_Bool rtl);
25 static void _theme_hook(Evas_Object *obj);
26 static void _sizing_eval(Evas_Object *obj);
27 static int _get_value_in_key_string(const char *oldstring, const char *key, char **value);
28 static int _strbuf_key_value_replace(Eina_Strbuf *srcbuf, const char *key, const char *value, int deleteflag);
29 static int _stringshare_key_value_replace(const char **srcstring, const char *key, const char *value, int deleteflag);
30 static void _label_sliding_change(Evas_Object *obj);
31
32 static void
33 _elm_recalc_job(void *data)
34 {
35    Widget_Data *wd = elm_widget_data_get(data);
36    Evas_Coord minw = -1, minh = -1;
37    Evas_Coord resw;
38    if (!wd) return;
39    evas_event_freeze(evas_object_evas_get(data));
40    wd->deferred_recalc_job = NULL;
41    evas_object_geometry_get(wd->lbl, NULL, NULL, &resw, NULL);
42    if (wd->wrap_w > resw)
43       resw = wd->wrap_w;
44
45    edje_object_size_min_restricted_calc(wd->lbl, &minw, &minh, resw, 0);
46    /* This is a hack to workaround the way min size hints are treated.
47     * If the minimum width is smaller than the restricted width, it means
48     * the mininmum doesn't matter. */
49    if ((minw <= resw) && (minw != wd->wrap_w))
50      {
51         Evas_Coord ominw = -1;
52         evas_object_size_hint_min_get(data, &ominw, NULL);
53         minw = ominw;
54      }
55    evas_object_size_hint_min_set(data, minw, minh);
56    evas_event_thaw(evas_object_evas_get(data));
57    evas_event_thaw_eval(evas_object_evas_get(data));
58 }
59
60 static void
61 _del_hook(Evas_Object *obj)
62 {
63    Widget_Data *wd = elm_widget_data_get(obj);
64    if (!wd) return;
65    evas_event_freeze(evas_object_evas_get(obj));
66    if (wd->deferred_recalc_job) ecore_job_del(wd->deferred_recalc_job);
67    if (wd->label) eina_stringshare_del(wd->label);
68    free(wd);
69    evas_event_thaw(evas_object_evas_get(obj));
70    evas_event_thaw_eval(evas_object_evas_get(obj));
71 }
72
73 static void
74 _theme_change(Evas_Object *obj)
75 {
76    Widget_Data *wd = elm_widget_data_get(obj);
77    if (!wd) return;
78
79    _elm_theme_object_set(obj, wd->lbl, "label", "base",
80          elm_widget_style_get(obj));
81 }
82
83 static void
84 _mirrored_set(Evas_Object *obj, Eina_Bool rtl)
85 {
86    Widget_Data *wd = elm_widget_data_get(obj);
87    if (!wd) return;
88    edje_object_mirrored_set(wd->lbl, rtl);
89 }
90
91 static void
92 _theme_hook(Evas_Object *obj)
93 {
94    Widget_Data *wd = elm_widget_data_get(obj);
95    if (!wd) return;
96    evas_event_freeze(evas_object_evas_get(obj));
97    _elm_widget_mirrored_reload(obj);
98    _mirrored_set(obj, elm_widget_mirrored_get(obj));
99    _theme_change(obj);
100    edje_object_part_text_style_user_set(wd->lbl, "elm.text", wd->format);
101    edje_object_part_text_set(wd->lbl, "elm.text", wd->label);
102    edje_object_scale_set(wd->lbl, elm_widget_scale_get(obj) *
103                          _elm_config->scale);
104    _label_sliding_change(obj);
105    _sizing_eval(obj);
106    evas_event_thaw(evas_object_evas_get(obj));
107    evas_event_thaw_eval(evas_object_evas_get(obj));
108 }
109
110 static void
111 _sizing_eval(Evas_Object *obj)
112 {
113    Widget_Data *wd = elm_widget_data_get(obj);
114    Evas_Coord minw = -1, minh = -1;
115    Evas_Coord resw, resh;
116    if (!wd) return;
117
118    if (wd->linewrap)
119      {
120         evas_object_geometry_get(wd->lbl, NULL, NULL, &resw, &resh);
121         if ((resw == wd->lastw) && (!wd->changed)) return;
122         wd->changed = EINA_FALSE;
123         wd->lastw = resw;
124         _elm_recalc_job(obj);
125         // FIXME: works ok. but NOT for genlist. what should genlist do?
126         //        if (wd->deferred_recalc_job) ecore_job_del(wd->deferred_recalc_job);
127         //        wd->deferred_recalc_job = ecore_job_add(_elm_recalc_job, obj);
128      }
129    else
130      {
131         evas_event_freeze(evas_object_evas_get(obj));
132         evas_object_geometry_get(wd->lbl, NULL, NULL, &resw, &resh);
133         edje_object_size_min_calc(wd->lbl, &minw, &minh);
134         if (wd->wrap_w > 0 && minw > wd->wrap_w) minw = wd->wrap_w;
135         evas_object_size_hint_min_set(obj, minw, minh);
136         evas_event_thaw(evas_object_evas_get(obj));
137         evas_event_thaw_eval(evas_object_evas_get(obj));
138      }
139 }
140
141 static void
142 _lbl_resize(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
143 {
144    Widget_Data *wd = elm_widget_data_get(data);
145    if (!wd) return;
146    if (wd->linewrap) _sizing_eval(data);
147 }
148
149 static int
150 _get_value_in_key_string(const char *oldstring, const char *key, char **value)
151 {
152    char *curlocater, *endtag;
153    int firstindex = 0, foundflag = -1;
154
155    curlocater = strstr(oldstring, key);
156    if (curlocater)
157      {
158         int key_len = strlen(key);
159         endtag = curlocater + key_len;
160         if ((!endtag) || (*endtag != '='))
161           {
162              foundflag = 0;
163              return -1;
164           }
165         firstindex = abs(oldstring - curlocater);
166         firstindex += key_len + 1; // strlen("key") + strlen("=")
167         *value = (char *)oldstring + firstindex;
168
169         foundflag = 1;
170      }
171    else
172      {
173         foundflag = 0;
174      }
175
176    if (foundflag == 1) return 0;
177
178    return -1;
179 }
180
181
182 static int
183 _strbuf_key_value_replace(Eina_Strbuf *srcbuf, const char *key, const char *value, int deleteflag)
184 {
185    char *kvalue;
186    const char *srcstring = NULL;
187
188    srcstring = eina_strbuf_string_get(srcbuf);
189
190    if (_get_value_in_key_string(srcstring, key, &kvalue) == 0)
191      {
192         const char *val_end;
193         int val_end_idx = 0;
194         int key_start_idx = 0;
195         val_end = strchr(kvalue, ' ');
196
197         if (val_end)
198            val_end_idx = val_end - srcstring;
199         else
200            val_end_idx = kvalue - srcstring + strlen(kvalue) - 1;
201
202         /* -1 is because of the '=' */
203         key_start_idx = kvalue - srcstring - 1 - strlen(key);
204         eina_strbuf_remove(srcbuf, key_start_idx, val_end_idx);
205         if (!deleteflag)
206           {
207              eina_strbuf_insert_printf(srcbuf, "%s=%s", key_start_idx, key,
208                    value);
209           }
210      }
211    else if (!deleteflag)
212      {
213         if (*srcstring)
214           {
215              /* -1 because we want it before the ' */
216              eina_strbuf_insert_printf(srcbuf, " %s=%s",
217                    eina_strbuf_length_get(srcbuf) - 1, key, value);
218           }
219         else
220           {
221              eina_strbuf_append_printf(srcbuf, "DEFAULT='%s=%s'", key, value);
222           }
223      }
224    return 0;
225 }
226
227 static int
228 _stringshare_key_value_replace(const char **srcstring, const char *key, const char *value, int deleteflag)
229 {
230    Eina_Strbuf *sharebuf = NULL;
231
232    sharebuf = eina_strbuf_new();
233    eina_strbuf_append(sharebuf, *srcstring);
234    _strbuf_key_value_replace(sharebuf, key, value, deleteflag);
235    eina_stringshare_del(*srcstring);
236    *srcstring = eina_stringshare_add(eina_strbuf_string_get(sharebuf));
237    eina_strbuf_free(sharebuf);
238
239    return 0;
240 }
241
242 static void
243 _label_sliding_change(Evas_Object *obj)
244 {
245    Widget_Data *wd = elm_widget_data_get(obj);
246    if (!wd) return;
247    char *plaintxt;
248    int plainlen = 0;
249
250    // dosen't support multiline sliding effect
251    if (wd->linewrap)
252      {
253         wd->slidingmode = EINA_FALSE;
254         return;
255      }
256
257    plaintxt = _elm_util_mkup_to_text(edje_object_part_text_get(wd->lbl, "elm.text"));
258    if (plaintxt != NULL)
259      {
260         plainlen = strlen(plaintxt);
261         free(plaintxt);
262      }
263    // too short to slide label
264    if (plainlen < 1)
265      {
266         wd->slidingmode = EINA_TRUE;
267         return;
268      }
269
270    if (wd->slidingmode)
271      {
272         Edje_Message_Float_Set *msg = alloca(sizeof(Edje_Message_Float_Set) + (sizeof(double)));
273
274         if (wd->ellipsis)
275           {
276              wd->slidingellipsis = EINA_TRUE;
277              elm_label_ellipsis_set(obj, EINA_FALSE);
278           }
279
280         msg->count = 1;
281         msg->val[0] = wd->slide_duration;
282
283         edje_object_message_send(wd->lbl, EDJE_MESSAGE_FLOAT_SET, 0, msg);
284         edje_object_signal_emit(wd->lbl, "elm,state,slide,start", "elm");
285      }
286    else
287      {
288         edje_object_signal_emit(wd->lbl, "elm,state,slide,stop", "elm");
289         if (wd->slidingellipsis)
290           {
291              wd->slidingellipsis = EINA_FALSE;
292              elm_label_ellipsis_set(obj, EINA_TRUE);
293           }
294      }
295 }
296
297 static void
298 _elm_label_label_set(Evas_Object *obj, const char *item, const char *label)
299 {
300    ELM_CHECK_WIDTYPE(obj, widtype);
301    Widget_Data *wd = elm_widget_data_get(obj);
302    if (!wd) return;
303    if (item && strcmp(item, "default")) return;
304    if (!label) label = "";
305    eina_stringshare_replace(&wd->label, label);
306    edje_object_part_text_style_user_set(wd->lbl, "elm.text",
307          wd->format);
308    edje_object_part_text_set(wd->lbl, "elm.text", wd->label);
309    wd->changed = 1;
310    _sizing_eval(obj);
311 }
312
313 static const char *
314 _elm_label_label_get(const Evas_Object *obj, const char *item)
315 {
316    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
317    Widget_Data *wd = elm_widget_data_get(obj);
318    if (item && strcmp(item, "default")) return NULL;
319    if (!wd) return NULL;
320    return wd->label;
321 }
322
323 static void
324 _translate_hook(Evas_Object *obj)
325 {
326    evas_object_smart_callback_call(obj, "language,changed", NULL);
327 }
328
329 EAPI Evas_Object *
330 elm_label_add(Evas_Object *parent)
331 {
332    Evas_Object *obj;
333    Evas *e;
334    Widget_Data *wd;
335
336    ELM_WIDGET_STANDARD_SETUP(wd, Widget_Data, parent, e, obj, NULL);
337
338    ELM_SET_WIDTYPE(widtype, "label");
339    elm_widget_type_set(obj, "label");
340    elm_widget_sub_object_add(parent, obj);
341    elm_widget_data_set(obj, wd);
342    elm_widget_del_hook_set(obj, _del_hook);
343    elm_widget_theme_hook_set(obj, _theme_hook);
344    elm_widget_can_focus_set(obj, EINA_FALSE);
345    elm_widget_text_set_hook_set(obj, _elm_label_label_set);
346    elm_widget_text_get_hook_set(obj, _elm_label_label_get);
347    elm_widget_translate_hook_set(obj, _translate_hook);
348
349    wd->linewrap = ELM_WRAP_NONE;
350    wd->ellipsis = EINA_FALSE;
351    wd->slidingmode = EINA_FALSE;
352    wd->slidingellipsis = EINA_FALSE;
353    wd->wrap_w = -1;
354    wd->slide_duration = 10;
355
356    wd->lbl = edje_object_add(e);
357    _elm_theme_object_set(obj, wd->lbl, "label", "base", "default");
358    wd->format = eina_stringshare_add("");
359    wd->label = eina_stringshare_add("<br>");
360    edje_object_part_text_style_user_set(wd->lbl, "elm.text",
361          wd->format);
362    edje_object_part_text_set(wd->lbl, "elm.text", wd->label);
363
364    elm_widget_resize_object_set(obj, wd->lbl);
365
366    evas_object_event_callback_add(wd->lbl, EVAS_CALLBACK_RESIZE, _lbl_resize, obj);
367
368    _mirrored_set(obj, elm_widget_mirrored_get(obj));
369    wd->changed = 1;
370    _sizing_eval(obj);
371    return obj;
372 }
373
374 EAPI void
375 elm_label_label_set(Evas_Object *obj, const char *label)
376 {
377   _elm_label_label_set(obj, NULL, label);
378 }
379
380 EAPI const char *
381 elm_label_label_get(const Evas_Object *obj)
382 {
383   return _elm_label_label_get(obj, NULL);
384 }
385
386 EAPI void
387 elm_label_line_wrap_set(Evas_Object *obj, Elm_Wrap_Type wrap)
388 {
389    ELM_CHECK_WIDTYPE(obj, widtype);
390    Widget_Data *wd = elm_widget_data_get(obj);
391    const char *wrap_str;
392    int len;
393
394    if (!wd) return;
395    if (wd->linewrap == wrap) return;
396    wd->linewrap = wrap;
397    len = strlen(wd->label);
398    if (len <= 0) return;
399
400    switch (wrap)
401      {
402       case ELM_WRAP_CHAR:
403          wrap_str = "char";
404          break;
405       case ELM_WRAP_WORD:
406          wrap_str = "word";
407          break;
408       case ELM_WRAP_MIXED:
409          wrap_str = "mixed";
410          break;
411       default:
412          wrap_str = "none";
413          break;
414      }
415
416    if (_stringshare_key_value_replace(&wd->format,
417             "wrap", wrap_str, 0) == 0)
418      {
419         edje_object_part_text_style_user_set(wd->lbl, "elm.text",
420               wd->format);
421         edje_object_part_text_set(wd->lbl, "elm.text", wd->label);
422         wd->changed = 1;
423         _sizing_eval(obj);
424      }
425 }
426
427 EAPI Elm_Wrap_Type
428 elm_label_line_wrap_get(const Evas_Object *obj)
429 {
430    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
431    Widget_Data *wd = elm_widget_data_get(obj);
432    if (!wd) return EINA_FALSE;
433    return wd->linewrap;
434 }
435
436 EAPI void
437 elm_label_wrap_width_set(Evas_Object *obj, Evas_Coord w)
438 {
439    ELM_CHECK_WIDTYPE(obj, widtype);
440    Widget_Data *wd = elm_widget_data_get(obj);
441    if (!wd) return;
442    if (w < 0) w = 0;
443    if (wd->wrap_w == w) return;
444    if (wd->ellipsis)
445      {
446         edje_object_part_text_style_user_set(wd->lbl, "elm.text",
447               wd->format);
448         edje_object_part_text_set(wd->lbl, "elm.text", wd->label);
449      }
450    wd->wrap_w = w;
451    _sizing_eval(obj);
452 }
453
454 EAPI Evas_Coord
455 elm_label_wrap_width_get(const Evas_Object *obj)
456 {
457    ELM_CHECK_WIDTYPE(obj, widtype) 0;
458    Widget_Data *wd = elm_widget_data_get(obj);
459    if (!wd) return 0;
460    return wd->wrap_w;
461 }
462
463 EINA_DEPRECATED EAPI void
464 elm_label_wrap_height_set(Evas_Object *obj __UNUSED__,
465                           Evas_Coord   h __UNUSED__)
466 {
467    return;
468 }
469
470 EINA_DEPRECATED EAPI Evas_Coord
471 elm_label_wrap_height_get(const Evas_Object *obj __UNUSED__)
472 {
473    return 0;
474 }
475
476 EINA_DEPRECATED EAPI void
477 elm_label_fontsize_set(Evas_Object *obj __UNUSED__,
478                        int fontsize __UNUSED__)
479 {
480    return;
481 }
482
483 EINA_DEPRECATED EAPI void
484 elm_label_text_align_set(Evas_Object *obj __UNUSED__,
485                          const char *alignmode __UNUSED__)
486 {
487    return;
488 }
489
490 EINA_DEPRECATED EAPI void
491 elm_label_text_color_set(Evas_Object *obj __UNUSED__,
492                          unsigned int r __UNUSED__,
493                          unsigned int g __UNUSED__,
494                          unsigned int b __UNUSED__,
495                          unsigned int a __UNUSED__)
496 {
497    return;
498 }
499
500 EINA_DEPRECATED EAPI void
501 elm_label_background_color_set(Evas_Object *obj __UNUSED__,
502                                unsigned int r __UNUSED__,
503                                unsigned int g __UNUSED__,
504                                unsigned int b __UNUSED__,
505                                unsigned int a __UNUSED__)
506 {
507    return;
508 }
509
510 EAPI void
511 elm_label_ellipsis_set(Evas_Object *obj, Eina_Bool ellipsis)
512 {
513    ELM_CHECK_WIDTYPE(obj, widtype);
514    Widget_Data *wd = elm_widget_data_get(obj);
515    Eina_Strbuf *fontbuf = NULL;
516    int len, removeflag = 0;
517
518    if (!wd) return;
519    if (wd->ellipsis == ellipsis) return;
520    wd->ellipsis = ellipsis;
521    len = strlen(wd->label);
522    if (len <= 0) return;
523
524    if (ellipsis == EINA_FALSE) removeflag = 1;  // remove fontsize tag
525
526    fontbuf = eina_strbuf_new();
527    eina_strbuf_append_printf(fontbuf, "%f", 1.0);
528
529    if (_stringshare_key_value_replace(&wd->format,
530             "ellipsis", eina_strbuf_string_get(fontbuf), removeflag) == 0)
531      {
532         edje_object_part_text_style_user_set(wd->lbl, "elm.text",
533               wd->format);
534         edje_object_part_text_set(wd->lbl, "elm.text", wd->label);
535         wd->changed = 1;
536         _sizing_eval(obj);
537      }
538    eina_strbuf_free(fontbuf);
539
540 }
541
542 EAPI Eina_Bool
543 elm_label_ellipsis_get(const Evas_Object *obj)
544 {
545    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
546    Widget_Data *wd = elm_widget_data_get(obj);
547    if (!wd) return EINA_FALSE;
548    return wd->ellipsis;
549 }
550
551 EAPI void
552 elm_label_slide_set(Evas_Object *obj,
553                     Eina_Bool    slide)
554 {
555    ELM_CHECK_WIDTYPE(obj, widtype);
556    Widget_Data *wd = elm_widget_data_get(obj);
557    if (!wd) return;
558
559    if (wd->slidingmode == slide) return;
560    wd->slidingmode = slide;
561    _label_sliding_change(obj);
562    wd->changed = 1;
563    _sizing_eval(obj);
564 }
565
566 EAPI Eina_Bool
567 elm_label_slide_get(const Evas_Object *obj)
568 {
569    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
570    Widget_Data *wd = elm_widget_data_get(obj);
571    if (!wd) return EINA_FALSE;
572    return wd->slidingmode;
573 }
574
575 EAPI void
576 elm_label_slide_duration_set(Evas_Object *obj, double duration)
577 {
578    ELM_CHECK_WIDTYPE(obj, widtype);
579    Widget_Data *wd = elm_widget_data_get(obj);
580    Edje_Message_Float_Set *msg = alloca(sizeof(Edje_Message_Float_Set) + (sizeof(double)));
581
582    if (!wd) return;
583    wd->slide_duration = duration;
584    msg->count = 1;
585    msg->val[0] = wd->slide_duration;
586    edje_object_message_send(wd->lbl, EDJE_MESSAGE_FLOAT_SET, 0, msg);
587 }
588
589 EAPI double
590 elm_label_slide_duration_get(const Evas_Object *obj)
591 {
592    ELM_CHECK_WIDTYPE(obj, widtype) 0.0;
593    Widget_Data *wd = elm_widget_data_get(obj);
594    if (!wd) return 0;
595    return wd->slide_duration;
596 }
597