elementary/datetime - elm_datetime - Open source patch : Separator parsing logic...
[framework/uifw/elementary.git] / src / lib / elm_slider.c
1 #include <Elementary.h>
2 #include "elm_priv.h"
3
4 typedef struct _Widget_Data Widget_Data;
5
6 struct _Widget_Data
7 {
8    Evas_Object *slider;
9    Evas_Object *icon;
10    Evas_Object *end;
11    Evas_Object *spacer;
12
13    Ecore_Timer *delay;
14
15    Eina_Hash  *labels;
16    const char *units;
17    const char *indicator;
18
19    char *(*indicator_format_func)(double val);
20    void (*indicator_format_free)(char *str);
21
22    char *(*units_format_func)(double val);
23    void (*units_format_free)(char *str);
24
25    double val, val_min, val_max, val2;
26    Evas_Coord size;
27    Evas_Coord downx, downy;
28
29    Eina_Bool horizontal : 1;
30    Eina_Bool inverted : 1;
31    Eina_Bool indicator_show : 1;
32    Eina_Bool spacer_down : 1;
33    Eina_Bool frozen : 1;
34 };
35
36 #define ELM_SLIDER_INVERTED_FACTOR (-1.0)
37
38 static const char *widtype = NULL;
39 static void _del_hook(Evas_Object *obj);
40 static void _mirrored_set(Evas_Object *obj, Eina_Bool rtl);
41 static void _theme_hook(Evas_Object *obj);
42 static void _disable_hook(Evas_Object *obj);
43 static void _sizing_eval(Evas_Object *obj);
44 static void _changed_size_hints(void *data, Evas *e, Evas_Object *obj, void *event_info);
45 static void _sub_del(void *data, Evas_Object *obj, void *event_info);
46 static void _units_set(Evas_Object *obj);
47 static void _val_set(Evas_Object *obj);
48 static void _indicator_set(Evas_Object *obj);
49 static void _on_focus_hook(void *data, Evas_Object *obj);
50 static void _drag_up(void *data, Evas_Object *obj,
51                     const char *emission, const char *source);
52 static void _drag_down(void *data, Evas_Object *obj,
53                     const char *emission, const char *source);
54 static Eina_Bool _event_hook(Evas_Object *obj, Evas_Object *src,
55                              Evas_Callback_Type type, void *event_info);
56 static void _spacer_down_cb(void *data, Evas * e, Evas_Object * obj, void *event_info);
57 static void _spacer_move_cb(void *data, Evas * e, Evas_Object * obj, void *event_info);
58 static void _spacer_up_cb(void *data, Evas * e, Evas_Object * obj, void *event_info);
59
60 static const char SIG_CHANGED[] = "changed";
61 static const char SIG_DELAY_CHANGED[] = "delay,changed";
62 static const char SIG_DRAG_START[] = "slider,drag,start";
63 static const char SIG_DRAG_STOP[] = "slider,drag,stop";
64 static const Evas_Smart_Cb_Description _signals[] = {
65   {SIG_CHANGED, ""},
66   {SIG_DELAY_CHANGED, ""},
67   {SIG_DRAG_START, ""},
68   {SIG_DRAG_STOP, ""},
69   {NULL, NULL}
70 };
71
72 static Eina_Bool
73 _event_hook(Evas_Object *obj, Evas_Object *src __UNUSED__, Evas_Callback_Type type, void *event_info)
74 {
75    Evas_Event_Mouse_Wheel *mev;
76    Evas_Event_Key_Down *ev;
77    Widget_Data *wd;
78
79    wd = elm_widget_data_get(obj);
80    if (!wd) return EINA_FALSE;
81
82    if (type == EVAS_CALLBACK_KEY_DOWN) goto key_down;
83    else if (type != EVAS_CALLBACK_MOUSE_WHEEL) return EINA_FALSE;
84
85    mev = event_info;
86    if (mev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return EINA_FALSE;
87    if (elm_widget_disabled_get(obj)) return EINA_FALSE;
88
89    if (mev->z < 0) _drag_up(obj, NULL, NULL, NULL);
90    else _drag_down(obj, NULL, NULL, NULL);
91    mev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
92    return EINA_TRUE;
93
94   key_down:
95    ev = event_info;
96    if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return EINA_FALSE;
97    if (elm_widget_disabled_get(obj)) return EINA_FALSE;
98    if ((!strcmp(ev->keyname, "Left")) ||
99        ((!strcmp(ev->keyname, "KP_Left")) && (!ev->string)))
100      {
101         if (!wd->horizontal) return EINA_FALSE;
102         if (!wd->inverted) _drag_down(obj, NULL, NULL, NULL);
103         else _drag_up(obj, NULL, NULL, NULL);
104         ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
105         return EINA_TRUE;
106      }
107    else if ((!strcmp(ev->keyname, "Right")) ||
108             ((!strcmp(ev->keyname, "KP_Right")) && (!ev->string)))
109      {
110         if (!wd->horizontal) return EINA_FALSE;
111         if (!wd->inverted) _drag_up(obj, NULL, NULL, NULL);
112         else _drag_down(obj, NULL, NULL, NULL);
113         ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
114         return EINA_TRUE;
115      }
116    else if ((!strcmp(ev->keyname, "Up")) ||
117             ((!strcmp(ev->keyname, "KP_Up")) && (!ev->string)))
118      {
119         if (wd->horizontal) return EINA_FALSE;
120         if (wd->inverted) _drag_up(obj, NULL, NULL, NULL);
121         else _drag_down(obj, NULL, NULL, NULL);
122         ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
123         return EINA_TRUE;
124      }
125    else if ((!strcmp(ev->keyname, "Down")) ||
126             ((!strcmp(ev->keyname, "KP_Down")) && (!ev->string)))
127      {
128         if (wd->horizontal) return EINA_FALSE;
129         if (wd->inverted) _drag_down(obj, NULL, NULL, NULL);
130         else _drag_up(obj, NULL, NULL, NULL);
131         ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
132         return EINA_TRUE;
133      }
134    else return EINA_FALSE;
135 }
136
137 static void
138 _del_hook(Evas_Object *obj)
139 {
140    Widget_Data *wd = elm_widget_data_get(obj);
141    if (!wd) return;
142    if (wd->labels) eina_hash_free(wd->labels);
143    if (wd->indicator) eina_stringshare_del(wd->units);
144    if (wd->delay) ecore_timer_del(wd->delay);
145    free(wd);
146 }
147
148 static void
149 _on_focus_hook(void *data __UNUSED__, Evas_Object *obj)
150 {
151    Widget_Data *wd = elm_widget_data_get(obj);
152    if (!wd) return;
153    if (elm_widget_focus_get(obj))
154      {
155         edje_object_signal_emit(wd->slider, "elm,action,focus", "elm");
156         evas_object_focus_set(wd->slider, EINA_TRUE);
157      }
158    else
159      {
160         edje_object_signal_emit(wd->slider, "elm,action,unfocus", "elm");
161         evas_object_focus_set(wd->slider, EINA_FALSE);
162      }
163 }
164
165 static void
166 _mirrored_set(Evas_Object *obj, Eina_Bool rtl)
167 {
168    Widget_Data *wd = elm_widget_data_get(obj);
169    if (!wd) return;
170    edje_object_mirrored_set(wd->slider, rtl);
171 }
172
173 static Eina_Bool
174 _labels_foreach_text_set(const Eina_Hash *hash __UNUSED__, const void *key, void *data, void *fdata)
175 {
176   Widget_Data *wd = fdata;
177
178   edje_object_part_text_set(wd->slider, key, data);
179
180   return 1;
181 }
182
183 static void
184 _theme_hook(Evas_Object *obj)
185 {
186    Widget_Data *wd = elm_widget_data_get(obj);
187    if (!wd) return;
188    _elm_widget_mirrored_reload(obj);
189    _mirrored_set(obj, elm_widget_mirrored_get(obj));
190    if (wd->horizontal)
191      _elm_theme_object_set(obj, wd->slider, "slider", "horizontal", elm_widget_style_get(obj));
192    else
193      _elm_theme_object_set(obj, wd->slider, "slider", "vertical", elm_widget_style_get(obj));
194    if (wd->icon)
195      {
196         edje_object_part_swallow(wd->slider, "elm.swallow.content", wd->icon);
197         edje_object_signal_emit(wd->slider, "elm,state,icon,visible", "elm");
198      }
199    if (wd->end)
200      edje_object_signal_emit(wd->slider, "elm,state,end,visible", "elm");
201    else
202      edje_object_signal_emit(wd->slider, "elm,state,end,hidden", "elm");
203    if (wd->labels)
204      {
205         eina_hash_foreach(wd->labels, _labels_foreach_text_set, wd);
206         edje_object_signal_emit(wd->slider, "elm,state,text,visible", "elm");
207      }
208
209    if (wd->units)
210      edje_object_signal_emit(wd->slider, "elm,state,units,visible", "elm");
211
212    if (wd->horizontal)
213      evas_object_size_hint_min_set(wd->spacer, (double)wd->size * elm_widget_scale_get(obj) * _elm_config->scale, 1);
214    else
215      evas_object_size_hint_min_set(wd->spacer, 1, (double)wd->size * elm_widget_scale_get(obj) * _elm_config->scale);
216
217    if (wd->inverted)
218      edje_object_signal_emit(wd->slider, "elm,state,inverted,on", "elm");
219
220    edje_object_part_swallow(wd->slider, "elm.swallow.bar", wd->spacer);
221    _units_set(obj);
222    _indicator_set(obj);
223    edje_object_message_signal_process(wd->slider);
224    edje_object_scale_set(wd->slider, elm_widget_scale_get(obj) * _elm_config->scale);
225    _val_set(obj);
226    _sizing_eval(obj);
227 }
228
229 static void
230 _disable_hook(Evas_Object *obj)
231 {
232    Widget_Data *wd = elm_widget_data_get(obj);
233    if (!wd) return;
234    if (elm_widget_disabled_get(obj))
235      edje_object_signal_emit(wd->slider, "elm,state,disabled", "elm");
236    else
237      edje_object_signal_emit(wd->slider, "elm,state,enabled", "elm");
238 }
239
240 static void
241 _sizing_eval(Evas_Object *obj)
242 {
243    Widget_Data *wd = elm_widget_data_get(obj);
244    Evas_Coord minw = -1, minh = -1, maxw = -1, maxh = -1;
245    if (!wd) return;
246    elm_coords_finger_size_adjust(1, &minw, 1, &minh);
247    edje_object_size_min_restricted_calc(wd->slider, &minw, &minh, minw, minh);
248    elm_coords_finger_size_adjust(1, &minw, 1, &minh);
249    evas_object_size_hint_min_set(obj, minw, minh);
250    evas_object_size_hint_max_set(obj, maxw, maxh);
251 }
252
253 static void
254 _changed_size_hints(void *data, Evas *e __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__)
255 {
256    Widget_Data *wd = elm_widget_data_get(data);
257    if (!wd) return;
258    if ((obj != wd->icon) && (obj != wd->end)) return;
259    _sizing_eval(data);
260 }
261
262 static void
263 _sub_del(void *data __UNUSED__, Evas_Object *obj, void *event_info)
264 {
265    Widget_Data *wd = elm_widget_data_get(obj);
266    Evas_Object *sub = event_info;
267    if (!wd) return;
268    if (sub == wd->icon)
269      {
270         edje_object_signal_emit(wd->slider, "elm,state,icon,hidden", "elm");
271         evas_object_event_callback_del_full
272            (sub, EVAS_CALLBACK_CHANGED_SIZE_HINTS, _changed_size_hints, obj);
273         wd->icon = NULL;
274         edje_object_message_signal_process(wd->slider);
275         _sizing_eval(obj);
276      }
277    if (sub == wd->end)
278      {
279         edje_object_signal_emit(wd->slider, "elm,state,end,hidden", "elm");
280         evas_object_event_callback_del_full(sub,
281                                             EVAS_CALLBACK_CHANGED_SIZE_HINTS,
282                                             _changed_size_hints, obj);
283         wd->end = NULL;
284         edje_object_message_signal_process(wd->slider);
285         _sizing_eval(obj);
286      }
287 }
288
289 static Eina_Bool
290 _delay_change(void *data)
291 {
292    Widget_Data *wd = elm_widget_data_get(data);
293    if (!wd) return ECORE_CALLBACK_CANCEL;
294    wd->delay = NULL;
295    evas_object_smart_callback_call(data, SIG_DELAY_CHANGED, NULL);
296    return ECORE_CALLBACK_CANCEL;
297 }
298
299 static void
300 _val_fetch(Evas_Object *obj)
301 {
302    Eina_Bool rtl;
303    Widget_Data *wd = elm_widget_data_get(obj);
304    double posx = 0.0, posy = 0.0, pos = 0.0, val;
305    if (!wd) return;
306    edje_object_part_drag_value_get(wd->slider, "elm.dragable.slider",
307                                    &posx, &posy);
308    if (wd->horizontal) pos = posx;
309    else pos = posy;
310
311    rtl = elm_widget_mirrored_get(obj);
312    if ((!rtl && wd->inverted) || (rtl &&
313                                   ((!wd->horizontal && wd->inverted) ||
314                                    (wd->horizontal && !wd->inverted)))) pos = 1.0 - pos;
315    val = (pos * (wd->val_max - wd->val_min)) + wd->val_min;
316    if (val != wd->val)
317      {
318         wd->val = val;
319         evas_object_smart_callback_call(obj, SIG_CHANGED, NULL);
320         if (wd->delay) ecore_timer_del(wd->delay);
321         wd->delay = ecore_timer_add(0.2, _delay_change, obj);
322      }
323 }
324
325 static void
326 _val_set(Evas_Object *obj)
327 {
328    Eina_Bool rtl;
329    Widget_Data *wd = elm_widget_data_get(obj);
330    double pos;
331    if (!wd) return;
332    if (wd->val_max > wd->val_min)
333      pos = (wd->val - wd->val_min) / (wd->val_max - wd->val_min);
334    else
335      pos = 0.0;
336    if (pos < 0.0) pos = 0.0;
337    else if (pos > 1.0) pos = 1.0;
338
339    rtl = elm_widget_mirrored_get(obj);
340    if ((!rtl && wd->inverted) || (rtl &&
341                                   ((!wd->horizontal && wd->inverted) ||
342                                    (wd->horizontal && !wd->inverted)))) pos = 1.0 - pos;
343    edje_object_part_drag_value_set(wd->slider, "elm.dragable.slider", pos, pos);
344 }
345
346 static void
347 _units_set(Evas_Object *obj)
348 {
349    Widget_Data *wd = elm_widget_data_get(obj);
350    if (!wd) return;
351    if (wd->units_format_func)
352      {
353         char *buf;
354         buf = wd->units_format_func(wd->val);
355         edje_object_part_text_set(wd->slider, "elm.units", buf);
356         if (wd->units_format_free) wd->units_format_free(buf);
357      }
358    else if (wd->units)
359      {
360         char buf[1024];
361
362         snprintf(buf, sizeof(buf), wd->units, wd->val);
363         edje_object_part_text_set(wd->slider, "elm.units", buf);
364      }
365    else
366      edje_object_part_text_set(wd->slider, "elm.units", NULL);
367 }
368
369 static void
370 _indicator_set(Evas_Object *obj)
371 {
372    Widget_Data *wd = elm_widget_data_get(obj);
373    if (!wd) return;
374    if (wd->indicator_format_func)
375      {
376         char *buf;
377         buf = wd->indicator_format_func(wd->val);
378         edje_object_part_text_set(wd->slider, "elm.dragable.slider:elm.indicator", buf);
379         if (wd->indicator_format_free) wd->indicator_format_free(buf);
380      }
381    else if (wd->indicator)
382      {
383         char buf[1024];
384         snprintf(buf, sizeof(buf), wd->indicator, wd->val);
385         edje_object_part_text_set(wd->slider, "elm.dragable.slider:elm.indicator", buf);
386      }
387    else
388      edje_object_part_text_set(wd->slider, "elm.dragable.slider:elm.indicator", NULL);
389 }
390
391 static void
392 _drag(void *data, Evas_Object *obj __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__)
393 {
394    _val_fetch(data);
395    _units_set(data);
396    _indicator_set(data);
397 }
398
399 static void
400 _drag_start(void *data, Evas_Object *obj __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__)
401 {
402    _val_fetch(data);
403    evas_object_smart_callback_call(data, SIG_DRAG_START, NULL);
404    _units_set(data);
405    _indicator_set(data);
406    elm_widget_scroll_freeze_push(data);
407 }
408
409 static void
410 _drag_stop(void *data, Evas_Object *obj __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__)
411 {
412    _val_fetch(data);
413    evas_object_smart_callback_call(data, SIG_DRAG_STOP, NULL);
414    _units_set(data);
415    _indicator_set(data);
416    elm_widget_scroll_freeze_pop(data);
417 }
418
419 static void
420 _drag_step(void *data, Evas_Object *obj __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__)
421 {
422    _val_fetch(data);
423    _units_set(data);
424    _indicator_set(data);
425 }
426
427 static void
428 _drag_up(void *data, Evas_Object *obj __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__)
429 {
430    double step;
431    Widget_Data *wd;
432
433    wd = elm_widget_data_get(data);
434    step = 0.05;
435
436    if (wd->inverted) step *= ELM_SLIDER_INVERTED_FACTOR;
437
438    edje_object_part_drag_step(wd->slider, "elm.dragable.slider", step, step);
439 }
440
441 static void
442 _drag_down(void *data, Evas_Object *obj __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__)
443 {
444    double step;
445    Widget_Data *wd;
446
447    wd = elm_widget_data_get(data);
448    step = -0.05;
449
450    if (wd->inverted) step *= ELM_SLIDER_INVERTED_FACTOR;
451
452    edje_object_part_drag_step(wd->slider, "elm.dragable.slider", step, step);
453 }
454
455 static void
456 _spacer_down_cb(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info)
457 {
458    Widget_Data *wd = elm_widget_data_get(data);
459    Evas_Event_Mouse_Down *ev = event_info;
460    Evas_Coord x, y, w, h;
461    double button_x = 0.0, button_y = 0.0;
462
463    wd->spacer_down = EINA_TRUE;
464    wd->val2 = wd->val;
465    evas_object_geometry_get(wd->spacer, &x, &y, &w, &h);
466    wd->downx = ev->canvas.x - x;
467    wd->downy = ev->canvas.y - y;
468    if (wd->horizontal)
469      {
470         button_x = ((double)ev->canvas.x - (double)x) / (double)w;
471         if (button_x > 1) button_x = 1;
472         if (button_x < 0) button_x = 0;
473      }
474    else
475      {
476         button_y = ((double)ev->canvas.y - (double)y) / (double)h;
477         if (button_y > 1) button_y = 1;
478         if (button_y < 0) button_y = 0;
479      }
480    edje_object_part_drag_value_set(wd->slider, "elm.dragable.slider", button_x, button_y);
481    _val_fetch(data);
482    evas_object_smart_callback_call(data, SIG_DRAG_START, NULL);
483    _units_set(data);
484    _indicator_set(data);
485    edje_object_signal_emit(wd->slider, "elm,state,indicator,show", "elm");
486 }
487
488 static void
489 _spacer_move_cb(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info)
490 {
491    Widget_Data *wd = elm_widget_data_get(data);
492    Evas_Event_Mouse_Move *ev = event_info;
493    Evas_Coord x, y, w, h;
494    double button_x = 0.0, button_y = 0.0;
495
496    if  (wd->spacer_down)
497      {
498         Evas_Coord d = 0;
499
500         evas_object_geometry_get(wd->spacer, &x, &y, &w, &h);
501         if (wd->horizontal) d = abs(ev->cur.canvas.x - x - wd->downx);
502         else d = abs(ev->cur.canvas.y - y - wd->downy);
503         if (d > (_elm_config->thumbscroll_threshold - 1))
504           {
505              if (!wd->frozen)
506                {
507                   elm_widget_scroll_freeze_push(data);
508                   wd->frozen = 1;
509                }
510              ev->event_flags &= ~EVAS_EVENT_FLAG_ON_HOLD;
511           }
512
513         if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD)
514           {
515              if (wd->spacer_down) wd->spacer_down = EINA_FALSE;
516              _val_fetch(data);
517              evas_object_smart_callback_call(data, SIG_DRAG_STOP, NULL);
518              _units_set(data);
519              _indicator_set(data);
520              if (wd->frozen)
521                {
522                   elm_widget_scroll_freeze_pop(data);
523                   wd->frozen = 0;
524                }
525              edje_object_signal_emit(wd->slider, "elm,state,indicator,hide", "elm");
526              elm_slider_value_set(data, wd->val2);
527              return;
528           }
529         if (wd->horizontal)
530           {
531              button_x = ((double)ev->cur.canvas.x - (double)x) / (double)w;
532              if (button_x > 1) button_x = 1;
533              if (button_x < 0) button_x = 0;
534           }
535         else
536           {
537              button_y = ((double)ev->cur.canvas.y - (double)y) / (double)h;
538              if (button_y > 1) button_y = 1;
539              if (button_y < 0) button_y = 0;
540           }
541         edje_object_part_drag_value_set(wd->slider, "elm.dragable.slider", button_x, button_y);
542         _val_fetch(data);
543         _units_set(data);
544         _indicator_set(data);
545      }
546 }
547
548 static void
549 _spacer_up_cb(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
550 {
551    Widget_Data *wd = elm_widget_data_get(data);
552
553    if (!wd->spacer_down) return;
554    if (wd->spacer_down) wd->spacer_down = EINA_FALSE;
555    _val_fetch(data);
556    evas_object_smart_callback_call(data, SIG_DRAG_STOP, NULL);
557    _units_set(data);
558    _indicator_set(data);
559    if (wd->frozen)
560      {
561         elm_widget_scroll_freeze_pop(data);
562         wd->frozen = 0;
563      }
564    edje_object_signal_emit(wd->slider, "elm,state,indicator,hide", "elm");
565 }
566
567 static void
568 _elm_slider_label_set(Evas_Object *obj, const char *part, const char *label)
569 {
570    ELM_CHECK_WIDTYPE(obj, widtype);
571    Widget_Data *wd = elm_widget_data_get(obj);
572    const char* default_part = "elm.text";
573    const char* real_part;
574
575    if (!wd) return;
576
577    if (!part)
578      real_part = default_part;
579    else
580      real_part = part;
581
582    if (wd->labels)
583      {
584        const char* old_label;
585
586        old_label = eina_hash_find(wd->labels, real_part);
587        if (!old_label)
588            eina_hash_add(wd->labels, real_part, eina_stringshare_add(label));
589        else
590          {
591            eina_stringshare_ref(old_label);
592            eina_hash_modify(wd->labels, real_part, eina_stringshare_add(label));
593            eina_stringshare_del(old_label);
594          }
595      }
596
597    if (label)
598      {
599         edje_object_signal_emit(wd->slider, "elm,state,text,visible", "elm");
600         edje_object_message_signal_process(wd->slider);
601      }
602    else
603      {
604         edje_object_signal_emit(wd->slider, "elm,state,text,hidden", "elm");
605         edje_object_message_signal_process(wd->slider);
606      }
607
608    edje_object_part_text_set(wd->slider, real_part, label);
609    _sizing_eval(obj);
610 }
611
612 static const char *
613 _elm_slider_label_get(const Evas_Object *obj, const char *part)
614 {
615    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
616    Widget_Data *wd = elm_widget_data_get(obj);
617    if (!wd) return NULL;
618    if (!wd->labels) return NULL;
619
620    if (!part)
621      return eina_hash_find(wd->labels, "elm.text");
622    return eina_hash_find(wd->labels, part);
623 }
624
625 static void
626 _icon_set(Evas_Object *obj, Evas_Object *icon)
627 {
628    Widget_Data *wd = elm_widget_data_get(obj);
629    if (!wd) return;
630    if (wd->icon == icon) return;
631    if (wd->icon) evas_object_del(wd->icon);
632    wd->icon = icon;
633    if (icon)
634      {
635         elm_widget_sub_object_add(obj, icon);
636         evas_object_event_callback_add(icon, EVAS_CALLBACK_CHANGED_SIZE_HINTS,
637                                        _changed_size_hints, obj);
638         edje_object_part_swallow(wd->slider, "elm.swallow.icon", icon);
639         edje_object_signal_emit(wd->slider, "elm,state,icon,visible", "elm");
640         edje_object_message_signal_process(wd->slider);
641      }
642    _sizing_eval(obj);
643 }
644
645 static Evas_Object *
646 _icon_unset(Evas_Object *obj)
647 {
648    Widget_Data *wd = elm_widget_data_get(obj);
649    Evas_Object *ret = NULL;
650    if (!wd) return NULL;
651    if (wd->icon)
652      {
653         elm_widget_sub_object_del(obj, wd->icon);
654         evas_object_event_callback_del_full(wd->icon,
655                                             EVAS_CALLBACK_CHANGED_SIZE_HINTS,
656                                             _changed_size_hints, obj);
657         ret = wd->icon;
658         edje_object_part_unswallow(wd->slider, wd->icon);
659         edje_object_signal_emit(wd->slider, "elm,state,icon,hidden", "elm");
660         wd->icon = NULL;
661         _sizing_eval(obj);
662      }
663    return ret;
664 }
665
666 static void
667 _end_set(Evas_Object *obj, Evas_Object *end)
668 {
669    Widget_Data *wd = elm_widget_data_get(obj);
670    if (!wd) return;
671    if (wd->end == end) return;
672    if (wd->end) evas_object_del(wd->end);
673    wd->end = end;
674    if (end)
675      {
676         elm_widget_sub_object_add(obj, end);
677         evas_object_event_callback_add(end, EVAS_CALLBACK_CHANGED_SIZE_HINTS,
678                                        _changed_size_hints, obj);
679         edje_object_part_swallow(wd->slider, "elm.swallow.end", end);
680         edje_object_signal_emit(wd->slider, "elm,state,end,visible", "elm");
681         edje_object_message_signal_process(wd->slider);
682      }
683    _sizing_eval(obj);
684 }
685
686 static Evas_Object *
687 _end_unset(Evas_Object *obj)
688 {
689    Widget_Data *wd = elm_widget_data_get(obj);
690    Evas_Object *ret = NULL;
691    if (!wd) return NULL;
692    if (wd->end)
693      {
694         elm_widget_sub_object_del(obj, wd->end);
695         evas_object_event_callback_del_full(wd->end,
696                                             EVAS_CALLBACK_CHANGED_SIZE_HINTS,
697                                             _changed_size_hints, obj);
698         ret = wd->end;
699         edje_object_part_unswallow(wd->slider, wd->end);
700         edje_object_signal_emit(wd->slider, "elm,state,end,hidden", "elm");
701         wd->end = NULL;
702         _sizing_eval(obj);
703      }
704    return ret;
705 }
706
707 static void
708 _content_set_hook(Evas_Object *obj, const char *part, Evas_Object *content)
709 {
710    ELM_CHECK_WIDTYPE(obj, widtype);
711    if (!part || !strcmp(part, "icon"))
712      _icon_set(obj, content);
713    else if (!strcmp(part, "end"))
714      _end_set(obj, content);
715 }
716
717 static Evas_Object *
718 _content_get_hook(const Evas_Object *obj, const char *part)
719 {
720    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
721    Widget_Data *wd;
722    wd = elm_widget_data_get(obj);
723    if (!wd) return NULL;
724    if (!part || !strcmp(part, "icon"))
725      return wd->icon;
726    else if (!strcmp(part, "end"))
727      return wd->end;
728    return NULL;
729 }
730
731 static Evas_Object *
732 _content_unset_hook(Evas_Object *obj, const char *part)
733 {
734    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
735    if (!part || !strcmp(part, "icon"))
736      return _icon_unset(obj);
737    else if (!strcmp(part, "end"))
738      return _end_unset(obj);
739    return NULL;
740 }
741
742 static void
743 _hash_labels_free_cb(void* label)
744 {
745   if (label)
746     eina_stringshare_del(label);
747 }
748
749 static void
750 _min_max_set(Evas_Object *obj)
751 {
752    char *buf_min = NULL;
753    char *buf_max = NULL;
754
755    Widget_Data *wd = elm_widget_data_get(obj);
756    if (!wd) return;
757    if (wd->units_format_func)
758      {
759         buf_min = wd->units_format_func(wd->val_min);
760         buf_max = wd->units_format_func(wd->val_max);
761      }
762    else if (wd->units)
763      {
764         int length = strlen(wd->units);
765
766         buf_min = alloca(length + 128);
767         buf_max = alloca(length + 128);
768
769         snprintf((char*) buf_min, length + 128, wd->units, wd->val_min);
770         snprintf((char*) buf_max, length + 128, wd->units, wd->val_max);
771      }
772
773    edje_object_part_text_set(wd->slider, "elm.units.min", buf_min);
774    edje_object_part_text_set(wd->slider, "elm.units.max", buf_max);
775
776    if (wd->units_format_func && wd->units_format_free)
777      {
778         wd->units_format_free(buf_min);
779         wd->units_format_free(buf_max);
780      }
781 }
782
783
784 EAPI Evas_Object *
785 elm_slider_add(Evas_Object *parent)
786 {
787    Evas_Object *obj;
788    Evas *e;
789    Widget_Data *wd;
790
791    ELM_WIDGET_STANDARD_SETUP(wd, Widget_Data, parent, e, obj, NULL);
792
793    ELM_SET_WIDTYPE(widtype, "slider");
794    elm_widget_type_set(obj, "slider");
795    elm_widget_sub_object_add(parent, obj);
796    elm_widget_on_focus_hook_set(obj, _on_focus_hook, NULL);
797    elm_widget_data_set(obj, wd);
798    elm_widget_del_hook_set(obj, _del_hook);
799    elm_widget_theme_hook_set(obj, _theme_hook);
800    elm_widget_disable_hook_set(obj, _disable_hook);
801    elm_widget_can_focus_set(obj, EINA_TRUE);
802    elm_widget_event_hook_set(obj, _event_hook);
803    elm_widget_text_set_hook_set(obj, _elm_slider_label_set);
804    elm_widget_text_get_hook_set(obj, _elm_slider_label_get);
805    elm_widget_content_set_hook_set(obj, _content_set_hook);
806    elm_widget_content_get_hook_set(obj, _content_get_hook);
807    elm_widget_content_unset_hook_set(obj, _content_unset_hook);
808
809    wd->horizontal = EINA_TRUE;
810    wd->indicator_show = EINA_TRUE;
811    wd->val = 0.0;
812    wd->val_min = 0.0;
813    wd->val_max = 1.0;
814    wd->labels = eina_hash_string_superfast_new(_hash_labels_free_cb);
815
816    wd->slider = edje_object_add(e);
817    _elm_theme_object_set(obj, wd->slider, "slider", "horizontal", "default");
818    elm_widget_resize_object_set(obj, wd->slider);
819    edje_object_signal_callback_add(wd->slider, "drag", "*", _drag, obj);
820    edje_object_signal_callback_add(wd->slider, "drag,start", "*", _drag_start, obj);
821    edje_object_signal_callback_add(wd->slider, "drag,stop", "*", _drag_stop, obj);
822    edje_object_signal_callback_add(wd->slider, "drag,step", "*", _drag_step, obj);
823    edje_object_signal_callback_add(wd->slider, "drag,page", "*", _drag_stop, obj);
824    //   edje_object_signal_callback_add(wd->slider, "drag,set", "*", _drag_stop, obj);
825    edje_object_part_drag_value_set(wd->slider, "elm.dragable.slider", 0.0, 0.0);
826
827    wd->spacer = evas_object_rectangle_add(e);
828    evas_object_color_set(wd->spacer, 0, 0, 0, 0);
829    evas_object_pass_events_set(wd->spacer, EINA_TRUE);
830    elm_widget_sub_object_add(obj, wd->spacer);
831    edje_object_part_swallow(wd->slider, "elm.swallow.bar", wd->spacer);
832    evas_object_event_callback_add(wd->spacer, EVAS_CALLBACK_MOUSE_DOWN, _spacer_down_cb, obj);
833    evas_object_event_callback_add(wd->spacer, EVAS_CALLBACK_MOUSE_MOVE, _spacer_move_cb, obj);
834    evas_object_event_callback_add(wd->spacer, EVAS_CALLBACK_MOUSE_UP, _spacer_up_cb, obj);
835    evas_object_smart_callback_add(obj, "sub-object-del", _sub_del, obj);
836
837    _mirrored_set(obj, elm_widget_mirrored_get(obj));
838    _sizing_eval(obj);
839
840    // TODO: convert Elementary to subclassing of Evas_Smart_Class
841    // TODO: and save some bytes, making descriptions per-class and not instance!
842    evas_object_smart_callbacks_descriptions_set(obj, _signals);
843    return obj;
844 }
845
846 EAPI void
847 elm_slider_span_size_set(Evas_Object *obj, Evas_Coord size)
848 {
849    ELM_CHECK_WIDTYPE(obj, widtype);
850    Widget_Data *wd = elm_widget_data_get(obj);
851    if (!wd) return;
852    if (wd->size == size) return;
853    wd->size = size;
854    if (wd->horizontal)
855      evas_object_size_hint_min_set(wd->spacer, (double)wd->size * elm_widget_scale_get(obj) * _elm_config->scale, 1);
856    else
857      evas_object_size_hint_min_set(wd->spacer, 1, (double)wd->size * elm_widget_scale_get(obj) * _elm_config->scale);
858    if (wd->indicator_show)
859      edje_object_signal_emit(wd->slider, "elm,state,val,show", "elm");
860    else
861      edje_object_signal_emit(wd->slider, "elm,state,val,hide", "elm");
862    edje_object_part_swallow(wd->slider, "elm.swallow.bar", wd->spacer);
863    _sizing_eval(obj);
864 }
865
866 EAPI Evas_Coord
867 elm_slider_span_size_get(const Evas_Object *obj)
868 {
869    ELM_CHECK_WIDTYPE(obj, widtype) 0;
870    Widget_Data *wd = elm_widget_data_get(obj);
871    if (!wd) return 0;
872    return wd->size;
873 }
874
875 EAPI void
876 elm_slider_unit_format_set(Evas_Object *obj, const char *units)
877 {
878    ELM_CHECK_WIDTYPE(obj, widtype);
879    Widget_Data *wd = elm_widget_data_get(obj);
880    if (!wd) return;
881    eina_stringshare_replace(&wd->units, units);
882    if (units)
883      {
884         edje_object_signal_emit(wd->slider, "elm,state,units,visible", "elm");
885         edje_object_message_signal_process(wd->slider);
886      }
887    else
888      {
889         edje_object_signal_emit(wd->slider, "elm,state,units,hidden", "elm");
890         edje_object_message_signal_process(wd->slider);
891      }
892    _min_max_set(obj);
893    _units_set(obj);
894    _sizing_eval(obj);
895 }
896
897 EAPI const char *
898 elm_slider_unit_format_get(const Evas_Object *obj)
899 {
900    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
901    Widget_Data *wd = elm_widget_data_get(obj);
902    if (!wd) return NULL;
903    return wd->units;
904 }
905
906 EAPI void
907 elm_slider_indicator_format_set(Evas_Object *obj, const char *indicator)
908 {
909    ELM_CHECK_WIDTYPE(obj, widtype);
910    Widget_Data *wd = elm_widget_data_get(obj);
911    if (!wd) return;
912    eina_stringshare_replace(&wd->indicator, indicator);
913    _indicator_set(obj);
914 }
915
916 EAPI const char *
917 elm_slider_indicator_format_get(const Evas_Object *obj)
918 {
919    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
920    Widget_Data *wd = elm_widget_data_get(obj);
921    if (!wd) return NULL;
922    return wd->indicator;
923 }
924
925 EAPI void
926 elm_slider_horizontal_set(Evas_Object *obj, Eina_Bool horizontal)
927 {
928    ELM_CHECK_WIDTYPE(obj, widtype);
929    Widget_Data *wd = elm_widget_data_get(obj);
930    if (!wd) return;
931    horizontal = !!horizontal;
932    if (wd->horizontal == horizontal) return;
933    wd->horizontal = horizontal;
934    _theme_hook(obj);
935 }
936
937 EAPI Eina_Bool
938 elm_slider_horizontal_get(const Evas_Object *obj)
939 {
940    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
941    Widget_Data *wd = elm_widget_data_get(obj);
942    if (!wd) return EINA_FALSE;
943    return wd->horizontal;
944 }
945
946 EAPI void
947 elm_slider_min_max_set(Evas_Object *obj, double min, double max)
948 {
949    ELM_CHECK_WIDTYPE(obj, widtype);
950    Widget_Data *wd = elm_widget_data_get(obj);
951    if (!wd) return;
952    if ((wd->val_min == min) && (wd->val_max == max)) return;
953    wd->val_min = min;
954    wd->val_max = max;
955    if (wd->val < wd->val_min) wd->val = wd->val_min;
956    if (wd->val > wd->val_max) wd->val = wd->val_max;
957    _min_max_set(obj);
958    _val_set(obj);
959    _units_set(obj);
960    _indicator_set(obj);
961 }
962
963 EAPI void
964 elm_slider_min_max_get(const Evas_Object *obj, double *min, double *max)
965 {
966    if (min) *min = 0.0;
967    if (max) *max = 0.0;
968    ELM_CHECK_WIDTYPE(obj, widtype);
969    Widget_Data *wd = elm_widget_data_get(obj);
970    if (!wd) return;
971    if (min) *min = wd->val_min;
972    if (max) *max = wd->val_max;
973 }
974
975 EAPI void
976 elm_slider_value_set(Evas_Object *obj, double val)
977 {
978    ELM_CHECK_WIDTYPE(obj, widtype);
979    Widget_Data *wd = elm_widget_data_get(obj);
980    if (!wd) return;
981    if (wd->val == val) return;
982    wd->val = val;
983    if (wd->val < wd->val_min) wd->val = wd->val_min;
984    if (wd->val > wd->val_max) wd->val = wd->val_max;
985    _val_set(obj);
986    _units_set(obj);
987    _indicator_set(obj);
988 }
989
990 EAPI double
991 elm_slider_value_get(const Evas_Object *obj)
992 {
993    ELM_CHECK_WIDTYPE(obj, widtype) 0.0;
994    Widget_Data *wd = elm_widget_data_get(obj);
995    if (!wd) return 0.0;
996    return wd->val;
997 }
998
999 EAPI void
1000 elm_slider_inverted_set(Evas_Object *obj, Eina_Bool inverted)
1001 {
1002    ELM_CHECK_WIDTYPE(obj, widtype);
1003    Widget_Data *wd = elm_widget_data_get(obj);
1004    if (!wd) return;
1005    inverted = !!inverted;
1006    if (wd->inverted == inverted) return;
1007    wd->inverted = inverted;
1008    if (wd->inverted)
1009      edje_object_signal_emit(wd->slider, "elm,state,inverted,on", "elm");
1010    else
1011      edje_object_signal_emit(wd->slider, "elm,state,inverted,off", "elm");
1012    edje_object_message_signal_process(wd->slider);
1013    _val_set(obj);
1014    _units_set(obj);
1015    _indicator_set(obj);
1016 }
1017
1018 EAPI Eina_Bool
1019 elm_slider_inverted_get(const Evas_Object *obj)
1020 {
1021    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
1022    Widget_Data *wd = elm_widget_data_get(obj);
1023    if (!wd) return EINA_FALSE;
1024    return wd->inverted;
1025 }
1026
1027 EAPI void
1028 elm_slider_indicator_format_function_set(Evas_Object *obj, char *(*func)(double val), void (*free_func)(char *str))
1029 {
1030    ELM_CHECK_WIDTYPE(obj, widtype);
1031    Widget_Data *wd = elm_widget_data_get(obj);
1032    if (!wd) return;
1033    wd->indicator_format_func = func;
1034    wd->indicator_format_free = free_func;
1035    _indicator_set(obj);
1036 }
1037
1038 EAPI void
1039 elm_slider_units_format_function_set(Evas_Object *obj, char *(*func)(double val), void (*free_func)(char *str))
1040 {
1041    ELM_CHECK_WIDTYPE(obj, widtype);
1042    Widget_Data *wd = elm_widget_data_get(obj);
1043    if (!wd) return;
1044    wd->units_format_func = func;
1045    wd->units_format_free = free_func;
1046    _min_max_set(obj);
1047    _units_set(obj);
1048 }
1049
1050 EAPI void
1051 elm_slider_indicator_show_set(Evas_Object *obj, Eina_Bool show)
1052 {
1053    ELM_CHECK_WIDTYPE(obj, widtype);
1054    Widget_Data *wd = elm_widget_data_get(obj);
1055    if (show) {
1056         wd->indicator_show = EINA_TRUE;
1057         edje_object_signal_emit(wd->slider, "elm,state,val,show", "elm");
1058    }
1059    else {
1060         wd->indicator_show = EINA_FALSE;
1061         edje_object_signal_emit(wd->slider, "elm,state,val,hide", "elm");
1062    }
1063 }
1064
1065 EAPI Eina_Bool
1066 elm_slider_indicator_show_get(const Evas_Object *obj)
1067 {
1068    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
1069    Widget_Data *wd = elm_widget_data_get(obj);
1070    if (!wd) return EINA_FALSE;
1071    return wd->indicator_show;
1072 }
1073