elementary: make it possible to actually free string returned by value to string...
[framework/uifw/elementary.git] / src / lib / elm_slider.c
1 #include <Elementary.h>
2 #include "elm_priv.h"
3
4 /**
5  * @defgroup Slider Slider
6  *
7  * The slider adds a dragable “slider” widget for selecting the value of
8  * something within a range.
9  *
10  *
11  * A slider can be horizontal or vertical. It can contain an Icon and has a
12  * primary label as well as a units label (that is formatted with floating
13  * point values and thus accepts a printf-style format string, like
14  * “%1.2f units”. There is also an indicator string that may be somewhere
15  * else (like on the slider itself) that also accepts a format string like
16  * units. Label, Icon Unit and Indicator strings/objects are optional.
17  *
18  * A slider may be inverted which means values invert, with high vales being
19  * on the left or top and low values on the right or bottom (as opposed to
20  * normally being low on the left or top and high on the bottom and right).
21  *
22  * The slider should have its minimum and maximum values set by the
23  * application with  elm_slider_min_max_set() and value should also be set by
24  * the application before use with  elm_slider_value_set(). The span of the
25  * slider is its length (horizontally or vertically). This will be scaled by
26  * the object or applications scaling factor. At any point code can query the
27  * slider for its value with elm_slider_value_get().
28  *
29  * Signals that you can add callbacks for are:
30  *
31  * "changed" - Whenever the slider value is changed by the user.
32  * "slider,drag,start" - dragging the slider indicator around has started
33  * "slider,drag,stop" - dragging the slider indicator around has stopped
34  * "delay,changed" - A short time after the value is changed by the user.
35  *                   This will be called only when the user stops dragging for
36  *                   a very short period or when they release their
37  *                   finger/mouse, so it avoids possibly expensive reactions to
38  *                   the value change.
39  */
40
41 typedef struct _Widget_Data Widget_Data;
42
43 struct _Widget_Data
44 {
45    Evas_Object *slider;
46    Evas_Object *icon;
47    Evas_Object *end;
48    Evas_Object *spacer;
49
50    Ecore_Timer *delay;
51
52    const char *label;
53    const char *units;
54    const char *indicator;
55
56    const char *(*indicator_format_func)(double val);
57    void (*indicator_format_free)(const char *str);
58
59    const char *(*units_format_func)(double val);
60    void (*units_format_free)(const char *str);
61
62    double val, val_min, val_max;
63    Evas_Coord size;
64
65    Eina_Bool horizontal : 1;
66    Eina_Bool inverted : 1;
67    Eina_Bool indicator_show : 1;
68 };
69
70 #define ELM_SLIDER_INVERTED_FACTOR (-1.0)
71
72 static const char *widtype = NULL;
73 static void _del_hook(Evas_Object *obj);
74 static void _mirrored_set(Evas_Object *obj, Eina_Bool rtl);
75 static void _theme_hook(Evas_Object *obj);
76 static void _disable_hook(Evas_Object *obj);
77 static void _sizing_eval(Evas_Object *obj);
78 static void _changed_size_hints(void *data, Evas *e, Evas_Object *obj, void *event_info);
79 static void _sub_del(void *data, Evas_Object *obj, void *event_info);
80 static void _units_set(Evas_Object *obj);
81 static void _val_set(Evas_Object *obj);
82 static void _indicator_set(Evas_Object *obj);
83 static void _on_focus_hook(void *data, Evas_Object *obj);
84 static void _drag_up(void *data, Evas_Object *obj,
85                     const char *emission, const char *source);
86 static void _drag_down(void *data, Evas_Object *obj,
87                     const char *emission, const char *source);
88 static Eina_Bool _event_hook(Evas_Object *obj, Evas_Object *src,
89                              Evas_Callback_Type type, void *event_info);
90 static void _spacer_cb(void *data, Evas * e, Evas_Object * obj, void *event_info);
91
92 static const char SIG_CHANGED[] = "changed";
93 static const char SIG_DELAY_CHANGED[] = "delay,changed";
94 static const char SIG_DRAG_START[] = "slider,drag,start";
95 static const char SIG_DRAG_STOP[] = "slider,drag,stop";
96 static const Evas_Smart_Cb_Description _signals[] = {
97   {SIG_CHANGED, ""},
98   {SIG_DELAY_CHANGED, ""},
99   {SIG_DRAG_START, ""},
100   {SIG_DRAG_STOP, ""},
101   {NULL, NULL}
102 };
103
104 static Eina_Bool
105 _event_hook(Evas_Object *obj, Evas_Object *src __UNUSED__, Evas_Callback_Type type, void *event_info)
106 {
107    Evas_Event_Mouse_Wheel *mev;
108    Evas_Event_Key_Down *ev;
109    Widget_Data *wd;
110
111    wd = elm_widget_data_get(obj);
112    if (!wd) return EINA_FALSE;
113
114    if (type == EVAS_CALLBACK_KEY_DOWN) goto key_down;
115    else if (type != EVAS_CALLBACK_MOUSE_WHEEL) return EINA_FALSE;
116
117    mev = event_info;
118    if (mev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return EINA_FALSE;
119    if (elm_widget_disabled_get(obj)) return EINA_FALSE;
120
121    if (mev->z < 0) _drag_up(obj, NULL, NULL, NULL);
122    else _drag_down(obj, NULL, NULL, NULL);
123    mev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
124    return EINA_TRUE;
125
126   key_down:
127    ev = event_info;
128    if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return EINA_FALSE;
129    if (elm_widget_disabled_get(obj)) return EINA_FALSE;
130    if ((!strcmp(ev->keyname, "Left"))
131        || (!strcmp(ev->keyname, "KP_Left")))
132      {
133         if (!wd->horizontal) return EINA_FALSE;
134         if (!wd->inverted) _drag_down(obj, NULL, NULL, NULL);
135         else _drag_up(obj, NULL, NULL, NULL);
136         ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
137         return EINA_TRUE;
138      }
139    else if ((!strcmp(ev->keyname, "Right"))
140             || (!strcmp(ev->keyname, "KP_Right")))
141      {
142         if (!wd->horizontal) return EINA_FALSE;
143         if (!wd->inverted) _drag_up(obj, NULL, NULL, NULL);
144         else _drag_down(obj, NULL, NULL, NULL);
145         ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
146         return EINA_TRUE;
147      }
148    else if ((!strcmp(ev->keyname, "Up")) || (!strcmp(ev->keyname, "KP_Up")))
149      {
150         if (wd->horizontal) return EINA_FALSE;
151         if (wd->inverted) _drag_up(obj, NULL, NULL, NULL);
152         else _drag_down(obj, NULL, NULL, NULL);
153         ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
154         return EINA_TRUE;
155      }
156    else if ((!strcmp(ev->keyname, "Down")) || (!strcmp(ev->keyname, "KP_Down")))
157      {
158         if (wd->horizontal) return EINA_FALSE;
159         if (wd->inverted) _drag_down(obj, NULL, NULL, NULL);
160         else _drag_up(obj, NULL, NULL, NULL);
161         ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
162         return EINA_TRUE;
163      }
164    else return EINA_FALSE;
165 }
166
167 static void
168 _del_hook(Evas_Object *obj)
169 {
170    Widget_Data *wd = elm_widget_data_get(obj);
171    if (!wd) return;
172    if (wd->label) eina_stringshare_del(wd->label);
173    if (wd->indicator) eina_stringshare_del(wd->units);
174    if (wd->delay) ecore_timer_del(wd->delay);
175    free(wd);
176 }
177
178 static void
179 _on_focus_hook(void *data __UNUSED__, Evas_Object *obj)
180 {
181    Widget_Data *wd = elm_widget_data_get(obj);
182    if (!wd) return;
183    if (elm_widget_focus_get(obj))
184      {
185         edje_object_signal_emit(wd->slider, "elm,action,focus", "elm");
186         evas_object_focus_set(wd->slider, EINA_TRUE);
187      }
188    else
189      {
190         edje_object_signal_emit(wd->slider, "elm,action,unfocus", "elm");
191         evas_object_focus_set(wd->slider, EINA_FALSE);
192      }
193 }
194
195 static void
196 _mirrored_set(Evas_Object *obj, Eina_Bool rtl)
197 {
198    Widget_Data *wd = elm_widget_data_get(obj);
199    if (!wd) return;
200    edje_object_mirrored_set(wd->slider, rtl);
201 }
202
203 static void
204 _theme_hook(Evas_Object *obj)
205 {
206    Widget_Data *wd = elm_widget_data_get(obj);
207    if (!wd) return;
208    _elm_widget_mirrored_reload(obj);
209    _mirrored_set(obj, elm_widget_mirrored_get(obj));
210    if (wd->horizontal)
211      _elm_theme_object_set(obj, wd->slider, "slider", "horizontal", elm_widget_style_get(obj));
212    else
213      _elm_theme_object_set(obj, wd->slider, "slider", "vertical", elm_widget_style_get(obj));
214    if (wd->icon)
215      {
216         edje_object_part_swallow(wd->slider, "elm.swallow.content", wd->icon);
217         edje_object_signal_emit(wd->slider, "elm,state,icon,visible", "elm");
218      }
219    if (wd->end)
220      edje_object_signal_emit(wd->slider, "elm,state,end,visible", "elm");
221    else
222      edje_object_signal_emit(wd->slider, "elm,state,end,hidden", "elm");
223    if (wd->label)
224      {
225         edje_object_part_text_set(wd->slider, "elm.text", wd->label);
226         edje_object_signal_emit(wd->slider, "elm,state,text,visible", "elm");
227      }
228
229    if (wd->units)
230      edje_object_signal_emit(wd->slider, "elm,state,units,visible", "elm");
231
232    if (wd->horizontal)
233      evas_object_size_hint_min_set(wd->spacer, (double)wd->size * elm_widget_scale_get(obj) * _elm_config->scale, 1);
234    else
235      evas_object_size_hint_min_set(wd->spacer, 1, (double)wd->size * elm_widget_scale_get(obj) * _elm_config->scale);
236
237    if (wd->inverted)
238      edje_object_signal_emit(wd->slider, "elm,state,inverted,on", "elm");
239
240    edje_object_part_swallow(wd->slider, "elm.swallow.bar", wd->spacer);
241    _units_set(obj);
242    _indicator_set(obj);
243    edje_object_message_signal_process(wd->slider);
244    edje_object_scale_set(wd->slider, elm_widget_scale_get(obj) * _elm_config->scale);
245    _val_set(obj);
246    _sizing_eval(obj);
247 }
248
249 static void
250 _disable_hook(Evas_Object *obj)
251 {
252    Widget_Data *wd = elm_widget_data_get(obj);
253    if (!wd) return;
254    if (elm_widget_disabled_get(obj))
255      edje_object_signal_emit(wd->slider, "elm,state,disabled", "elm");
256    else
257      edje_object_signal_emit(wd->slider, "elm,state,enabled", "elm");
258 }
259
260 static void
261 _sizing_eval(Evas_Object *obj)
262 {
263    Widget_Data *wd = elm_widget_data_get(obj);
264    Evas_Coord minw = -1, minh = -1, maxw = -1, maxh = -1;
265    if (!wd) return;
266    elm_coords_finger_size_adjust(1, &minw, 1, &minh);
267    edje_object_size_min_restricted_calc(wd->slider, &minw, &minh, minw, minh);
268    elm_coords_finger_size_adjust(1, &minw, 1, &minh);
269    evas_object_size_hint_min_set(obj, minw, minh);
270    evas_object_size_hint_max_set(obj, maxw, maxh);
271 }
272
273 static void
274 _changed_size_hints(void *data, Evas *e __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__)
275 {
276    Widget_Data *wd = elm_widget_data_get(data);
277    if (!wd) return;
278    if ((obj != wd->icon) && (obj != wd->end)) return;
279    _sizing_eval(data);
280 }
281
282 static void
283 _sub_del(void *data __UNUSED__, Evas_Object *obj, void *event_info)
284 {
285    Widget_Data *wd = elm_widget_data_get(obj);
286    Evas_Object *sub = event_info;
287    if (!wd) return;
288    if (sub == wd->icon)
289      {
290         edje_object_signal_emit(wd->slider, "elm,state,icon,hidden", "elm");
291         evas_object_event_callback_del_full
292            (sub, EVAS_CALLBACK_CHANGED_SIZE_HINTS, _changed_size_hints, obj);
293         wd->icon = NULL;
294         edje_object_message_signal_process(wd->slider);
295         _sizing_eval(obj);
296      }
297    if (sub == wd->end)
298      {
299         edje_object_signal_emit(wd->slider, "elm,state,end,hidden", "elm");
300         evas_object_event_callback_del_full(sub,
301                                             EVAS_CALLBACK_CHANGED_SIZE_HINTS,
302                                             _changed_size_hints, obj);
303         wd->end = NULL;
304         edje_object_message_signal_process(wd->slider);
305         _sizing_eval(obj);
306      }
307 }
308
309 static Eina_Bool
310 _delay_change(void *data)
311 {
312    Widget_Data *wd = elm_widget_data_get(data);
313    if (!wd) return ECORE_CALLBACK_CANCEL;
314    wd->delay = NULL;
315    evas_object_smart_callback_call(data, SIG_DELAY_CHANGED, NULL);
316    return ECORE_CALLBACK_CANCEL;
317 }
318
319 static void
320 _val_fetch(Evas_Object *obj)
321 {
322    Eina_Bool rtl;
323    Widget_Data *wd = elm_widget_data_get(obj);
324    double posx = 0.0, posy = 0.0, pos = 0.0, val;
325    if (!wd) return;
326    edje_object_part_drag_value_get(wd->slider, "elm.dragable.slider",
327                                    &posx, &posy);
328    if (wd->horizontal) pos = posx;
329    else pos = posy;
330
331    rtl = elm_widget_mirrored_get(obj);
332    if ((!rtl && wd->inverted) || (rtl &&
333                                   ((!wd->horizontal && wd->inverted) ||
334                                    (wd->horizontal && !wd->inverted)))) pos = 1.0 - pos;
335    val = (pos * (wd->val_max - wd->val_min)) + wd->val_min;
336    if (val != wd->val)
337      {
338         wd->val = val;
339         evas_object_smart_callback_call(obj, SIG_CHANGED, NULL);
340         if (wd->delay) ecore_timer_del(wd->delay);
341         wd->delay = ecore_timer_add(0.2, _delay_change, obj);
342      }
343 }
344
345 static void
346 _val_set(Evas_Object *obj)
347 {
348    Eina_Bool rtl;
349    Widget_Data *wd = elm_widget_data_get(obj);
350    double pos;
351    if (!wd) return;
352    if (wd->val_max > wd->val_min)
353      pos = (wd->val - wd->val_min) / (wd->val_max - wd->val_min);
354    else
355      pos = 0.0;
356    if (pos < 0.0) pos = 0.0;
357    else if (pos > 1.0) pos = 1.0;
358
359    rtl = elm_widget_mirrored_get(obj);
360    if ((!rtl && wd->inverted) || (rtl &&
361                                   ((!wd->horizontal && wd->inverted) ||
362                                    (wd->horizontal && !wd->inverted)))) pos = 1.0 - pos;
363    edje_object_part_drag_value_set(wd->slider, "elm.dragable.slider", pos, pos);
364 }
365
366 static void
367 _units_set(Evas_Object *obj)
368 {
369    Widget_Data *wd = elm_widget_data_get(obj);
370    if (!wd) return;
371    if (wd->units_format_func)
372      {
373         const char *buf;
374         buf = wd->units_format_func(wd->val);
375         edje_object_part_text_set(wd->slider, "elm.units", buf);
376         if (wd->units_format_free) wd->units_format_free(buf);
377      }
378    else if (wd->units)
379      {
380         char buf[1024];
381
382         snprintf(buf, sizeof(buf), wd->units, wd->val);
383         edje_object_part_text_set(wd->slider, "elm.units", buf);
384      }
385    else
386      edje_object_part_text_set(wd->slider, "elm.units", NULL);
387 }
388
389 static void
390 _indicator_set(Evas_Object *obj)
391 {
392    Widget_Data *wd = elm_widget_data_get(obj);
393    if (!wd) return;
394    if (wd->indicator_format_func)
395      {
396         const char *buf;
397         buf = wd->indicator_format_func(wd->val);
398         edje_object_part_text_set(wd->slider, "elm.dragable.slider:elm.indicator", buf);
399         if (wd->indicator_format_free) wd->indicator_format_free(buf);
400      }
401    else if (wd->indicator)
402      {
403         char buf[1024];
404         snprintf(buf, sizeof(buf), wd->indicator, wd->val);
405         edje_object_part_text_set(wd->slider, "elm.dragable.slider:elm.indicator", buf);
406      }
407    else
408      edje_object_part_text_set(wd->slider, "elm.dragable.slider:elm.indicator", NULL);
409 }
410
411 static void
412 _drag(void *data, Evas_Object *obj __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__)
413 {
414    _val_fetch(data);
415    _units_set(data);
416    _indicator_set(data);
417 }
418
419 static void
420 _drag_start(void *data, Evas_Object *obj __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__)
421 {
422    _val_fetch(data);
423    evas_object_smart_callback_call(data, SIG_DRAG_START, NULL);
424    _units_set(data);
425    _indicator_set(data);
426    elm_widget_scroll_freeze_push(data);
427 }
428
429 static void
430 _drag_stop(void *data, Evas_Object *obj __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__)
431 {
432    _val_fetch(data);
433    evas_object_smart_callback_call(data, SIG_DRAG_STOP, NULL);
434    _units_set(data);
435    _indicator_set(data);
436    elm_widget_scroll_freeze_pop(data);
437 }
438
439 static void
440 _drag_step(void *data, Evas_Object *obj __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__)
441 {
442    _val_fetch(data);
443    _units_set(data);
444    _indicator_set(data);
445 }
446
447 static void
448 _drag_up(void *data, Evas_Object *obj __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__)
449 {
450    double step;
451    Widget_Data *wd;
452
453    wd = elm_widget_data_get(data);
454    step = 0.05;
455
456    if (wd->inverted) step *= ELM_SLIDER_INVERTED_FACTOR;
457
458    edje_object_part_drag_step(wd->slider, "elm.dragable.slider", step, step);
459 }
460
461 static void
462 _drag_down(void *data, Evas_Object *obj __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__)
463 {
464    double step;
465    Widget_Data *wd;
466
467    wd = elm_widget_data_get(data);
468    step = -0.05;
469
470    if (wd->inverted) step *= ELM_SLIDER_INVERTED_FACTOR;
471
472    edje_object_part_drag_step(wd->slider, "elm.dragable.slider", step, step);
473 }
474
475 static void
476 _spacer_cb(void *data, Evas *e, Evas_Object *obj __UNUSED__, void *event_info)
477 {
478    Widget_Data *wd = elm_widget_data_get(data);
479    Evas_Event_Mouse_Down *ev = event_info;
480    Evas_Coord x, y, w, h;
481    double button_x, button_y;
482
483    evas_object_geometry_get(wd->spacer, &x, &y, &w, &h);
484    edje_object_part_drag_value_get(wd->slider, "elm.dragable.slider", &button_x, &button_y);
485    if (wd->horizontal)
486      {
487         button_x = ((double)ev->canvas.x - (double)x) / (double)w;
488         if (button_x > 1) button_x = 1;
489         if (button_x < 0) button_x = 0;
490      }
491    else
492      {
493         button_y = ((double)ev->canvas.y - (double)y) / (double)h;
494         if (button_y > 1) button_y = 1;
495         if (button_y < 0) button_y = 0;
496      }
497    edje_object_part_drag_value_set(wd->slider, "elm.dragable.slider", button_x, button_y);
498    evas_event_feed_mouse_cancel(e, 0, NULL);
499    evas_event_feed_mouse_down(e, 1, EVAS_BUTTON_NONE, 0, NULL);
500 }
501
502 static void
503 _elm_slider_label_set(Evas_Object *obj, const char *item, const char *label)
504 {
505    ELM_CHECK_WIDTYPE(obj, widtype);
506    Widget_Data *wd = elm_widget_data_get(obj);
507    if (item && strcmp(item, "default")) return;
508    if (!wd) return;
509    eina_stringshare_replace(&wd->label, label);
510    if (label)
511      {
512         edje_object_signal_emit(wd->slider, "elm,state,text,visible", "elm");
513         edje_object_message_signal_process(wd->slider);
514      }
515    else
516      {
517         edje_object_signal_emit(wd->slider, "elm,state,text,hidden", "elm");
518         edje_object_message_signal_process(wd->slider);
519      }
520    edje_object_part_text_set(wd->slider, "elm.text", label);
521    _sizing_eval(obj);
522 }
523
524 static const char *
525 _elm_slider_label_get(const Evas_Object *obj, const char *item)
526 {
527    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
528    Widget_Data *wd = elm_widget_data_get(obj);
529    if (item && strcmp(item, "default")) return NULL;
530    if (!wd) return NULL;
531    return wd->label;
532 }
533
534 /**
535  * Add a new slider to the parent
536  *
537  * @param parent The parent object
538  * @return The new object or NULL if it cannot be created
539  *
540  * @ingroup Slider
541  */
542 EAPI Evas_Object *
543 elm_slider_add(Evas_Object *parent)
544 {
545    Evas_Object *obj;
546    Evas *e;
547    Widget_Data *wd;
548
549    ELM_WIDGET_STANDARD_SETUP(wd, Widget_Data, parent, e, obj, NULL);
550
551    ELM_SET_WIDTYPE(widtype, "slider");
552    elm_widget_type_set(obj, "slider");
553    elm_widget_sub_object_add(parent, obj);
554    elm_widget_on_focus_hook_set(obj, _on_focus_hook, NULL);
555    elm_widget_data_set(obj, wd);
556    elm_widget_del_hook_set(obj, _del_hook);
557    elm_widget_theme_hook_set(obj, _theme_hook);
558    elm_widget_disable_hook_set(obj, _disable_hook);
559    elm_widget_can_focus_set(obj, EINA_TRUE);
560    elm_widget_event_hook_set(obj, _event_hook);
561    elm_widget_text_set_hook_set(obj, _elm_slider_label_set);
562    elm_widget_text_get_hook_set(obj, _elm_slider_label_get);
563
564    wd->horizontal = EINA_TRUE;
565    wd->indicator_show = EINA_TRUE;
566    wd->val = 0.0;
567    wd->val_min = 0.0;
568    wd->val_max = 1.0;
569
570    wd->slider = edje_object_add(e);
571    _elm_theme_object_set(obj, wd->slider, "slider", "horizontal", "default");
572    elm_widget_resize_object_set(obj, wd->slider);
573    edje_object_signal_callback_add(wd->slider, "drag", "*", _drag, obj);
574    edje_object_signal_callback_add(wd->slider, "drag,start", "*", _drag_start, obj);
575    edje_object_signal_callback_add(wd->slider, "drag,stop", "*", _drag_stop, obj);
576    edje_object_signal_callback_add(wd->slider, "drag,step", "*", _drag_step, obj);
577    edje_object_signal_callback_add(wd->slider, "drag,page", "*", _drag_stop, obj);
578    //   edje_object_signal_callback_add(wd->slider, "drag,set", "*", _drag_stop, obj);
579    edje_object_part_drag_value_set(wd->slider, "elm.dragable.slider", 0.0, 0.0);
580
581    wd->spacer = evas_object_rectangle_add(e);
582    evas_object_color_set(wd->spacer, 0, 0, 0, 0);
583    evas_object_pass_events_set(wd->spacer, EINA_TRUE);
584    elm_widget_sub_object_add(obj, wd->spacer);
585    edje_object_part_swallow(wd->slider, "elm.swallow.bar", wd->spacer);
586    evas_object_event_callback_add(wd->spacer, EVAS_CALLBACK_MOUSE_DOWN, _spacer_cb, obj);
587    evas_object_smart_callback_add(obj, "sub-object-del", _sub_del, obj);
588
589    _mirrored_set(obj, elm_widget_mirrored_get(obj));
590    _sizing_eval(obj);
591
592    // TODO: convert Elementary to subclassing of Evas_Smart_Class
593    // TODO: and save some bytes, making descriptions per-class and not instance!
594    evas_object_smart_callbacks_descriptions_set(obj, _signals);
595    return obj;
596 }
597
598 /**
599  * Set the label of the slider
600  *
601  * @param obj The slider object
602  * @param label The text label string in UTF-8
603  *
604  * @ingroup Slider
605  */
606 EAPI void
607 elm_slider_label_set(Evas_Object *obj, const char *label)
608 {
609    _elm_slider_label_set(obj, NULL, label);
610 }
611
612 /**
613  * Get the label of the slider
614  *
615  * @param obj The slider object
616  * @return The text label string in UTF-8
617  *
618  * @ingroup Slider
619  */
620 EAPI const char *
621 elm_slider_label_get(const Evas_Object *obj)
622 {
623    return _elm_slider_label_get(obj, NULL);
624 }
625
626 /**
627  * Set the icon object (leftmost widget) of the slider object.
628  *
629  * Once the icon object is set, a previously set one will be deleted.
630  * If you want to keep that old content object, use the
631  * elm_slider_icon_unset() function.
632  *
633  * @param obj The slider object
634  * @param icon The icon object
635  *
636  * @note If the object being set does not have minimum size hints set,
637  * it won't get properly displayed.
638  *
639  * @ingroup Slider
640  */
641 EAPI void
642 elm_slider_icon_set(Evas_Object *obj, Evas_Object *icon)
643 {
644    ELM_CHECK_WIDTYPE(obj, widtype);
645    Widget_Data *wd = elm_widget_data_get(obj);
646    if (!wd) return;
647    if (wd->icon == icon) return;
648    if (wd->icon) evas_object_del(wd->icon);
649    wd->icon = icon;
650    if (icon)
651      {
652         elm_widget_sub_object_add(obj, icon);
653         evas_object_event_callback_add(icon, EVAS_CALLBACK_CHANGED_SIZE_HINTS,
654                                        _changed_size_hints, obj);
655         edje_object_part_swallow(wd->slider, "elm.swallow.icon", icon);
656         edje_object_signal_emit(wd->slider, "elm,state,icon,visible", "elm");
657         edje_object_message_signal_process(wd->slider);
658      }
659    _sizing_eval(obj);
660 }
661
662 /**
663  * Unset the leftmost widget of the slider, unparenting and
664  * returning it.
665  *
666  * @param obj The slider object
667  * @return the previously set icon sub-object of this slider, on
668  * success.
669  *
670  * @see elm_slider_icon_set()
671  *
672  * @ingroup Slider
673  */
674 EAPI Evas_Object *
675 elm_slider_icon_unset(Evas_Object *obj)
676 {
677    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
678    Widget_Data *wd = elm_widget_data_get(obj);
679    Evas_Object *ret = NULL;
680    if (!wd) return NULL;
681    if (wd->icon)
682      {
683         elm_widget_sub_object_del(obj, wd->icon);
684         ret = wd->icon;
685         edje_object_part_unswallow(wd->slider, wd->icon);
686         edje_object_signal_emit(wd->slider, "elm,state,icon,hidden", "elm");
687         wd->icon = NULL;
688         _sizing_eval(obj);
689      }
690    return ret;
691 }
692
693 /**
694  * Get the icon object of the slider object. This object is owned by
695  * the scrolled entry and should not be modified.
696  *
697  * @param obj The slider object
698  * @return The icon object
699  *
700  * @ingroup Slider
701  */
702 EAPI Evas_Object *
703 elm_slider_icon_get(const Evas_Object *obj)
704 {
705    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
706    Widget_Data *wd = elm_widget_data_get(obj);
707    if (!wd) return NULL;
708    return wd->icon;
709 }
710
711 /**
712  * Set the length of the dragable region of the slider
713  *
714  * This sets the minimum width or height (depending on orientation) of the
715  * area of the slider that allows the slider to be dragged around. This in
716  * turn affects the objects minimum size (along with icon label and unit
717  * text). Note that this will also get multiplied by the scale factor.
718  *
719  * @param obj The slider object
720  * @param size The length of the slider area
721  *
722  * @ingroup Slider
723  */
724 EAPI void
725 elm_slider_span_size_set(Evas_Object *obj, Evas_Coord size)
726 {
727    ELM_CHECK_WIDTYPE(obj, widtype);
728    Widget_Data *wd = elm_widget_data_get(obj);
729    if (!wd) return;
730    if (wd->size == size) return;
731    wd->size = size;
732    if (wd->horizontal)
733      evas_object_size_hint_min_set(wd->spacer, (double)wd->size * elm_widget_scale_get(obj) * _elm_config->scale, 1);
734    else
735      evas_object_size_hint_min_set(wd->spacer, 1, (double)wd->size * elm_widget_scale_get(obj) * _elm_config->scale);
736    if (wd->indicator_show)
737      edje_object_signal_emit(wd->slider, "elm,state,val,show", "elm");
738    else
739      edje_object_signal_emit(wd->slider, "elm,state,val,hide", "elm");
740    edje_object_part_swallow(wd->slider, "elm.swallow.bar", wd->spacer);
741    _sizing_eval(obj);
742 }
743
744 /**
745  * Get the length of the dragable region of the slider
746  *
747  * This gets the minimum width or height (depending on orientation) of
748  * the area of the slider that allows the slider to be dragged
749  * around. Note that this will also get multiplied by the scale
750  * factor.
751  *
752  * @param obj The slider object
753  * @return The length of the slider area
754  *
755  * @ingroup Slider
756  */
757 EAPI Evas_Coord
758 elm_slider_span_size_get(const Evas_Object *obj)
759 {
760    ELM_CHECK_WIDTYPE(obj, widtype) 0;
761    Widget_Data *wd = elm_widget_data_get(obj);
762    if (!wd) return 0;
763    return wd->size;
764 }
765
766 /**
767  * Set the format string of the unit area
768  *
769  * If NULL, this disabls the unit area display. If not it sets the format
770  * string for the unit text. The unit text is provided a floating point
771  * value, so the unit text can display up to 1 floating point value. Note that
772  * this is optional. Use a format string such as "%1.2f meters" for example.
773  *
774  * @param obj The slider object
775  * @param units The format string for the units display
776  *
777  * @ingroup Slider
778  */
779 EAPI void
780 elm_slider_unit_format_set(Evas_Object *obj, const char *units)
781 {
782    ELM_CHECK_WIDTYPE(obj, widtype);
783    Widget_Data *wd = elm_widget_data_get(obj);
784    if (!wd) return;
785    eina_stringshare_replace(&wd->units, units);
786    if (units)
787      {
788         edje_object_signal_emit(wd->slider, "elm,state,units,visible", "elm");
789         edje_object_message_signal_process(wd->slider);
790      }
791    else
792      {
793         edje_object_signal_emit(wd->slider, "elm,state,units,hidden", "elm");
794         edje_object_message_signal_process(wd->slider);
795      }
796    _units_set(obj);
797    _sizing_eval(obj);
798 }
799
800 /**
801  * Get the format string for the unit area
802  *
803  * The slider may also display a value (the value of the slider) somewhere
804  * (for example above the slider knob that is dragged around). This sets the
805  * format string for this. See elm_slider_unit_format_set() for more
806  * information on how this works.
807  *
808  * @param obj The slider object
809  * @return The format string for the unit display.
810  *
811  * @ingroup Slider
812  */
813 EAPI const char *
814 elm_slider_unit_format_get(const Evas_Object *obj)
815 {
816    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
817    Widget_Data *wd = elm_widget_data_get(obj);
818    if (!wd) return NULL;
819    return wd->units;
820 }
821
822 /**
823  * Set the format string for the indicator area
824  *
825  * The slider may also display a value (the value of the slider) somewhere
826  * (for example above the slider knob that is dragged around). This sets the
827  * format string for this. See elm_slider_unit_format_set() for more
828  * information on how this works.
829  *
830  * @param obj The slider object
831  * @param indicator The format string for the indicator display
832  *
833  * @ingroup Slider
834  */
835 EAPI void
836 elm_slider_indicator_format_set(Evas_Object *obj, const char *indicator)
837 {
838    ELM_CHECK_WIDTYPE(obj, widtype);
839    Widget_Data *wd = elm_widget_data_get(obj);
840    if (!wd) return;
841    eina_stringshare_replace(&wd->indicator, indicator);
842    _indicator_set(obj);
843 }
844
845 /**
846  * Get the format string for the indicator area
847  *
848  * The slider may also display a value (the value of the slider) somewhere
849  * (for example above the slider knob that is dragged around). This sets the
850  * format string for this. See elm_slider_indicator_format_set() for more
851  * information on how this works.
852  *
853  * @param obj The slider object
854  * @return The format string for the indicator display.
855  *
856  * @ingroup Slider
857  */
858 EAPI const char *
859 elm_slider_indicator_format_get(const Evas_Object *obj)
860 {
861    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
862    Widget_Data *wd = elm_widget_data_get(obj);
863    if (!wd) return NULL;
864    return wd->indicator;
865 }
866
867 /**
868  * Set orientation of the slider
869  *
870  * @param obj The slider object
871  * @param horizontal If set, the slider will be horizontal
872  *
873  * @ingroup Slider
874  */
875 EAPI void
876 elm_slider_horizontal_set(Evas_Object *obj, Eina_Bool horizontal)
877 {
878    ELM_CHECK_WIDTYPE(obj, widtype);
879    Widget_Data *wd = elm_widget_data_get(obj);
880    if (!wd) return;
881    horizontal = !!horizontal;
882    if (wd->horizontal == horizontal) return;
883    wd->horizontal = horizontal;
884    _theme_hook(obj);
885 }
886
887 /**
888  * Get orientation of the slider
889  *
890  * @param obj The slider object
891  * @return If @c EINA_TRUE the slider will be horizontal, else it is
892  *         vertical.
893  * @ingroup Slider
894  */
895 EAPI Eina_Bool
896 elm_slider_horizontal_get(const Evas_Object *obj)
897 {
898    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
899    Widget_Data *wd = elm_widget_data_get(obj);
900    if (!wd) return EINA_FALSE;
901    return wd->horizontal;
902 }
903
904 /**
905  * Set the minimum and maximum values for the slider
906  *
907  * Maximum mut be greater than minimum.
908  *
909  * @param obj The slider object
910  * @param min The minimum value
911  * @param max The maximum value
912  *
913  * @ingroup Slider
914  */
915 EAPI void
916 elm_slider_min_max_set(Evas_Object *obj, double min, double max)
917 {
918    ELM_CHECK_WIDTYPE(obj, widtype);
919    Widget_Data *wd = elm_widget_data_get(obj);
920    if (!wd) return;
921    if ((wd->val_min == min) && (wd->val_max == max)) return;
922    wd->val_min = min;
923    wd->val_max = max;
924    if (wd->val < wd->val_min) wd->val = wd->val_min;
925    if (wd->val > wd->val_max) wd->val = wd->val_max;
926    _val_set(obj);
927    _units_set(obj);
928    _indicator_set(obj);
929 }
930
931 /**
932  * Get the minimum and maximum values for the slider
933  *
934  * @param obj The slider object
935  * @param min The pointer to store minimum value, may be @c NULL.
936  * @param max The pointer to store maximum value, may be @c NULL.
937  *
938  * @ingroup Slider
939  */
940 EAPI void
941 elm_slider_min_max_get(const Evas_Object *obj, double *min, double *max)
942 {
943    if (min) *min = 0.0;
944    if (max) *max = 0.0;
945    ELM_CHECK_WIDTYPE(obj, widtype);
946    Widget_Data *wd = elm_widget_data_get(obj);
947    if (!wd) return;
948    if (min) *min = wd->val_min;
949    if (max) *max = wd->val_max;
950 }
951
952 /**
953  * Set the value the slider indicates
954  *
955  * @param obj The slider object
956  * @param val The value (must be between min and max for the slider)
957  *
958  * @ingroup Slider
959  */
960 EAPI void
961 elm_slider_value_set(Evas_Object *obj, double val)
962 {
963    ELM_CHECK_WIDTYPE(obj, widtype);
964    Widget_Data *wd = elm_widget_data_get(obj);
965    if (!wd) return;
966    if (wd->val == val) return;
967    wd->val = val;
968    if (wd->val < wd->val_min) wd->val = wd->val_min;
969    if (wd->val > wd->val_max) wd->val = wd->val_max;
970    _val_set(obj);
971    _units_set(obj);
972    _indicator_set(obj);
973 }
974
975 /**
976  * Get the value the slider has
977  *
978  * @param obj The slider object
979  * @return The value of the slider
980  *
981  * @ingroup Slider
982  */
983 EAPI double
984 elm_slider_value_get(const Evas_Object *obj)
985 {
986    ELM_CHECK_WIDTYPE(obj, widtype) 0.0;
987    Widget_Data *wd = elm_widget_data_get(obj);
988    if (!wd) return 0.0;
989    return wd->val;
990 }
991
992 /**
993  * Invert the slider display
994  *
995  * Normally the slider will display and interpret values from low to high
996  * and when horizontal that is left to right. When vertical that is top
997  * to bottom. This inverts this (so from right to left or bottom to top) if
998  * inverted is set to 1.
999  *
1000  * @param obj The slider object
1001  * @param inverted The inverted flag. 1 == inverted, 0 == normal
1002  *
1003  * @ingroup Slider
1004  */
1005 EAPI void
1006 elm_slider_inverted_set(Evas_Object *obj, Eina_Bool inverted)
1007 {
1008    ELM_CHECK_WIDTYPE(obj, widtype);
1009    Widget_Data *wd = elm_widget_data_get(obj);
1010    if (!wd) return;
1011    inverted = !!inverted;
1012    if (wd->inverted == inverted) return;
1013    wd->inverted = inverted;
1014    if (wd->inverted)
1015      edje_object_signal_emit(wd->slider, "elm,state,inverted,on", "elm");
1016    else
1017      edje_object_signal_emit(wd->slider, "elm,state,inverted,off", "elm");
1018    edje_object_message_signal_process(wd->slider);
1019    _val_set(obj);
1020    _units_set(obj);
1021    _indicator_set(obj);
1022 }
1023
1024 /**
1025  * Get if the slider display is inverted (backwards)
1026  *
1027  * @param obj The slider object
1028  * @return If @c EINA_TRUE the slider will be inverted.
1029  * @ingroup Slider
1030  */
1031 EAPI Eina_Bool
1032 elm_slider_inverted_get(const Evas_Object *obj)
1033 {
1034    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
1035    Widget_Data *wd = elm_widget_data_get(obj);
1036    if (!wd) return EINA_FALSE;
1037    return wd->inverted;
1038 }
1039
1040 /**
1041  * Set the format function pointer for the indicator area
1042  *
1043  * Set the callback function to format the indicator string.
1044  * See elm_slider_indicator_format_set() for more info on how this works.
1045  *
1046  * @param obj The slider object
1047  * @param indicator The format string for the indicator display
1048  * @param func The indicator format function
1049  * @param free_func The freeing function for the format string
1050  *
1051  * @ingroup Slider
1052  */
1053 EAPI void
1054 elm_slider_indicator_format_function_set(Evas_Object *obj, const char *(*func)(double val), void (*free_func)(const char *str))
1055 {
1056    ELM_CHECK_WIDTYPE(obj, widtype);
1057    Widget_Data *wd = elm_widget_data_get(obj);
1058    if (!wd) return;
1059    wd->indicator_format_func = func;
1060    wd->indicator_format_free = free_func;
1061    _indicator_set(obj);
1062 }
1063
1064 /**
1065  * Set the format function pointer for the units area
1066  *
1067  * Set the callback function to format the indicator string.
1068  * See elm_slider_units_format_set() for more info on how this works.
1069  *
1070  * @param obj The slider object
1071  * @param indicator The format string for the units display
1072  * @param func The units format function
1073  * @param free_func The freeing function for the format string
1074  *
1075  * @ingroup Slider
1076  */
1077 EAPI void
1078 elm_slider_units_format_function_set(Evas_Object *obj, const char *(*func)(double val), void (*free_func)(const char *str))
1079 {
1080    ELM_CHECK_WIDTYPE(obj, widtype);
1081    Widget_Data *wd = elm_widget_data_get(obj);
1082    if (!wd) return;
1083    wd->units_format_func = func;
1084    wd->units_format_free = free_func;
1085    _indicator_set(obj);
1086 }
1087
1088 /**
1089  * Set the end object (rightmost widget) of the slider object.
1090  *
1091  * Once the end object is set, a previously set one will be deleted.
1092  * If you want to keep that old content object, use the
1093  * elm_button_end_unset() function.
1094  *
1095  * @param obj The slider object
1096  * @param end The end object
1097  *
1098  * @note If the object being set does not have minimum size hints set,
1099  * it won't get properly displayed.
1100  *
1101  * @ingroup Slider
1102  */
1103 EAPI void
1104 elm_slider_end_set(Evas_Object *obj, Evas_Object *end)
1105 {
1106    ELM_CHECK_WIDTYPE(obj, widtype);
1107    Widget_Data *wd = elm_widget_data_get(obj);
1108    if (!wd) return;
1109    if (wd->end == end) return;
1110    if (wd->end) evas_object_del(wd->end);
1111    wd->end = end;
1112    if (end)
1113      {
1114         elm_widget_sub_object_add(obj, end);
1115         evas_object_event_callback_add(end, EVAS_CALLBACK_CHANGED_SIZE_HINTS,
1116                                        _changed_size_hints, obj);
1117         edje_object_part_swallow(wd->slider, "elm.swallow.end", end);
1118         edje_object_signal_emit(wd->slider, "elm,state,end,visible", "elm");
1119         edje_object_message_signal_process(wd->slider);
1120      }
1121    _sizing_eval(obj);
1122 }
1123
1124 /**
1125  * Unset the rightmost widget of the slider, unparenting and
1126  * returning it.
1127  *
1128  * @param obj The slider object
1129  * @return the previously set end sub-object of this slider, on
1130  * success.
1131  *
1132  * @see elm_slider_end_set()
1133  *
1134  * @ingroup Slider
1135  */
1136 EAPI Evas_Object *
1137 elm_slider_end_unset(Evas_Object *obj)
1138 {
1139    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
1140    Widget_Data *wd = elm_widget_data_get(obj);
1141    Evas_Object *ret = NULL;
1142    if (!wd) return NULL;
1143    if (wd->end)
1144      {
1145         elm_widget_sub_object_del(obj, wd->end);
1146         ret = wd->end;
1147         edje_object_part_unswallow(wd->slider, wd->end);
1148         edje_object_signal_emit(wd->slider, "elm,state,end,hidden", "elm");
1149         wd->end = NULL;
1150         _sizing_eval(obj);
1151      }
1152    return ret;
1153 }
1154
1155 /**
1156  * Get the end icon object of the slider object. This object is owned
1157  * by the scrolled entry and should not be modified.
1158  *
1159  * @param obj The slider object
1160  * @return The end icon object
1161  *
1162  * @ingroup Slider
1163  */
1164 EAPI Evas_Object *
1165 elm_slider_end_get(const Evas_Object *obj)
1166 {
1167    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
1168    Widget_Data *wd = elm_widget_data_get(obj);
1169    if (!wd) return NULL;
1170    return wd->end;
1171 }
1172
1173 /**
1174  * Set whether to the slider indicator (augmented knob) at all.
1175  *
1176  * @param obj The slider object
1177  * @param show @c EINA_TRUE will make it show it, @c EINA_FALSE will
1178  * let the knob alwayes at default size.
1179  *
1180  * @note It will conflict with elm_slider_indicator_format_set(), if
1181  * you wanted those effects.
1182  *
1183  * @ingroup Slider
1184  */
1185 EAPI void
1186 elm_slider_indicator_show_set(Evas_Object *obj, Eina_Bool show)
1187 {
1188    ELM_CHECK_WIDTYPE(obj, widtype);
1189    Widget_Data *wd = elm_widget_data_get(obj);
1190    if (show) {
1191         wd->indicator_show = EINA_TRUE;
1192         edje_object_signal_emit(wd->slider, "elm,state,val,show", "elm");
1193    }
1194    else {
1195         wd->indicator_show = EINA_FALSE;
1196         edje_object_signal_emit(wd->slider, "elm,state,val,hide", "elm");
1197    }
1198 }
1199
1200 /**
1201  * Get the state of indicator in the slider (if it's being shown or
1202  * not).
1203  *
1204  * @param obj The slider object
1205  * @return @c EINA_TRUE if the indicator is being shown, @c EINA_FALSE
1206  * otherwise.
1207  *
1208  *  @ingroup Slider
1209  */
1210 EAPI Eina_Bool
1211 elm_slider_indicator_show_get(const Evas_Object *obj)
1212 {
1213    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
1214    Widget_Data *wd = elm_widget_data_get(obj);
1215    if (!wd) return EINA_FALSE;
1216    return wd->indicator_show;
1217 }
1218