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