elementary/map - map supports language,changed
[framework/uifw/elementary.git] / src / lib / elm_slider.c
1 #include <Elementary.h>
2 #include "elm_priv.h"
3 #include "elm_widget_slider.h"
4
5 EAPI const char ELM_SLIDER_SMART_NAME[] = "elm_slider";
6
7 static const Elm_Layout_Part_Alias_Description _content_aliases[] =
8 {
9    {"icon", "elm.swallow.icon"},
10    {"end", "elm.swallow.end"},
11    {NULL, NULL}
12 };
13
14 static const Elm_Layout_Part_Alias_Description _text_aliases[] =
15 {
16    {"default", "elm.text"},
17    {NULL, NULL}
18 };
19
20 static const char SIG_CHANGED[] = "changed";
21 static const char SIG_DELAY_CHANGED[] = "delay,changed";
22 static const char SIG_DRAG_START[] = "slider,drag,start";
23 static const char SIG_DRAG_STOP[] = "slider,drag,stop";
24 static const Evas_Smart_Cb_Description _smart_callbacks[] = {
25    {SIG_CHANGED, ""},
26    {SIG_DELAY_CHANGED, ""},
27    {SIG_DRAG_START, ""},
28    {SIG_DRAG_STOP, ""},
29    {NULL, NULL}
30 };
31
32 EVAS_SMART_SUBCLASS_NEW
33   (ELM_SLIDER_SMART_NAME, _elm_slider, Elm_Slider_Smart_Class,
34   Elm_Layout_Smart_Class, elm_layout_smart_class_get, _smart_callbacks);
35
36 static Eina_Bool
37 _delay_change(void *data)
38 {
39    ELM_SLIDER_DATA_GET(data, sd);
40
41    sd->delay = NULL;
42    evas_object_smart_callback_call(data, SIG_DELAY_CHANGED, NULL);
43
44    return ECORE_CALLBACK_CANCEL;
45 }
46
47 static void
48 _val_fetch(Evas_Object *obj)
49 {
50    Eina_Bool rtl;
51    double posx = 0.0, posy = 0.0, pos = 0.0, val;
52    char text[1024] = {0,};
53    Eina_Strbuf *buf = NULL;
54    char *str = NULL;
55
56    ELM_SLIDER_DATA_GET(obj, sd);
57
58    edje_object_part_drag_value_get
59      (ELM_WIDGET_DATA(sd)->resize_obj, "elm.dragable.slider", &posx, &posy);
60    if (sd->horizontal) pos = posx;
61    else pos = posy;
62
63    rtl = elm_widget_mirrored_get(obj);
64    if ((!rtl && sd->inverted) ||
65        (rtl && ((!sd->horizontal && sd->inverted) ||
66                 (sd->horizontal && !sd->inverted))))
67      pos = 1.0 - pos;
68
69    val = (pos * (sd->val_max - sd->val_min)) + sd->val_min;
70    if ((float)val != (float)sd->val)
71      {
72         sd->val = val;
73         evas_object_smart_callback_call(obj, SIG_CHANGED, NULL);
74         if (sd->delay) ecore_timer_del(sd->delay);
75         sd->delay = ecore_timer_add(0.2, _delay_change, obj);
76
77         if (_elm_config->access_mode != ELM_ACCESS_MODE_OFF)
78           {
79              buf = eina_strbuf_new();
80              if (sd->indicator_format_func)
81                {
82                   str = sd->indicator_format_func(sd->val);
83                   eina_strbuf_append(buf, str);
84                   if (sd->indicator_format_free) sd->indicator_format_free(str);
85                   eina_strbuf_append(buf, E_(" of "));
86                   str = sd->indicator_format_func(sd->val_max);
87                   eina_strbuf_append(buf, str);
88                   if (sd->indicator_format_free) sd->indicator_format_free(str);
89                }
90              else if (sd->indicator)
91                {
92                   snprintf(text, sizeof(text), sd->indicator, sd->val);
93                   eina_strbuf_append(buf, text);
94                   eina_strbuf_append(buf, E_(" of "));
95                   snprintf(text, sizeof(text), sd->indicator, sd->val_max);
96                   eina_strbuf_append(buf, text);
97                }
98
99              _elm_access_say(eina_strbuf_string_get(buf));
100              eina_strbuf_free(buf);
101           }
102      }
103 }
104
105 static void
106 _val_set(Evas_Object *obj)
107 {
108    Eina_Bool rtl;
109    double pos;
110
111    ELM_SLIDER_DATA_GET(obj, sd);
112
113    if (sd->val_max > sd->val_min)
114      pos = (sd->val - sd->val_min) / (sd->val_max - sd->val_min);
115    else pos = 0.0;
116
117    if (pos < 0.0) pos = 0.0;
118    else if (pos > 1.0)
119      pos = 1.0;
120
121    rtl = elm_widget_mirrored_get(obj);
122    if ((!rtl && sd->inverted) ||
123        (rtl && ((!sd->horizontal && sd->inverted) ||
124                 (sd->horizontal && !sd->inverted))))
125      pos = 1.0 - pos;
126
127    edje_object_part_drag_value_set
128      (ELM_WIDGET_DATA(sd)->resize_obj, "elm.dragable.slider", pos, pos);
129 }
130
131 static void
132 _units_set(Evas_Object *obj)
133 {
134    ELM_SLIDER_DATA_GET(obj, sd);
135
136    if (sd->units_format_func)
137      {
138         char *buf;
139
140         buf = sd->units_format_func(sd->val);
141         elm_layout_text_set(obj, "elm.units", buf);
142
143         if (sd->units_format_free) sd->units_format_free(buf);
144      }
145    else if (sd->units)
146      {
147         char buf[1024];
148
149         snprintf(buf, sizeof(buf), sd->units, sd->val);
150         elm_layout_text_set(obj, "elm.units", buf);
151      }
152    else elm_layout_text_set(obj, "elm.units", NULL);
153 }
154
155 static void
156 _indicator_set(Evas_Object *obj)
157 {
158    ELM_SLIDER_DATA_GET(obj, sd);
159
160    if (sd->indicator_format_func)
161      {
162         char *buf;
163
164         buf = sd->indicator_format_func(sd->val);
165         elm_layout_text_set(obj, "elm.indicator", buf);
166         elm_layout_text_set(obj, "elm.dragable.slider:elm.indicator", buf);
167         if (sd->popup)
168           edje_object_part_text_set(sd->popup, "elm.indicator", buf);
169
170         if (sd->indicator_format_free) sd->indicator_format_free(buf);
171      }
172    else if (sd->indicator)
173      {
174         char buf[1024];
175
176         snprintf(buf, sizeof(buf), sd->indicator, sd->val);
177         elm_layout_text_set(obj, "elm.indicator", buf);
178         elm_layout_text_set(obj, "elm.dragable.slider:elm.indicator", buf);
179         if (sd->popup)
180           edje_object_part_text_set(sd->popup, "elm.indicator", buf);
181      }
182    else
183      {
184         elm_layout_text_set(obj, "elm.indicator", NULL);
185         elm_layout_text_set(obj, "elm.dragable.slider:elm.indicator", NULL);
186         if (sd->popup)
187           edje_object_part_text_set(sd->popup, "elm.indicator", NULL);
188      }
189 }
190
191 static void
192 _slider_update(Evas_Object *obj)
193 {
194    evas_object_smart_changed(obj);
195 }
196
197 static void
198 _drag(void *data,
199       Evas_Object *obj __UNUSED__,
200       const char *emission __UNUSED__,
201       const char *source __UNUSED__)
202 {
203    _slider_update(data);
204 }
205
206 static void
207 _drag_start(void *data,
208             Evas_Object *obj __UNUSED__,
209             const char *emission __UNUSED__,
210             const char *source __UNUSED__)
211 {
212    _slider_update(data);
213    evas_object_smart_callback_call(data, SIG_DRAG_START, NULL);
214    elm_widget_scroll_freeze_push(data);
215 }
216
217 static void
218 _drag_stop(void *data,
219            Evas_Object *obj __UNUSED__,
220            const char *emission __UNUSED__,
221            const char *source __UNUSED__)
222 {
223    _slider_update(data);
224    evas_object_smart_callback_call(data, SIG_DRAG_STOP, NULL);
225    elm_widget_scroll_freeze_pop(data);
226 }
227
228 static void
229 _drag_step(void *data,
230            Evas_Object *obj __UNUSED__,
231            const char *emission __UNUSED__,
232            const char *source __UNUSED__)
233 {
234    _slider_update(data);
235 }
236
237 static void
238 _drag_up(void *data,
239          Evas_Object *obj __UNUSED__,
240          const char *emission __UNUSED__,
241          const char *source __UNUSED__)
242 {
243    double step;
244
245    ELM_SLIDER_DATA_GET(data, sd);
246    step = 0.05;
247
248    if (sd->inverted) step *= -1.0;
249
250    edje_object_part_drag_step
251      (ELM_WIDGET_DATA(sd)->resize_obj, "elm.dragable.slider", step, step);
252 }
253
254 static void
255 _drag_down(void *data,
256            Evas_Object *obj __UNUSED__,
257            const char *emission __UNUSED__,
258            const char *source __UNUSED__)
259 {
260    double step;
261
262    ELM_SLIDER_DATA_GET(data, sd);
263    step = -0.05;
264
265    if (sd->inverted) step *= -1.0;
266
267    edje_object_part_drag_step
268      (ELM_WIDGET_DATA(sd)->resize_obj, "elm.dragable.slider", step, step);
269 }
270
271 static void
272 _popup_show(void *data,
273             Evas_Object *obj __UNUSED__,
274             const char *emission __UNUSED__,
275             const char *source __UNUSED__)
276 {
277    ELM_SLIDER_DATA_GET(data, sd);
278    if (sd->popup)
279      {
280         evas_object_layer_set(sd->popup, evas_object_layer_get(data));
281         evas_object_raise(sd->popup);
282         evas_object_show(sd->popup);
283         edje_object_signal_emit(sd->popup, "popup,show", "elm");
284      }
285 }
286
287 static void
288 _popup_hide(void *data,
289             Evas_Object *obj __UNUSED__,
290             const char *emission __UNUSED__,
291             const char *source __UNUSED__)
292 {
293    ELM_SLIDER_DATA_GET(data, sd);
294    if (sd->popup)
295      {
296         if (!sd->popup_hiding)
297           {
298              edje_object_signal_emit(sd->popup, "popup,hide", "elm");
299              sd->popup_hiding = EINA_TRUE;
300           }
301      }
302 }
303
304 static void
305 _popup_hide_done(void *data,
306                  Evas_Object *obj __UNUSED__,
307                  const char *emission __UNUSED__,
308                  const char *source __UNUSED__)
309 {
310    ELM_SLIDER_DATA_GET(data, sd);
311    if (sd->popup)
312      {
313         if (sd->popup_hiding)
314           {
315              evas_object_hide(sd->popup);
316              sd->popup_hiding = EINA_FALSE;
317           }
318      }
319 }
320
321 static void
322 _popup_emit(void *data,
323             Evas_Object *obj __UNUSED__,
324             const char *emission,
325             const char *source)
326 {
327    ELM_SLIDER_DATA_GET(data, sd);
328    if (sd->popup)
329      {
330         edje_object_signal_emit(sd->popup, emission, source);
331      }
332 }
333
334 static Eina_Bool
335 _elm_slider_smart_event(Evas_Object *obj,
336                         Evas_Object *src __UNUSED__,
337                         Evas_Callback_Type type,
338                         void *event_info)
339 {
340    Evas_Event_Mouse_Wheel *mev;
341    Evas_Event_Key_Down *ev;
342
343    ELM_SLIDER_DATA_GET(obj, sd);
344
345    if (elm_widget_disabled_get(obj)) return EINA_FALSE;
346
347    if (type == EVAS_CALLBACK_KEY_DOWN) goto key_down;
348    else if (type != EVAS_CALLBACK_MOUSE_WHEEL)
349      return EINA_FALSE;
350
351    mev = event_info;
352    if (mev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return EINA_FALSE;
353
354    if (mev->z < 0) _drag_up(obj, NULL, NULL, NULL);
355    else _drag_down(obj, NULL, NULL, NULL);
356    mev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
357
358    goto success;
359
360 key_down:
361    ev = event_info;
362    if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return EINA_FALSE;
363    if (elm_widget_disabled_get(obj)) return EINA_FALSE;
364    if ((!strcmp(ev->keyname, "Left")) ||
365        ((!strcmp(ev->keyname, "KP_Left")) && (!ev->string)))
366      {
367         if (!sd->horizontal) return EINA_FALSE;
368         if (!sd->inverted) _drag_down(obj, NULL, NULL, NULL);
369         else _drag_up(obj, NULL, NULL, NULL);
370         ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
371         goto success;
372      }
373    else if ((!strcmp(ev->keyname, "Right")) ||
374             ((!strcmp(ev->keyname, "KP_Right")) && (!ev->string)))
375      {
376         if (!sd->horizontal) return EINA_FALSE;
377         if (!sd->inverted) _drag_up(obj, NULL, NULL, NULL);
378         else _drag_down(obj, NULL, NULL, NULL);
379         ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
380         goto success;
381      }
382    else if ((!strcmp(ev->keyname, "Up")) ||
383             ((!strcmp(ev->keyname, "KP_Up")) && (!ev->string)))
384      {
385         if (sd->horizontal) return EINA_FALSE;
386         if (sd->inverted) _drag_up(obj, NULL, NULL, NULL);
387         else _drag_down(obj, NULL, NULL, NULL);
388         ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
389         goto success;
390      }
391    else if ((!strcmp(ev->keyname, "Down")) ||
392             ((!strcmp(ev->keyname, "KP_Down")) && (!ev->string)))
393      {
394         if (sd->horizontal) return EINA_FALSE;
395         if (sd->inverted) _drag_down(obj, NULL, NULL, NULL);
396         else _drag_up(obj, NULL, NULL, NULL);
397         ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
398         goto success;
399      }
400    else return EINA_FALSE;
401
402 success:
403    _slider_update(obj);
404
405    return EINA_TRUE;
406 }
407
408 static Eina_Bool
409 _elm_slider_smart_activate(Evas_Object *obj, Elm_Activate act)
410 {
411    ELM_SLIDER_DATA_GET(obj, sd);
412
413    if ((elm_widget_disabled_get(obj)) ||
414        (act == ELM_ACTIVATE_DEFAULT)) return EINA_FALSE;
415
416    if ((act == ELM_ACTIVATE_UP) ||
417        (act == ELM_ACTIVATE_RIGHT))
418      {
419         if (!sd->inverted) _drag_up(obj, NULL, NULL, NULL);
420         else _drag_down(obj, NULL, NULL, NULL);
421      }
422    else if ((act == ELM_ACTIVATE_DOWN) ||
423             (act == ELM_ACTIVATE_LEFT))
424      {
425         if (!sd->inverted) _drag_down(obj, NULL, NULL, NULL);
426         else _drag_up(obj, NULL, NULL, NULL);
427      }
428
429    _slider_update(obj);
430
431    return EINA_TRUE;
432 }
433
434 static void
435 _visuals_refresh(Evas_Object *obj)
436 {
437    _val_set(obj);
438    evas_object_smart_changed(obj);
439 }
440
441 static Eina_Bool
442 _elm_slider_smart_theme(Evas_Object *obj)
443 {
444    ELM_SLIDER_DATA_GET(obj, sd);
445
446    if (sd->horizontal)
447      {
448         eina_stringshare_replace(&ELM_LAYOUT_DATA(sd)->group, "horizontal");
449         if (sd->popup)
450           _elm_theme_set(NULL, sd->popup,
451                          "slider", "horizontal/popup",
452                          elm_widget_style_get(obj));
453      }
454    else
455      {
456         eina_stringshare_replace(&ELM_LAYOUT_DATA(sd)->group, "vertical");
457         if (sd->popup)
458           _elm_theme_set(NULL, sd->popup,
459                          "slider", "vertical/popup",
460                          elm_widget_style_get(obj));
461      }
462
463    if (!ELM_WIDGET_CLASS(_elm_slider_parent_sc)->theme(obj)) return EINA_FALSE;
464
465    if (sd->popup)
466      edje_object_scale_set(sd->popup, elm_widget_scale_get(obj) *
467                            elm_config_scale_get());
468
469    if (sd->units)
470      elm_layout_signal_emit(obj, "elm,state,units,visible", "elm");
471
472    if (sd->horizontal)
473      evas_object_size_hint_min_set
474        (sd->spacer, (double)sd->size * elm_widget_scale_get(obj) *
475        elm_config_scale_get(), 1);
476    else
477      evas_object_size_hint_min_set
478        (sd->spacer, 1, (double)sd->size * elm_widget_scale_get(obj) *
479        elm_config_scale_get());
480
481    if (sd->inverted)
482      {
483         elm_layout_signal_emit(obj, "elm,state,inverted,on", "elm");
484         if (sd->popup)
485           edje_object_signal_emit(sd->popup, "elm,state,inverted,on", "elm");
486      }
487
488    _visuals_refresh(obj);
489
490    edje_object_message_signal_process(ELM_WIDGET_DATA(sd)->resize_obj);
491    if (sd->popup)
492      edje_object_message_signal_process(sd->popup);
493
494    evas_object_smart_changed(obj);
495
496    return EINA_TRUE;
497 }
498
499 static void
500 _elm_slider_smart_sizing_eval(Evas_Object *obj)
501 {
502    ELM_SLIDER_DATA_GET(obj, sd);
503
504    Evas_Coord minw = -1, minh = -1, maxw = -1, maxh = -1;
505
506    elm_coords_finger_size_adjust(1, &minw, 1, &minh);
507    edje_object_size_min_restricted_calc
508      (ELM_WIDGET_DATA(sd)->resize_obj, &minw, &minh, minw, minh);
509    elm_coords_finger_size_adjust(1, &minw, 1, &minh);
510    evas_object_size_hint_min_set(obj, minw, minh);
511    evas_object_size_hint_max_set(obj, maxw, maxh);
512 }
513
514 static void
515 _spacer_down_cb(void *data,
516                 Evas *e __UNUSED__,
517                 Evas_Object *obj __UNUSED__,
518                 void *event_info)
519 {
520    ELM_SLIDER_DATA_GET(data, sd);
521
522    Evas_Event_Mouse_Down *ev = event_info;
523    Evas_Coord x, y, w, h;
524    double button_x = 0.0, button_y = 0.0;
525
526    sd->spacer_down = EINA_TRUE;
527    sd->val2 = sd->val;
528    evas_object_geometry_get(sd->spacer, &x, &y, &w, &h);
529    sd->downx = ev->canvas.x - x;
530    sd->downy = ev->canvas.y - y;
531    if (sd->horizontal)
532      {
533         button_x = ((double)ev->canvas.x - (double)x) / (double)w;
534         if (button_x > 1) button_x = 1;
535         if (button_x < 0) button_x = 0;
536      }
537    else
538      {
539         button_y = ((double)ev->canvas.y - (double)y) / (double)h;
540         if (button_y > 1) button_y = 1;
541         if (button_y < 0) button_y = 0;
542      }
543
544    edje_object_part_drag_value_set
545      (ELM_WIDGET_DATA(sd)->resize_obj, "elm.dragable.slider",
546      button_x, button_y);
547    _slider_update(data);
548    evas_object_smart_callback_call(data, SIG_DRAG_START, NULL);
549    elm_layout_signal_emit(data, "elm,state,indicator,show", "elm");
550 }
551
552 static void
553 _spacer_move_cb(void *data,
554                 Evas *e __UNUSED__,
555                 Evas_Object *obj __UNUSED__,
556                 void *event_info)
557 {
558    ELM_SLIDER_DATA_GET(data, sd);
559
560    Evas_Coord x, y, w, h;
561    double button_x = 0.0, button_y = 0.0;
562    Evas_Event_Mouse_Move *ev = event_info;
563
564    if (sd->spacer_down)
565      {
566         Evas_Coord d = 0;
567
568         evas_object_geometry_get(sd->spacer, &x, &y, &w, &h);
569         if (sd->horizontal) d = abs(ev->cur.canvas.x - x - sd->downx);
570         else d = abs(ev->cur.canvas.y - y - sd->downy);
571         if (d > (_elm_config->thumbscroll_threshold - 1))
572           {
573              if (!sd->frozen)
574                {
575                   elm_widget_scroll_freeze_push(data);
576                   sd->frozen = EINA_TRUE;
577                }
578              ev->event_flags &= ~EVAS_EVENT_FLAG_ON_HOLD;
579           }
580
581         if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD)
582           {
583              if (sd->spacer_down) sd->spacer_down = EINA_FALSE;
584              _slider_update(data);
585              evas_object_smart_callback_call(data, SIG_DRAG_STOP, NULL);
586              if (sd->frozen)
587                {
588                   elm_widget_scroll_freeze_pop(data);
589                   sd->frozen = EINA_FALSE;
590                }
591              elm_layout_signal_emit(data, "elm,state,indicator,hide", "elm");
592              elm_slider_value_set(data, sd->val2);
593              return;
594           }
595         if (sd->horizontal)
596           {
597              button_x = ((double)ev->cur.canvas.x - (double)x) / (double)w;
598              if (button_x > 1) button_x = 1;
599              if (button_x < 0) button_x = 0;
600           }
601         else
602           {
603              button_y = ((double)ev->cur.canvas.y - (double)y) / (double)h;
604              if (button_y > 1) button_y = 1;
605              if (button_y < 0) button_y = 0;
606           }
607
608         edje_object_part_drag_value_set
609           (ELM_WIDGET_DATA(sd)->resize_obj, "elm.dragable.slider",
610           button_x, button_y);
611
612         _slider_update(data);
613      }
614 }
615
616 static void
617 _spacer_up_cb(void *data,
618               Evas *e __UNUSED__,
619               Evas_Object *obj __UNUSED__,
620               void *event_info __UNUSED__)
621 {
622    ELM_SLIDER_DATA_GET(data, sd);
623
624    if (!sd->spacer_down) return;
625    if (sd->spacer_down) sd->spacer_down = EINA_FALSE;
626
627    _slider_update(data);
628    evas_object_smart_callback_call(data, SIG_DRAG_STOP, NULL);
629
630    if (sd->frozen)
631      {
632         elm_widget_scroll_freeze_pop(data);
633         sd->frozen = EINA_FALSE;
634      }
635    elm_layout_signal_emit(data, "elm,state,indicator,hide", "elm");
636 }
637
638 static void
639 _track_move_cb(void *data,
640                Evas *e __UNUSED__,
641                Evas_Object *obj,
642                void *event_info __UNUSED__)
643 {
644    Evas_Coord x, y;
645
646    ELM_SLIDER_DATA_GET(data, sd);
647    evas_object_geometry_get(obj, &x, &y, NULL, NULL);
648    evas_object_move(sd->popup, x, y);
649 }
650
651 static void
652 _track_resize_cb(void *data,
653                  Evas *e __UNUSED__,
654                  Evas_Object *obj,
655                  void *event_info __UNUSED__)
656 {
657    Evas_Coord w, h;
658
659    ELM_SLIDER_DATA_GET(data, sd);
660    evas_object_geometry_get(obj, NULL, NULL, &w, &h);
661    evas_object_resize(sd->popup, w, h);
662 }
663
664 static void
665 _min_max_set(Evas_Object *obj)
666 {
667    char *buf_min = NULL;
668    char *buf_max = NULL;
669
670    ELM_SLIDER_DATA_GET(obj, sd);
671
672    if (sd->units_format_func)
673      {
674         buf_min = sd->units_format_func(sd->val_min);
675         buf_max = sd->units_format_func(sd->val_max);
676      }
677    else if (sd->units)
678      {
679         int length = strlen(sd->units);
680
681         buf_min = alloca(length + 128);
682         buf_max = alloca(length + 128);
683
684         snprintf((char *)buf_min, length + 128, sd->units, sd->val_min);
685         snprintf((char *)buf_max, length + 128, sd->units, sd->val_max);
686      }
687
688    elm_layout_text_set(obj, "elm.units.min", buf_min);
689    elm_layout_text_set(obj, "elm.units.max", buf_max);
690
691    if (sd->units_format_func && sd->units_format_free)
692      {
693         sd->units_format_free(buf_min);
694         sd->units_format_free(buf_max);
695      }
696 }
697
698 static void
699 _elm_slider_smart_calculate(Evas_Object *obj)
700 {
701    ELM_SLIDER_DATA_GET(obj, sd);
702
703    elm_layout_freeze(obj);
704
705    if (sd->horizontal)
706      evas_object_size_hint_min_set
707        (sd->spacer, (double)sd->size * elm_widget_scale_get(obj) *
708        elm_config_scale_get(), 1);
709    else
710      evas_object_size_hint_min_set
711        (sd->spacer, 1, (double)sd->size * elm_widget_scale_get(obj) *
712        elm_config_scale_get());
713
714    _val_fetch(obj);
715    _units_set(obj);
716    _min_max_set(obj);
717    _indicator_set(obj);
718
719    elm_layout_thaw(obj);
720 }
721
722 static char *
723 _access_info_cb(void *data __UNUSED__, Evas_Object *obj)
724 {
725    const char *txt = elm_widget_access_info_get(obj);
726
727    if (!txt) txt = elm_layout_text_get(obj, NULL);
728    if (txt) return strdup(txt);
729
730    return NULL;
731 }
732
733 static char *
734 _access_state_cb(void *data __UNUSED__, Evas_Object *obj)
735 {
736    char *ret;
737    Eina_Strbuf *buf = eina_strbuf_new();
738    const char *txt = elm_layout_text_get(obj, "elm.units");
739
740    if (txt) eina_strbuf_append(buf, txt);
741
742    if (elm_widget_disabled_get(obj))
743      eina_strbuf_append(buf, " state: disabled");
744
745    if (eina_strbuf_length_get(buf))
746      {
747         ret = eina_strbuf_string_steal(buf);
748         eina_strbuf_free(buf);
749         return ret;
750      }
751
752    eina_strbuf_free(buf);
753    return NULL;
754 }
755
756 static void
757 _elm_slider_smart_add(Evas_Object *obj)
758 {
759    EVAS_SMART_DATA_ALLOC(obj, Elm_Slider_Smart_Data);
760
761    ELM_WIDGET_CLASS(_elm_slider_parent_sc)->base.add(obj);
762
763    priv->horizontal = EINA_TRUE;
764    priv->indicator_show = EINA_TRUE;
765    priv->val = 0.0;
766    priv->val_min = 0.0;
767    priv->val_max = 1.0;
768
769    elm_layout_theme_set
770      (obj, "slider", "horizontal", elm_widget_style_get(obj));
771
772    elm_layout_signal_callback_add(obj, "drag", "*", _drag, obj);
773    elm_layout_signal_callback_add(obj, "drag,start", "*", _drag_start, obj);
774    elm_layout_signal_callback_add(obj, "drag,stop", "*", _drag_stop, obj);
775    elm_layout_signal_callback_add(obj, "drag,step", "*", _drag_step, obj);
776    elm_layout_signal_callback_add(obj, "drag,page", "*", _drag_stop, obj);
777    elm_layout_signal_callback_add(obj, "popup,show", "elm", _popup_show, obj);
778    elm_layout_signal_callback_add(obj, "popup,hide", "elm", _popup_hide, obj);
779    elm_layout_signal_callback_add(obj, "*", "popup,emit", _popup_emit, obj);
780    edje_object_part_drag_value_set
781      (ELM_WIDGET_DATA(priv)->resize_obj, "elm.dragable.slider", 0.0, 0.0);
782
783    priv->spacer = evas_object_rectangle_add(evas_object_evas_get(obj));
784    evas_object_color_set(priv->spacer, 0, 0, 0, 0);
785    evas_object_pass_events_set(priv->spacer, EINA_TRUE);
786    elm_layout_content_set(obj, "elm.swallow.bar", priv->spacer);
787
788    /* if theme has an overlayed slider mode, then lets support it */
789    if (edje_object_part_exists(elm_layout_edje_get(obj),
790                                "elm.track.slider"))
791      {
792         // XXX popup needs to adapt to theme etc.
793         priv->popup = edje_object_add(evas_object_evas_get(obj));
794         _elm_theme_set(NULL, priv->popup,
795                        "slider", "horizontal/popup",
796                        elm_widget_style_get(obj));
797         edje_object_scale_set(priv->popup, elm_widget_scale_get(obj) *
798                               elm_config_scale_get());
799         edje_object_signal_callback_add(priv->popup, "popup,hide,done", "elm",
800                                         _popup_hide_done, obj);
801
802         /* create a rectangle to track position+size of the dragable */
803         priv->track = evas_object_rectangle_add(evas_object_evas_get(obj));
804         evas_object_event_callback_add
805           (priv->track, EVAS_CALLBACK_MOVE, _track_move_cb, obj);
806         evas_object_event_callback_add
807           (priv->track, EVAS_CALLBACK_RESIZE, _track_resize_cb, obj);
808
809         evas_object_color_set(priv->track, 0, 0, 0, 0);
810         evas_object_pass_events_set(priv->track, EINA_TRUE);
811         elm_layout_content_set(obj, "elm.track.slider", priv->track);
812      }
813
814    evas_object_event_callback_add
815      (priv->spacer, EVAS_CALLBACK_MOUSE_DOWN, _spacer_down_cb, obj);
816    evas_object_event_callback_add
817      (priv->spacer, EVAS_CALLBACK_MOUSE_MOVE, _spacer_move_cb, obj);
818    evas_object_event_callback_add
819      (priv->spacer, EVAS_CALLBACK_MOUSE_UP, _spacer_up_cb, obj);
820
821    elm_widget_can_focus_set(obj, EINA_TRUE);
822
823    _elm_access_object_register(obj, ELM_WIDGET_DATA(priv)->resize_obj);
824    _elm_access_text_set
825      (_elm_access_object_get(obj), ELM_ACCESS_TYPE, E_("slider"));
826    _elm_access_callback_set
827      (_elm_access_object_get(obj), ELM_ACCESS_INFO, _access_info_cb, NULL);
828    _elm_access_callback_set
829      (_elm_access_object_get(obj), ELM_ACCESS_STATE, _access_state_cb, priv);
830
831    evas_object_smart_changed(obj);
832 }
833
834 static void
835 _elm_slider_smart_del(Evas_Object *obj)
836 {
837    ELM_SLIDER_DATA_GET(obj, sd);
838
839    // Because "drag,stop" edje signal is queued,
840    // It can be discarded after object is deletd.
841    if (0 != elm_widget_scroll_freeze_get(obj))
842      elm_widget_scroll_freeze_pop(obj);
843
844    if (sd->indicator) eina_stringshare_del(sd->indicator);
845    if (sd->units) eina_stringshare_del(sd->units);
846    if (sd->delay) ecore_timer_del(sd->delay);
847
848    if (sd->popup) evas_object_del(sd->popup);
849
850    ELM_WIDGET_CLASS(_elm_slider_parent_sc)->base.del(obj);
851 }
852
853 static void
854 _elm_slider_smart_set_user(Elm_Slider_Smart_Class *sc)
855 {
856    ELM_WIDGET_CLASS(sc)->base.add = _elm_slider_smart_add;
857    ELM_WIDGET_CLASS(sc)->base.del = _elm_slider_smart_del;
858    ELM_WIDGET_CLASS(sc)->base.calculate = _elm_slider_smart_calculate;
859
860    ELM_WIDGET_CLASS(sc)->theme = _elm_slider_smart_theme;
861    ELM_WIDGET_CLASS(sc)->event = _elm_slider_smart_event;
862    ELM_WIDGET_CLASS(sc)->activate = _elm_slider_smart_activate;
863
864    /* not a 'focus chain manager' */
865    ELM_WIDGET_CLASS(sc)->focus_next = NULL;
866    ELM_WIDGET_CLASS(sc)->focus_direction = NULL;
867
868    ELM_LAYOUT_CLASS(sc)->sizing_eval = _elm_slider_smart_sizing_eval;
869
870    ELM_LAYOUT_CLASS(sc)->content_aliases = _content_aliases;
871    ELM_LAYOUT_CLASS(sc)->text_aliases = _text_aliases;
872 }
873
874 EAPI const Elm_Slider_Smart_Class *
875 elm_slider_smart_class_get(void)
876 {
877    static Elm_Slider_Smart_Class _sc =
878      ELM_SLIDER_SMART_CLASS_INIT_NAME_VERSION(ELM_SLIDER_SMART_NAME);
879    static const Elm_Slider_Smart_Class *class = NULL;
880    Evas_Smart_Class *esc = (Evas_Smart_Class *)&_sc;
881
882    if (class)
883      return class;
884
885    _elm_slider_smart_set(&_sc);
886    esc->callbacks = _smart_callbacks;
887    class = &_sc;
888
889    return class;
890 }
891
892 EAPI Evas_Object *
893 elm_slider_add(Evas_Object *parent)
894 {
895    Evas_Object *obj;
896
897    EINA_SAFETY_ON_NULL_RETURN_VAL(parent, NULL);
898
899    obj = elm_widget_add(_elm_slider_smart_class_new(), parent);
900    if (!obj) return NULL;
901
902    if (!elm_widget_sub_object_add(parent, obj))
903      ERR("could not add %p as sub object of %p", obj, parent);
904
905    return obj;
906 }
907
908 EAPI void
909 elm_slider_span_size_set(Evas_Object *obj,
910                          Evas_Coord size)
911 {
912    ELM_SLIDER_CHECK(obj);
913    ELM_SLIDER_DATA_GET(obj, sd);
914
915    if (sd->size == size) return;
916    sd->size = size;
917
918    if (sd->indicator_show)
919      {
920         elm_layout_signal_emit(obj, "elm,state,val,show", "elm");
921         if (sd->popup)
922           edje_object_signal_emit(sd->popup, "elm,state,val,show", "elm");
923      }
924    else
925      {
926         elm_layout_signal_emit(obj, "elm,state,val,hide", "elm");
927         if (sd->popup)
928           edje_object_signal_emit(sd->popup, "elm,state,val,hide", "elm");
929      }
930
931    evas_object_smart_changed(obj);
932 }
933
934 EAPI Evas_Coord
935 elm_slider_span_size_get(const Evas_Object *obj)
936 {
937    ELM_SLIDER_CHECK(obj) 0;
938    ELM_SLIDER_DATA_GET(obj, sd);
939
940    return sd->size;
941 }
942
943 EAPI void
944 elm_slider_unit_format_set(Evas_Object *obj,
945                            const char *units)
946 {
947    ELM_SLIDER_CHECK(obj);
948    ELM_SLIDER_DATA_GET(obj, sd);
949
950    eina_stringshare_replace(&sd->units, units);
951    if (units)
952      {
953         elm_layout_signal_emit(obj, "elm,state,units,visible", "elm");
954         edje_object_message_signal_process(ELM_WIDGET_DATA(sd)->resize_obj);
955         if (sd->popup)
956           edje_object_signal_emit(sd->popup, "elm,state,units,visible", "elm");
957      }
958    else
959      {
960         elm_layout_signal_emit(obj, "elm,state,units,hidden", "elm");
961         edje_object_message_signal_process(ELM_WIDGET_DATA(sd)->resize_obj);
962         if (sd->popup)
963           edje_object_signal_emit(sd->popup, "elm,state,units,hidden", "elm");
964      }
965
966    evas_object_smart_changed(obj);
967 }
968
969 EAPI const char *
970 elm_slider_unit_format_get(const Evas_Object *obj)
971 {
972    ELM_SLIDER_CHECK(obj) NULL;
973    ELM_SLIDER_DATA_GET(obj, sd);
974
975    return sd->units;
976 }
977
978 EAPI void
979 elm_slider_indicator_format_set(Evas_Object *obj,
980                                 const char *indicator)
981 {
982    ELM_SLIDER_CHECK(obj);
983    ELM_SLIDER_DATA_GET(obj, sd);
984
985    eina_stringshare_replace(&sd->indicator, indicator);
986    evas_object_smart_changed(obj);
987 }
988
989 EAPI const char *
990 elm_slider_indicator_format_get(const Evas_Object *obj)
991 {
992    ELM_SLIDER_CHECK(obj) NULL;
993    ELM_SLIDER_DATA_GET(obj, sd);
994
995    return sd->indicator;
996 }
997
998 EAPI void
999 elm_slider_horizontal_set(Evas_Object *obj,
1000                           Eina_Bool horizontal)
1001 {
1002    ELM_SLIDER_CHECK(obj);
1003    ELM_SLIDER_DATA_GET(obj, sd);
1004
1005    horizontal = !!horizontal;
1006    if (sd->horizontal == horizontal) return;
1007    sd->horizontal = horizontal;
1008
1009    ELM_WIDGET_DATA(sd)->api->theme(obj);
1010 }
1011
1012 EAPI Eina_Bool
1013 elm_slider_horizontal_get(const Evas_Object *obj)
1014 {
1015    ELM_SLIDER_CHECK(obj) EINA_FALSE;
1016    ELM_SLIDER_DATA_GET(obj, sd);
1017
1018    return sd->horizontal;
1019 }
1020
1021 EAPI void
1022 elm_slider_min_max_set(Evas_Object *obj,
1023                        double min,
1024                        double max)
1025 {
1026    ELM_SLIDER_CHECK(obj);
1027    ELM_SLIDER_DATA_GET(obj, sd);
1028
1029    if ((sd->val_min == min) && (sd->val_max == max)) return;
1030    sd->val_min = min;
1031    sd->val_max = max;
1032    if (sd->val < sd->val_min) sd->val = sd->val_min;
1033    if (sd->val > sd->val_max) sd->val = sd->val_max;
1034
1035    _visuals_refresh(obj);
1036 }
1037
1038 EAPI void
1039 elm_slider_min_max_get(const Evas_Object *obj,
1040                        double *min,
1041                        double *max)
1042 {
1043    if (min) *min = 0.0;
1044    if (max) *max = 0.0;
1045
1046    ELM_SLIDER_CHECK(obj);
1047    ELM_SLIDER_DATA_GET(obj, sd);
1048
1049    if (min) *min = sd->val_min;
1050    if (max) *max = sd->val_max;
1051 }
1052
1053 EAPI void
1054 elm_slider_value_set(Evas_Object *obj,
1055                      double val)
1056 {
1057    ELM_SLIDER_CHECK(obj);
1058    ELM_SLIDER_DATA_GET(obj, sd);
1059
1060    if (sd->val == val) return;
1061    sd->val = val;
1062
1063    if (sd->val < sd->val_min) sd->val = sd->val_min;
1064    if (sd->val > sd->val_max) sd->val = sd->val_max;
1065
1066    _visuals_refresh(obj);
1067 }
1068
1069 EAPI double
1070 elm_slider_value_get(const Evas_Object *obj)
1071 {
1072    ELM_SLIDER_CHECK(obj) 0.0;
1073    ELM_SLIDER_DATA_GET(obj, sd);
1074
1075    return sd->val;
1076 }
1077
1078 EAPI void
1079 elm_slider_inverted_set(Evas_Object *obj,
1080                         Eina_Bool inverted)
1081 {
1082    ELM_SLIDER_CHECK(obj);
1083    ELM_SLIDER_DATA_GET(obj, sd);
1084
1085    inverted = !!inverted;
1086    if (sd->inverted == inverted) return;
1087    sd->inverted = inverted;
1088
1089    if (sd->inverted)
1090      {
1091         elm_layout_signal_emit(obj, "elm,state,inverted,on", "elm");
1092         if (sd->popup)
1093           edje_object_signal_emit(sd->popup, "elm,state,inverted,on", "elm");
1094      }
1095    else
1096      {
1097         elm_layout_signal_emit(obj, "elm,state,inverted,off", "elm");
1098         if (sd->popup)
1099           edje_object_signal_emit(sd->popup, "elm,state,inverted,off", "elm");
1100      }
1101
1102    edje_object_message_signal_process(ELM_WIDGET_DATA(sd)->resize_obj);
1103
1104    _visuals_refresh(obj);
1105 }
1106
1107 EAPI Eina_Bool
1108 elm_slider_inverted_get(const Evas_Object *obj)
1109 {
1110    ELM_SLIDER_CHECK(obj) EINA_FALSE;
1111    ELM_SLIDER_DATA_GET(obj, sd);
1112
1113    return sd->inverted;
1114 }
1115
1116 EAPI void
1117 elm_slider_indicator_format_function_set(Evas_Object *obj,
1118                                          char *(*func)(double),
1119                                          void (*free_func)(char *))
1120 {
1121    ELM_SLIDER_CHECK(obj);
1122    ELM_SLIDER_DATA_GET(obj, sd);
1123
1124    sd->indicator_format_func = func;
1125    sd->indicator_format_free = free_func;
1126    evas_object_smart_changed(obj);
1127 }
1128
1129 EAPI void
1130 elm_slider_units_format_function_set(Evas_Object *obj,
1131                                      char *(*func)(double),
1132                                      void (*free_func)(char *))
1133 {
1134    ELM_SLIDER_CHECK(obj);
1135    ELM_SLIDER_DATA_GET(obj, sd);
1136
1137    sd->units_format_func = func;
1138    sd->units_format_free = free_func;
1139
1140    evas_object_smart_changed(obj);
1141 }
1142
1143 EAPI void
1144 elm_slider_indicator_show_set(Evas_Object *obj,
1145                               Eina_Bool show)
1146 {
1147    ELM_SLIDER_CHECK(obj);
1148    ELM_SLIDER_DATA_GET(obj, sd);
1149
1150    if (show)
1151      {
1152         sd->indicator_show = EINA_TRUE;
1153         elm_layout_signal_emit(obj, "elm,state,val,show", "elm");
1154         if (sd->popup)
1155           edje_object_signal_emit(sd->popup, "elm,state,val,show", "elm");
1156      }
1157    else {
1158         sd->indicator_show = EINA_FALSE;
1159         elm_layout_signal_emit(obj, "elm,state,val,hide", "elm");
1160         if (sd->popup)
1161           edje_object_signal_emit(sd->popup, "elm,state,val,hide", "elm");
1162      }
1163
1164    evas_object_smart_changed(obj);
1165 }
1166
1167 EAPI Eina_Bool
1168 elm_slider_indicator_show_get(const Evas_Object *obj)
1169 {
1170    ELM_SLIDER_CHECK(obj) EINA_FALSE;
1171    ELM_SLIDER_DATA_GET(obj, sd);
1172
1173    return sd->indicator_show;
1174 }