elm progressbar: add the elm_object_text_set/get hooks.
[framework/uifw/elementary.git] / src / lib / elm_progressbar.c
1 #include <Elementary.h>
2 #include "elm_priv.h"
3
4 /**
5  * @defgroup Progressbar Progressbar
6  *
7  * The progressbar adds a widget for representing current progress
8  * of a job status
9  *
10  * A progressbar can be horizontal or vertical. It can contain an Icon and has a
11  * primary label as well as a units label (that is formatted with floating
12  * point values and thus accepts a printf-style format string, like
13  * “%1.2f units”.
14  *
15  *  Label, Icon and Unit strings/objects are optional.
16  *
17  * A progressbar may be inverted which means values invert, with high vales being
18  * on the left or top and low values on the right or bottom (as opposed to
19  * normally being low on the left or top and high on the bottom and right).
20  *
21  * The span of the progressbar is its length (horizontally or vertically).
22  * This will be scaled by the object or applications scaling factor. At any point
23  * code can query the progressbar for its value with elm_progressbar_value_get().
24  */
25
26 #define MIN_RATIO_LVL 0.0
27 #define MAX_RATIO_LVL 1.0
28
29 typedef struct _Widget_Data Widget_Data;
30
31 struct _Widget_Data
32 {
33    Evas_Object *progressbar;
34    Evas_Object *spacer;
35    Evas_Object *icon;
36    Evas_Coord size;
37    Eina_Bool horizontal : 1;
38    Eina_Bool inverted : 1;
39    Eina_Bool pulse : 1;
40    Eina_Bool pulse_state : 1;
41    const char *units;
42    const char *label;
43    double val;
44 };
45
46 static const char *widtype = NULL;
47 static void _del_hook(Evas_Object *obj);
48 static void _mirrored_set(Evas_Object *obj, Eina_Bool rtl);
49 static void _theme_hook(Evas_Object *obj);
50 static void _sizing_eval(Evas_Object *obj);
51 static void _changed_size_hints(void *data, Evas *e, Evas_Object *obj, void *event_info);
52 static void _sub_del(void *data, Evas_Object *obj, void *event_info);
53 static void _units_set(Evas_Object *obj);
54 static void _val_set(Evas_Object *obj);
55
56 static void
57 _del_hook(Evas_Object *obj)
58 {
59    Widget_Data *wd = elm_widget_data_get(obj);
60    if (!wd) return;
61    if (wd->label) eina_stringshare_del(wd->label);
62    if (wd->units) eina_stringshare_del(wd->units);
63    free(wd);
64 }
65
66 static void
67 _mirrored_set(Evas_Object *obj, Eina_Bool rtl)
68 {
69    Widget_Data *wd = elm_widget_data_get(obj);
70    if (!wd) return;
71    edje_object_mirrored_set(wd->progressbar, rtl);
72 }
73
74 static void
75 _theme_hook(Evas_Object *obj)
76 {
77    Widget_Data *wd = elm_widget_data_get(obj);
78    if (!wd) return;
79    _elm_widget_mirrored_reload(obj);
80    _mirrored_set(obj, elm_widget_mirrored_get(obj));
81    if (wd->horizontal)
82      _elm_theme_object_set(obj, wd->progressbar, "progressbar", "horizontal", elm_widget_style_get(obj));
83    else
84      _elm_theme_object_set(obj, wd->progressbar, "progressbar", "vertical", elm_widget_style_get(obj));
85
86    if (wd->icon)
87      {
88         edje_object_part_swallow(wd->progressbar, "elm.swallow.content", wd->icon);
89         edje_object_signal_emit(wd->progressbar, "elm,state,icon,visible", "elm");
90      }
91    if (wd->label)
92      {
93         edje_object_part_text_set(wd->progressbar, "elm.text", wd->label);
94         edje_object_signal_emit(wd->progressbar, "elm,state,text,visible", "elm");
95      }
96    if (wd->pulse)
97      edje_object_signal_emit(wd->progressbar, "elm,state,pulse", "elm");
98    else
99      edje_object_signal_emit(wd->progressbar, "elm,state,fraction", "elm");
100    if (wd->pulse_state)
101      edje_object_signal_emit(wd->progressbar, "elm,state,pulse,start", "elm");
102
103    if ((wd->units) && (!wd->pulse))
104      edje_object_signal_emit(wd->progressbar, "elm,state,units,visible", "elm");
105
106    if (wd->horizontal)
107      evas_object_size_hint_min_set(wd->spacer, (double)wd->size * elm_widget_scale_get(obj) * _elm_config->scale, 1);
108    else
109      evas_object_size_hint_min_set(wd->spacer, 1, (double)wd->size * elm_widget_scale_get(obj) * _elm_config->scale);
110
111    edje_object_part_swallow(wd->progressbar, "elm.swallow.bar", wd->spacer);
112
113    if (wd->inverted)
114      edje_object_signal_emit(wd->progressbar, "elm,state,inverted,on", "elm");
115
116    _units_set(obj);
117    edje_object_message_signal_process(wd->progressbar);
118    edje_object_scale_set(wd->progressbar, elm_widget_scale_get(obj) * _elm_config->scale);
119    _val_set(obj);
120    _sizing_eval(obj);
121 }
122
123 static void
124 _sizing_eval(Evas_Object *obj)
125 {
126    Widget_Data *wd = elm_widget_data_get(obj);
127    Evas_Coord minw = -1, minh = -1;
128    if (!wd) return;
129    edje_object_size_min_restricted_calc(wd->progressbar, &minw, &minh, minw, minh);
130    evas_object_size_hint_min_set(obj, minw, minh);
131    evas_object_size_hint_max_set(obj, -1, -1);
132 }
133
134 static void
135 _changed_size_hints(void *data, Evas *e __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__)
136 {
137    Widget_Data *wd = elm_widget_data_get(data);
138    if (!wd) return;
139    if (obj != wd->icon) return;
140    _sizing_eval(data);
141 }
142
143 static void
144 _sub_del(void *data __UNUSED__, Evas_Object *obj, void *event_info)
145 {
146    Widget_Data *wd = elm_widget_data_get(obj);
147    Evas_Object *sub = event_info;
148    if (!wd) return;
149    if (sub == wd->icon)
150      {
151         edje_object_signal_emit(wd->progressbar, "elm,state,icon,hidden", "elm");
152         evas_object_event_callback_del_full
153            (sub, EVAS_CALLBACK_CHANGED_SIZE_HINTS, _changed_size_hints, obj);
154         wd->icon = NULL;
155         edje_object_message_signal_process(wd->progressbar);
156         _sizing_eval(obj);
157      }
158 }
159
160 static void
161 _val_set(Evas_Object *obj)
162 {
163    Widget_Data *wd = elm_widget_data_get(obj);
164    Eina_Bool rtl;
165    double pos;
166    if (!wd) return;
167    pos = wd->val;
168    rtl = elm_widget_mirrored_get(obj);
169    if ((!rtl && wd->inverted) || (rtl &&
170                                   ((!wd->horizontal && wd->inverted) ||
171                                    (wd->horizontal && !wd->inverted)))) pos = MAX_RATIO_LVL - pos;
172    edje_object_part_drag_value_set(wd->progressbar, "elm.cur.progressbar", pos, pos);
173 }
174
175 static void
176 _units_set(Evas_Object *obj)
177 {
178    Widget_Data *wd = elm_widget_data_get(obj);
179    if (!wd) return;
180    if (wd->units)
181      {
182         char buf[1024];
183         snprintf(buf, sizeof(buf), wd->units, 100 * wd->val);
184         edje_object_part_text_set(wd->progressbar, "elm.text.status", buf);
185      }
186    else
187      edje_object_part_text_set(wd->progressbar, "elm.text.status", NULL);
188 }
189
190 static void
191 _elm_progressbar_label_set(Evas_Object *obj, const char *item, const char *label)
192 {
193    ELM_CHECK_WIDTYPE(obj, widtype);
194    Widget_Data *wd = elm_widget_data_get(obj);
195    if (item) return;
196    if (!wd) return;
197    eina_stringshare_replace(&wd->label, label);
198    if (label)
199      {
200         edje_object_signal_emit(wd->progressbar, "elm,state,text,visible", "elm");
201         edje_object_message_signal_process(wd->progressbar);
202      }
203    else
204      {
205         edje_object_signal_emit(wd->progressbar, "elm,state,text,hidden", "elm");
206         edje_object_message_signal_process(wd->progressbar);
207      }
208    edje_object_part_text_set(wd->progressbar, "elm.text", label);
209    _sizing_eval(obj);
210 }
211
212 static const char *
213 _elm_progressbar_label_get(const Evas_Object *obj, const char *item)
214 {
215    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
216    Widget_Data *wd = elm_widget_data_get(obj);
217    if (item) return NULL;
218    if (!wd) return NULL;
219    return wd->label;
220 }
221
222 /**
223  * Add a new progressbar to the parent
224  *
225  * @param parent The parent object
226  * @return The new object or NULL if it cannot be created
227  *
228  * @ingroup Progressbar
229  */
230 EAPI Evas_Object *
231 elm_progressbar_add(Evas_Object *parent)
232 {
233    Evas_Object *obj;
234    Evas *e;
235    Widget_Data *wd;
236
237    ELM_WIDGET_STANDARD_SETUP(wd, Widget_Data, parent, e, obj, NULL);
238
239    ELM_SET_WIDTYPE(widtype, "progressbar");
240    elm_widget_type_set(obj, "progressbar");
241    elm_widget_sub_object_add(parent, obj);
242    elm_widget_data_set(obj, wd);
243    elm_widget_del_hook_set(obj, _del_hook);
244    elm_widget_theme_hook_set(obj, _theme_hook);
245    elm_widget_can_focus_set(obj, EINA_FALSE);
246    elm_widget_text_set_hook_set(obj, _elm_progressbar_label_set);
247    elm_widget_text_get_hook_set(obj, _elm_progressbar_label_get);
248
249    wd->horizontal = EINA_TRUE;
250    wd->inverted = EINA_FALSE;
251    wd->pulse = EINA_FALSE;
252    wd->pulse_state = EINA_FALSE;
253    wd->units = eina_stringshare_add("%.0f %%");
254    wd->val = MIN_RATIO_LVL;
255
256    wd->progressbar = edje_object_add(e);
257    _elm_theme_object_set(obj, wd->progressbar, "progressbar", "horizontal", "default");
258    elm_widget_resize_object_set(obj, wd->progressbar);
259
260    wd->spacer = evas_object_rectangle_add(e);
261    evas_object_color_set(wd->spacer, 0, 0, 0, 0);
262    evas_object_pass_events_set(wd->spacer, EINA_TRUE);
263    elm_widget_sub_object_add(obj, wd->spacer);
264    edje_object_part_swallow(wd->progressbar, "elm.swallow.bar", wd->spacer);
265
266    evas_object_smart_callback_add(obj, "sub-object-del", _sub_del, obj);
267    _units_set(obj);
268    _val_set(obj);
269    _mirrored_set(obj, elm_widget_mirrored_get(obj));
270    _sizing_eval(obj);
271    return obj;
272 }
273
274 /**
275  * Normally the progressbar will display and interpret values from low to high.
276  * This display a progressbar for jobs with unknow state of progression,
277  * (the cursor pulse right to left and left to right, and loop) if pulse is set to 1.
278  *
279  * @param obj The progressbar object
280  * @param pulse The pulse flag. 1 == pulse, 0 == normal
281  *
282  * @ingroup Progressbar
283  */
284 EAPI void
285 elm_progressbar_pulse_set(Evas_Object *obj, Eina_Bool pulse)
286 {
287    ELM_CHECK_WIDTYPE(obj, widtype);
288    Widget_Data *wd = elm_widget_data_get(obj);
289    if (!wd) return;
290    pulse = !!pulse;
291    if (wd->pulse == pulse) return;
292    wd->pulse = pulse;
293    _theme_hook(obj);
294 }
295
296 /**
297  * Normally the progressbar will display and interpret values from low to high.
298  * This display a progressbar for jobs with unknow state of progression,
299  * (the cursor pulse right to left and left to right, and loop) if pulse is set to 1.
300  *
301  * @param obj The progressbar object
302  * @return The pulse flag
303  * (1 == pulse, 0 == normal)
304  *
305  * @ingroup Progressbar
306  */
307 EAPI Eina_Bool
308 elm_progressbar_pulse_get(const Evas_Object *obj)
309 {
310    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
311    Widget_Data *wd = elm_widget_data_get(obj);
312    if (!wd) return EINA_FALSE;
313    return wd->pulse;
314 }
315
316 /**
317  * Stat/Stop de pulse action
318  *
319  * @param obj The progressbar object
320  * @param state The pulse flag. 1 == start pulse, 0 == stop pulse
321  *
322  * @ingroup Progressbar
323  */
324 EAPI void
325 elm_progressbar_pulse(Evas_Object *obj, Eina_Bool state)
326 {
327    ELM_CHECK_WIDTYPE(obj, widtype);
328    Widget_Data *wd = elm_widget_data_get(obj);
329    if (!wd) return;
330    state = !!state;
331    if ((!wd->pulse) && (wd->pulse_state == state)) return;
332    wd->pulse_state = state;
333    if (wd->pulse_state)
334      edje_object_signal_emit(wd->progressbar, "elm,state,pulse,start", "elm");
335    else
336      edje_object_signal_emit(wd->progressbar, "elm,state,pulse,stop", "elm");
337 }
338
339 /**
340  * Set the value the progressbar indicates
341  *
342  * @param obj The progressbar object
343  * @param val The fraction value (must be between 0.0 and 1.0)
344  *
345  * @ingroup Progressbar
346  */
347 EAPI void
348 elm_progressbar_value_set(Evas_Object *obj, double val)
349 {
350    ELM_CHECK_WIDTYPE(obj, widtype);
351    Widget_Data *wd = elm_widget_data_get(obj);
352    if (!wd) return;
353    if (wd->val == val) return;
354    wd->val = val;
355    if (wd->val < MIN_RATIO_LVL) wd->val = MIN_RATIO_LVL;
356    if (wd->val > MAX_RATIO_LVL) wd->val = MAX_RATIO_LVL;
357    _val_set(obj);
358    _units_set(obj);
359 }
360
361
362 /**
363  * Get the value the progressbar has
364  *
365  * @param obj The progressbar object
366  * @return The value of the progressbar
367  *
368  * @ingroup Progressbar
369  */
370 EAPI double
371 elm_progressbar_value_get(const Evas_Object *obj)
372 {
373    ELM_CHECK_WIDTYPE(obj, widtype) 0.0;
374    Widget_Data *wd = elm_widget_data_get(obj);
375    if (!wd) return 0.0;
376    return wd->val;
377 }
378
379 /**
380  * Set the label of the progressbar
381  *
382  * @param obj The progressbar object
383  * @param label The text label string in UTF-8
384  *
385  * @ingroup Progressbar
386  * @deprecated use elm_object_text_set() instead.
387  */
388 EAPI void
389 elm_progressbar_label_set(Evas_Object *obj, const char *label)
390 {
391    _elm_progressbar_label_set(obj, NULL, label);
392 }
393
394 /**
395  * Get the label of the progressbar
396  *
397  * @param obj The progressbar object
398  * @return The text label string in UTF-8
399  *
400  * @ingroup Progressbar
401  * @deprecated use elm_object_text_set() instead.
402  */
403 EAPI const char *
404 elm_progressbar_label_get(const Evas_Object *obj)
405 {
406    return _elm_progressbar_label_get(obj, NULL);
407 }
408
409 /**
410  * Set the icon object of the progressbar object
411  *
412  * Once the icon object is set, a previously set one will be deleted.
413  * If you want to keep that old content object, use the
414  * elm_progressbar_icon_unset() function.
415  *
416  * @param obj The progressbar object
417  * @param icon The icon object
418  *
419  * @ingroup Progressbar
420  */
421 EAPI void
422 elm_progressbar_icon_set(Evas_Object *obj, Evas_Object *icon)
423 {
424    ELM_CHECK_WIDTYPE(obj, widtype);
425    Widget_Data *wd = elm_widget_data_get(obj);
426    if (!wd) return;
427    if (wd->icon == icon) return;
428    if (wd->icon) evas_object_del(wd->icon);
429    wd->icon = icon;
430    if (icon)
431      {
432         elm_widget_sub_object_add(obj, icon);
433         evas_object_event_callback_add(icon, EVAS_CALLBACK_CHANGED_SIZE_HINTS,
434                                        _changed_size_hints, obj);
435         edje_object_part_swallow(wd->progressbar, "elm.swallow.content", icon);
436         edje_object_signal_emit(wd->progressbar, "elm,state,icon,visible", "elm");
437         edje_object_message_signal_process(wd->progressbar);
438      }
439    _sizing_eval(obj);
440 }
441
442 /**
443  * Get the icon object of the progressbar object
444  *
445  * @param obj The progressbar object
446  * @return The icon object
447  *
448  * @ingroup Progressbar
449  */
450 EAPI Evas_Object *
451 elm_progressbar_icon_get(const Evas_Object *obj)
452 {
453    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
454    Widget_Data *wd = elm_widget_data_get(obj);
455    if (!wd) return NULL;
456    return wd->icon;
457 }
458
459 /**
460  * Unset the icon used for the progressbar object
461  *
462  * Unparent and return the icon object which was set for this widget.
463  *
464  * @param obj The progressbar object
465  * @return The icon object that was being used
466  *
467  * @ingroup Progressbar
468  */
469 EAPI Evas_Object *
470 elm_progressbar_icon_unset(Evas_Object *obj)
471 {
472    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
473    Widget_Data *wd = elm_widget_data_get(obj);
474    if (!wd) return NULL;
475    if (!wd->icon) return NULL;
476    Evas_Object *icon = wd->icon;
477    elm_widget_sub_object_del(obj, wd->icon);
478    edje_object_part_unswallow(wd->progressbar, wd->icon);
479    wd->icon = NULL;
480    return icon;
481 }
482
483 /**
484  * Set the length of the progression region of the progressbar
485  *
486  * This sets the minimum width or height (depending on orientation) of the
487  * area of the progressbar that allows the progressbar to be dragged around. This in
488  * turn affects the objects minimum size (along with icon label and unit
489  * text).
490  *
491  * @param obj The progressbar object
492  * @param size The length of the progressbar area
493  *
494  * @ingroup Progressbar
495  */
496 EAPI void
497 elm_progressbar_span_size_set(Evas_Object *obj, Evas_Coord size)
498 {
499    ELM_CHECK_WIDTYPE(obj, widtype);
500    Widget_Data *wd = elm_widget_data_get(obj);
501    if (!wd) return;
502    if (wd->size == size) return;
503    wd->size = size;
504    if (wd->horizontal)
505      evas_object_size_hint_min_set(wd->spacer, (double)wd->size * elm_widget_scale_get(obj) * _elm_config->scale, 1);
506    else
507      evas_object_size_hint_min_set(wd->spacer, 1, (double)wd->size * elm_widget_scale_get(obj) * _elm_config->scale);
508    edje_object_part_swallow(wd->progressbar, "elm.swallow.bar", wd->spacer);
509    _sizing_eval(obj);
510 }
511
512 /**
513  * Get the length of the progression region of the progressbar
514  *
515  * @param obj The progressbar object
516  * @return The length of the progressbar area
517  *
518  * @ingroup Progressbar
519  */
520 EAPI Evas_Coord
521 elm_progressbar_span_size_get(const Evas_Object *obj)
522 {
523    ELM_CHECK_WIDTYPE(obj, widtype) 0;
524    Widget_Data *wd = elm_widget_data_get(obj);
525    if (!wd) return 0;
526    return wd->size;
527 }
528
529 /**
530  * Set the format string of the unit area
531  *
532  * If NULL, this disabls the unit area display. If not it sets the format
533  * string for the unit text. The unit text is provided a floating point
534  * value, so the unit text can display up to 1 floating point falue. Note that
535  * this is optional. Use a format string such as "%1.2f meters" for example.
536  *
537  * @param obj The progressbar object
538  * @param units The format string for the units display
539  *
540  * @ingroup Progressbar
541  */
542 EAPI void
543 elm_progressbar_unit_format_set(Evas_Object *obj, const char *units)
544 {
545    ELM_CHECK_WIDTYPE(obj, widtype);
546    Widget_Data *wd = elm_widget_data_get(obj);
547    if (!wd) return;
548    eina_stringshare_replace(&wd->units, units);
549    if (units)
550      {
551         edje_object_signal_emit(wd->progressbar, "elm,state,units,visible", "elm");
552         edje_object_message_signal_process(wd->progressbar);
553      }
554    else
555      {
556         edje_object_signal_emit(wd->progressbar, "elm,state,units,hidden", "elm");
557         edje_object_message_signal_process(wd->progressbar);
558      }
559    _units_set(obj);
560    _sizing_eval(obj);
561 }
562
563 /**
564  * Get the format string of the unit area
565  *
566  * @param obj The progressbar object
567  * @return The format string for the units display
568  *
569  * @ingroup Progressbar
570  */
571 EAPI const char *
572 elm_progressbar_unit_format_get(const Evas_Object *obj)
573 {
574    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
575    Widget_Data *wd = elm_widget_data_get(obj);
576    if (!wd) return NULL;
577    return wd->units;
578 }
579
580 /**
581  * Set orientation of the progressbar
582  *
583  * @param obj The progressbar object
584  * @param horizontal If set, the progressbar will be horizontal
585  *
586  * @ingroup Progressbar
587  */
588 EAPI void
589 elm_progressbar_horizontal_set(Evas_Object *obj, Eina_Bool horizontal)
590 {
591    ELM_CHECK_WIDTYPE(obj, widtype);
592    Widget_Data *wd = elm_widget_data_get(obj);
593    if (!wd) return;
594    horizontal = !!horizontal;
595    if (wd->horizontal == horizontal) return;
596    wd->horizontal = horizontal;
597    _theme_hook(obj);
598 }
599
600 /**
601  * Gets orientation of the progressbar
602  *
603  * @param obj The progressbar object
604  * @return The orientation
605  * (0 = vertical, 1 = horizontal)
606  *
607  * @ingroup Progressbar
608  */
609 EAPI Eina_Bool
610 elm_progressbar_horizontal_get(const Evas_Object *obj)
611 {
612    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
613    Widget_Data *wd = elm_widget_data_get(obj);
614    if (!wd) return EINA_FALSE;
615    return wd->horizontal;
616 }
617
618 /**
619  * Invert the progressbar display
620  *
621  * Normally the progressbar will display and interpret values from low to high
622  * and when horizontal that is left to right. When vertical that is top
623  * to bottom. This inverts this (so from right to left or bottom to top) if
624  * inverted is set to 1.
625  *
626  * @param obj The progressbar object
627  * @param inverted The inverted flag. 1 == inverted, 0 == normal
628  *
629  * @ingroup Progressbar
630  */
631 EAPI void
632 elm_progressbar_inverted_set(Evas_Object *obj, Eina_Bool inverted)
633 {
634    ELM_CHECK_WIDTYPE(obj, widtype);
635    Widget_Data *wd = elm_widget_data_get(obj);
636    if (!wd) return;
637    inverted = !!inverted;
638    if (wd->inverted == inverted) return;
639    wd->inverted = inverted;
640    if (wd->inverted)
641      edje_object_signal_emit(wd->progressbar, "elm,state,inverted,on", "elm");
642    else
643      edje_object_signal_emit(wd->progressbar, "elm,state,inverted,off", "elm");
644    edje_object_message_signal_process(wd->progressbar);
645    _val_set(obj);
646    _units_set(obj);
647 }
648
649 /**
650  * Gets if the progressbar will displayed inverted
651  *
652  * @param obj The progressbar object
653  * @return The inverted flag
654  * (1 == inverted, 0 == normal)
655  *
656  * @ingroup Progressbar
657  */
658 EAPI Eina_Bool
659 elm_progressbar_inverted_get(const Evas_Object *obj)
660 {
661    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
662    Widget_Data *wd = elm_widget_data_get(obj);
663    if (!wd) return EINA_FALSE;
664    return wd->inverted;
665 }