[access] call a callback function with information
[framework/uifw/elementary.git] / src / lib / elm_label.c
1 #include <Elementary.h>
2 #include "elm_priv.h"
3 #include "elm_widget_label.h"
4
5 EAPI const char ELM_LABEL_SMART_NAME[] = "elm_label";
6
7 static const char SIG_SLIDE_END[] = "slide,end";
8 static const char SIG_LANGUAGE_CHANGED[] = "language,changed";
9
10 static const Evas_Smart_Cb_Description _smart_callbacks[] = {
11    {SIG_LANGUAGE_CHANGED, ""},
12    {SIG_SLIDE_END, ""},
13    {NULL, NULL}
14 };
15
16 static const Elm_Layout_Part_Alias_Description _text_aliases[] =
17 {
18    {"default", "elm.text"},
19    {NULL, NULL}
20 };
21
22 EVAS_SMART_SUBCLASS_NEW
23   (ELM_LABEL_SMART_NAME, _elm_label, Elm_Label_Smart_Class,
24   Elm_Layout_Smart_Class, elm_layout_smart_class_get, NULL);
25
26 static void
27 _recalc(void *data)
28 {
29    ELM_LABEL_DATA_GET(data, sd);
30
31    Evas_Coord minw = -1, minh = -1;
32    Evas_Coord resw;
33
34    evas_event_freeze(evas_object_evas_get(data));
35    evas_object_geometry_get
36      (ELM_WIDGET_DATA(sd)->resize_obj, NULL, NULL, &resw, NULL);
37    if (sd->wrap_w > resw)
38      resw = sd->wrap_w;
39
40    edje_object_size_min_restricted_calc
41      (ELM_WIDGET_DATA(sd)->resize_obj, &minw, &minh, resw, 0);
42
43    /* This is a hack to workaround the way min size hints are treated.
44     * If the minimum width is smaller than the restricted width, it means
45     * the mininmum doesn't matter. */
46    if ((minw <= resw) && (minw != sd->wrap_w))
47      {
48         Evas_Coord ominw = -1;
49
50         evas_object_size_hint_min_get(data, &ominw, NULL);
51         minw = ominw;
52      }
53
54    evas_object_size_hint_min_set(data, minw, minh);
55    evas_event_thaw(evas_object_evas_get(data));
56    evas_event_thaw_eval(evas_object_evas_get(data));
57 }
58
59 static void
60 _label_format_set(Evas_Object *obj,
61                   const char *format)
62 {
63    if (format)
64      edje_object_part_text_style_user_push(obj, "elm.text", format);
65    else
66      edje_object_part_text_style_user_pop(obj, "elm.text");
67 }
68
69 static void
70 _label_slide_change(Evas_Object *obj)
71 {
72    Evas_Object *tb;
73    char *plaintxt;
74    int plainlen = 0;
75
76    ELM_LABEL_DATA_GET(obj, sd);
77
78    edje_object_signal_emit(ELM_WIDGET_DATA(sd)->resize_obj,
79                            "elm,state,slide,stop", "elm");
80
81    //doesn't support multiline slide effect
82    if (sd->linewrap)
83      {
84         WRN("Doesn't support slide effect for multiline! : label=%p", obj);
85         return;
86      }
87
88    //stop if the text is none.
89    plaintxt = _elm_util_mkup_to_text
90        (edje_object_part_text_get
91          (ELM_WIDGET_DATA(sd)->resize_obj, "elm.text"));
92    if (plaintxt)
93      {
94         plainlen = strlen(plaintxt);
95         free(plaintxt);
96      }
97    if (plainlen < 1) return;
98
99    //has slide effect.
100    if (sd->slide_mode != ELM_LABEL_SLIDE_MODE_NONE)
101      {
102         if (sd->ellipsis)
103           {
104              sd->slide_ellipsis = EINA_TRUE;
105              elm_label_ellipsis_set(obj, EINA_FALSE);
106           }
107
108         //slide only if the slide area is smaller than text width size.
109         if (sd->slide_mode == ELM_LABEL_SLIDE_MODE_AUTO)
110           {
111              tb = (Evas_Object *) edje_object_part_object_get(ELM_WIDGET_DATA(sd)->resize_obj, "elm.text");
112              if (tb)
113                {
114                   Evas_Coord w, tb_w;
115
116                   evas_object_textblock_size_formatted_get(tb, &tb_w, NULL);
117                   evas_object_geometry_get(ELM_WIDGET_DATA(sd)->resize_obj,
118                                            NULL, NULL, &w, NULL);
119                   if ((tb_w > 0) && (tb_w < w))
120                     {
121                        if (sd->slide_ellipsis)
122                          {
123                             sd->slide_ellipsis = EINA_FALSE;
124                             elm_label_ellipsis_set(obj, EINA_TRUE);
125                          }
126                        return;
127                     }
128                }
129           }
130         Edje_Message_Float_Set *msg =
131           alloca(sizeof(Edje_Message_Float_Set) + (sizeof(double)));
132
133         msg->count = 1;
134         msg->val[0] = sd->slide_duration;
135
136         edje_object_message_send
137            (ELM_WIDGET_DATA(sd)->resize_obj, EDJE_MESSAGE_FLOAT_SET, 0, msg);
138         edje_object_signal_emit(ELM_WIDGET_DATA(sd)->resize_obj, "elm,state,slide,start", "elm");
139      }
140    //no slide effect.
141    else
142      {
143         if (sd->slide_ellipsis)
144           {
145              sd->slide_ellipsis = EINA_FALSE;
146              elm_label_ellipsis_set(obj, EINA_TRUE);
147           }
148      }
149 }
150
151 static Eina_Bool
152 _elm_label_smart_theme(Evas_Object *obj)
153 {
154    Eina_Bool ret;
155
156    ELM_LABEL_DATA_GET(obj, sd);
157
158    evas_event_freeze(evas_object_evas_get(obj));
159
160    ret = ELM_WIDGET_CLASS(_elm_label_parent_sc)->theme(obj);
161    if (!ret) goto end;
162
163    _label_format_set(ELM_WIDGET_DATA(sd)->resize_obj, sd->format);
164    _label_slide_change(obj);
165
166 end:
167    evas_event_thaw(evas_object_evas_get(obj));
168    evas_event_thaw_eval(evas_object_evas_get(obj));
169
170    return ret;
171 }
172
173 static void
174 _elm_label_smart_sizing_eval(Evas_Object *obj)
175 {
176    Evas_Coord minw = -1, minh = -1;
177    Evas_Coord resw, resh;
178
179    ELM_LABEL_DATA_GET(obj, sd);
180
181    if (sd->linewrap)
182      {
183         evas_object_geometry_get
184           (ELM_WIDGET_DATA(sd)->resize_obj, NULL, NULL, &resw, &resh);
185         if (resw == sd->lastw) return;
186         sd->lastw = resw;
187         _recalc(obj);
188      }
189    else
190      {
191         evas_event_freeze(evas_object_evas_get(obj));
192         evas_object_geometry_get
193           (ELM_WIDGET_DATA(sd)->resize_obj, NULL, NULL, &resw, &resh);
194         edje_object_size_min_calc
195           (ELM_WIDGET_DATA(sd)->resize_obj, &minw, &minh);
196         if (sd->wrap_w > 0 && minw > sd->wrap_w) minw = sd->wrap_w;
197         evas_object_size_hint_min_set(obj, minw, minh);
198         evas_event_thaw(evas_object_evas_get(obj));
199         evas_event_thaw_eval(evas_object_evas_get(obj));
200      }
201 }
202
203 static void
204 _on_label_resize(void *data,
205                  Evas *e __UNUSED__,
206                  Evas_Object *obj __UNUSED__,
207                  void *event_info __UNUSED__)
208 {
209    ELM_LABEL_DATA_GET(data, sd);
210
211    if (sd->linewrap) elm_layout_sizing_eval(data);
212 }
213
214 static int
215 _get_value_in_key_string(const char *oldstring,
216                          const char *key,
217                          char **value)
218 {
219    char *curlocater, *endtag;
220    int firstindex = 0, foundflag = -1;
221
222    curlocater = strstr(oldstring, key);
223    if (curlocater)
224      {
225         int key_len = strlen(key);
226         endtag = curlocater + key_len;
227         if ((!endtag) || (*endtag != '='))
228           {
229              foundflag = 0;
230              return -1;
231           }
232         firstindex = abs(oldstring - curlocater);
233         firstindex += key_len + 1; // strlen("key") + strlen("=")
234         *value = (char *)oldstring + firstindex;
235
236         foundflag = 1;
237      }
238    else
239      {
240         foundflag = 0;
241      }
242
243    if (foundflag == 1) return 0;
244
245    return -1;
246 }
247
248 static int
249 _strbuf_key_value_replace(Eina_Strbuf *srcbuf,
250                           const char *key,
251                           const char *value,
252                           int deleteflag)
253 {
254    char *kvalue;
255    const char *srcstring = NULL;
256
257    srcstring = eina_strbuf_string_get(srcbuf);
258
259    if (_get_value_in_key_string(srcstring, key, &kvalue) == 0)
260      {
261         const char *val_end;
262         int val_end_idx = 0;
263         int key_start_idx = 0;
264         val_end = strchr(kvalue, ' ');
265
266         if (val_end)
267           val_end_idx = val_end - srcstring;
268         else
269           val_end_idx = kvalue - srcstring + strlen(kvalue) - 1;
270
271         /* -1 is because of the '=' */
272         key_start_idx = kvalue - srcstring - 1 - strlen(key);
273         eina_strbuf_remove(srcbuf, key_start_idx, val_end_idx);
274         if (!deleteflag)
275           {
276              eina_strbuf_insert_printf(srcbuf, "%s=%s", key_start_idx, key,
277                                        value);
278           }
279      }
280    else if (!deleteflag)
281      {
282         if (*srcstring)
283           {
284              /* -1 because we want it before the ' */
285              eina_strbuf_insert_printf
286                (srcbuf, " %s=%s", eina_strbuf_length_get(srcbuf) - 1, key,
287                value);
288           }
289         else
290           {
291              eina_strbuf_append_printf(srcbuf, "DEFAULT='%s=%s'", key, value);
292           }
293      }
294
295    return 0;
296 }
297
298 static int
299 _stringshare_key_value_replace(const char **srcstring,
300                                const char *key,
301                                const char *value,
302                                int deleteflag)
303 {
304    Eina_Strbuf *sharebuf = NULL;
305
306    sharebuf = eina_strbuf_new();
307    eina_strbuf_append(sharebuf, *srcstring);
308    _strbuf_key_value_replace(sharebuf, key, value, deleteflag);
309    eina_stringshare_del(*srcstring);
310    *srcstring = eina_stringshare_add(eina_strbuf_string_get(sharebuf));
311    eina_strbuf_free(sharebuf);
312
313    return 0;
314 }
315
316 static Eina_Bool
317 _elm_label_smart_text_set(Evas_Object *obj,
318                           const char *item,
319                           const char *label)
320 {
321    ELM_LABEL_DATA_GET(obj, sd);
322
323    if (!label) label = "";
324    _label_format_set(ELM_WIDGET_DATA(sd)->resize_obj, sd->format);
325
326    if (_elm_label_parent_sc->text_set(obj, item, label))
327      {
328         sd->lastw = 0;
329         _elm_label_smart_sizing_eval(obj);
330         return EINA_TRUE;
331      }
332
333    return EINA_FALSE;
334 }
335
336 static Eina_Bool
337 _elm_label_smart_translate(Evas_Object *obj)
338 {
339    evas_object_smart_callback_call(obj, SIG_LANGUAGE_CHANGED, NULL);
340
341    return EINA_TRUE;
342 }
343
344 static char *
345 _access_info_cb(void *data __UNUSED__, Evas_Object *obj)
346 {
347    const char *txt = elm_widget_access_info_get(obj);
348
349    if (!txt) txt = _elm_util_mkup_to_text(elm_layout_text_get(obj, NULL));
350    if (txt) return strdup(txt);
351
352    return NULL;
353 }
354
355 static void
356 _on_slide_end(void *data, Evas_Object *obj __UNUSED__,
357               const char *emission __UNUSED__, const char *source __UNUSED__)
358 {
359    ELM_LABEL_DATA_GET(data, sd);
360    if (sd->slide_ellipsis)
361      elm_label_ellipsis_set(data, EINA_TRUE);
362
363    evas_object_smart_callback_call(data, SIG_SLIDE_END, NULL);
364 }
365
366 static void
367 _elm_label_smart_add(Evas_Object *obj)
368 {
369    EVAS_SMART_DATA_ALLOC(obj, Elm_Label_Smart_Data);
370
371    ELM_WIDGET_CLASS(_elm_label_parent_sc)->base.add(obj);
372
373    priv->linewrap = ELM_WRAP_NONE;
374    priv->wrap_w = -1;
375    priv->slide_duration = 10;
376
377    priv->format = eina_stringshare_add("");
378    _label_format_set(ELM_WIDGET_DATA(priv)->resize_obj, priv->format);
379
380    evas_object_event_callback_add
381      (ELM_WIDGET_DATA(priv)->resize_obj, EVAS_CALLBACK_RESIZE,
382      _on_label_resize, obj);
383
384    edje_object_signal_callback_add(ELM_WIDGET_DATA(priv)->resize_obj,
385                                    "elm,state,slide,end", "", _on_slide_end,
386                                    obj);
387    /* access */
388    elm_widget_can_focus_set(obj, _elm_config->access_mode);
389
390    _elm_access_object_register(obj, ELM_WIDGET_DATA(priv)->resize_obj);
391    _elm_access_text_set
392      (_elm_access_object_get(obj), ELM_ACCESS_TYPE, E_("Label"));
393    _elm_access_callback_set
394      (_elm_access_object_get(obj), ELM_ACCESS_INFO, _access_info_cb, NULL);
395
396    elm_layout_theme_set(obj, "label", "base", elm_widget_style_get(obj));
397    elm_layout_text_set(obj, NULL, "<br>");
398    elm_layout_sizing_eval(obj);
399 }
400
401 static void
402 _elm_label_smart_set_user(Elm_Label_Smart_Class *sc)
403 {
404    ELM_WIDGET_CLASS(sc)->base.add = _elm_label_smart_add;
405
406    /* not a 'focus chain manager' */
407    ELM_WIDGET_CLASS(sc)->focus_next = NULL;
408    ELM_WIDGET_CLASS(sc)->focus_direction = NULL;
409
410    ELM_WIDGET_CLASS(sc)->theme = _elm_label_smart_theme;
411    ELM_WIDGET_CLASS(sc)->translate = _elm_label_smart_translate;
412
413    ELM_LAYOUT_CLASS(sc)->sizing_eval = _elm_label_smart_sizing_eval;
414    ELM_LAYOUT_CLASS(sc)->text_set = _elm_label_smart_text_set;
415
416    ELM_LAYOUT_CLASS(sc)->text_aliases = _text_aliases;
417 }
418
419 EAPI const Elm_Label_Smart_Class *
420 elm_label_smart_class_get(void)
421 {
422    static Elm_Label_Smart_Class _sc =
423      ELM_LABEL_SMART_CLASS_INIT_NAME_VERSION(ELM_LABEL_SMART_NAME);
424    static const Elm_Label_Smart_Class *class = NULL;
425
426    if (class)
427      return class;
428
429    _elm_label_smart_set(&_sc);
430    class = &_sc;
431
432    return class;
433 }
434
435 EAPI Evas_Object *
436 elm_label_add(Evas_Object *parent)
437 {
438    Evas_Object *obj;
439
440    EINA_SAFETY_ON_NULL_RETURN_VAL(parent, NULL);
441
442    obj = elm_widget_add(_elm_label_smart_class_new(), parent);
443    if (!obj) return NULL;
444
445    if (!elm_widget_sub_object_add(parent, obj))
446      ERR("could not add %p as sub object of %p", obj, parent);
447
448    _elm_widget_orient_signal_emit(obj);
449
450    return obj;
451 }
452
453 EAPI void
454 elm_label_line_wrap_set(Evas_Object *obj,
455                         Elm_Wrap_Type wrap)
456 {
457    const char *wrap_str, *text;
458    int len;
459
460    ELM_LABEL_CHECK(obj);
461    ELM_LABEL_DATA_GET(obj, sd);
462
463    if (sd->linewrap == wrap) return;
464
465    sd->linewrap = wrap;
466    text = elm_layout_text_get(obj, NULL);
467    if (!text) return;
468
469    len = strlen(text);
470    if (len <= 0) return;
471
472    switch (wrap)
473      {
474       case ELM_WRAP_CHAR:
475         wrap_str = "char";
476         break;
477
478       case ELM_WRAP_WORD:
479         wrap_str = "word";
480         break;
481
482       case ELM_WRAP_MIXED:
483         wrap_str = "mixed";
484         break;
485
486       default:
487         wrap_str = "none";
488         break;
489      }
490
491    if (_stringshare_key_value_replace(&sd->format, "wrap", wrap_str, 0) == 0)
492      {
493         _label_format_set(ELM_WIDGET_DATA(sd)->resize_obj, sd->format);
494         elm_layout_sizing_eval(obj);
495      }
496 }
497
498 EAPI Elm_Wrap_Type
499 elm_label_line_wrap_get(const Evas_Object *obj)
500 {
501    ELM_LABEL_CHECK(obj) EINA_FALSE;
502    ELM_LABEL_DATA_GET(obj, sd);
503
504    return sd->linewrap;
505 }
506
507 EAPI void
508 elm_label_wrap_width_set(Evas_Object *obj,
509                          Evas_Coord w)
510 {
511    ELM_LABEL_CHECK(obj);
512    ELM_LABEL_DATA_GET(obj, sd);
513
514    if (w < 0) w = 0;
515
516    if (sd->wrap_w == w) return;
517
518    if (sd->ellipsis)
519      _label_format_set(ELM_WIDGET_DATA(sd)->resize_obj, sd->format);
520    sd->wrap_w = w;
521
522    elm_layout_sizing_eval(obj);
523 }
524
525 EAPI Evas_Coord
526 elm_label_wrap_width_get(const Evas_Object *obj)
527 {
528    ELM_LABEL_CHECK(obj) 0;
529    ELM_LABEL_DATA_GET(obj, sd);
530
531    return sd->wrap_w;
532 }
533
534 EAPI void
535 elm_label_ellipsis_set(Evas_Object *obj,
536                        Eina_Bool ellipsis)
537 {
538    Eina_Strbuf *fontbuf = NULL;
539    int len, removeflag = 0;
540    const char *text;
541
542    ELM_LABEL_CHECK(obj);
543    ELM_LABEL_DATA_GET(obj, sd);
544
545    if (sd->ellipsis == ellipsis) return;
546    sd->ellipsis = ellipsis;
547
548    text = elm_layout_text_get(obj, NULL);
549    if (!text) return;
550
551    len = strlen(text);
552    if (len <= 0) return;
553
554    if (ellipsis == EINA_FALSE) removeflag = 1;  // remove fontsize tag
555
556    fontbuf = eina_strbuf_new();
557    eina_strbuf_append_printf(fontbuf, "%f", 1.0);
558
559    if (_stringshare_key_value_replace
560          (&sd->format, "ellipsis", eina_strbuf_string_get
561            (fontbuf), removeflag) == 0)
562      {
563         _label_format_set(ELM_WIDGET_DATA(sd)->resize_obj, sd->format);
564         elm_layout_sizing_eval(obj);
565      }
566    eina_strbuf_free(fontbuf);
567 }
568
569 EAPI Eina_Bool
570 elm_label_ellipsis_get(const Evas_Object *obj)
571 {
572    ELM_LABEL_CHECK(obj) EINA_FALSE;
573    ELM_LABEL_DATA_GET(obj, sd);
574
575    return sd->ellipsis;
576 }
577
578 EAPI void
579 elm_label_slide_mode_set(Evas_Object *obj, Elm_Label_Slide_Mode mode)
580 {
581    ELM_LABEL_CHECK(obj);
582    ELM_LABEL_DATA_GET(obj, sd);
583    sd->slide_mode = mode;
584 }
585
586 EAPI Elm_Label_Slide_Mode
587 elm_label_slide_mode_get(const Evas_Object *obj)
588 {
589    ELM_LABEL_CHECK(obj) ELM_LABEL_SLIDE_MODE_NONE;
590    ELM_LABEL_DATA_GET(obj, sd);
591    return sd->slide_mode;
592 }
593
594 EINA_DEPRECATED EAPI void
595 elm_label_slide_set(Evas_Object *obj,
596                     Eina_Bool slide)
597 {
598    if (slide)
599      elm_label_slide_mode_set(obj, ELM_LABEL_SLIDE_MODE_ALWAYS);
600    else
601      elm_label_slide_mode_set(obj, ELM_LABEL_SLIDE_MODE_NONE);
602 }
603
604 EINA_DEPRECATED EAPI Eina_Bool
605 elm_label_slide_get(const Evas_Object *obj)
606 {
607    Eina_Bool ret = EINA_FALSE;
608    if (elm_label_slide_mode_get(obj) == ELM_LABEL_SLIDE_MODE_ALWAYS)
609      ret = EINA_TRUE;
610    return ret;
611 }
612
613 EAPI void
614 elm_label_slide_go(Evas_Object *obj)
615 {
616    ELM_LABEL_CHECK(obj);
617    _label_slide_change(obj);
618    elm_layout_sizing_eval(obj);
619 }
620
621 EAPI void
622 elm_label_slide_duration_set(Evas_Object *obj, double duration)
623 {
624    ELM_LABEL_CHECK(obj);
625    ELM_LABEL_DATA_GET(obj, sd);
626
627    sd->slide_duration = duration;
628 }
629
630 EAPI double
631 elm_label_slide_duration_get(const Evas_Object *obj)
632 {
633    ELM_LABEL_CHECK(obj) 0.0;
634    ELM_LABEL_DATA_GET(obj, sd);
635
636    return sd->slide_duration;
637 }