[Elm_Gengrid] Added NULL check for gengrid item and its item content. Very
[framework/uifw/elementary.git] / src / lib / elm_actionslider.c
1 #include <Elementary.h>
2 #include <math.h>
3 #include "elm_priv.h"
4
5 typedef struct _Widget_Data Widget_Data;
6
7 struct _Widget_Data
8 {
9    Evas_Object *as;     // actionslider
10    Evas_Object *drag_button_base;
11    Elm_Actionslider_Pos magnet_position, enabled_position;
12    const char *text_left, *text_right, *text_center;
13    const char *indicator_label;
14    Ecore_Animator *button_animator;
15    double final_position;
16    Eina_Bool mouse_down : 1;
17 };
18
19 static const char *widtype = NULL;
20
21 static const char SIG_CHANGED[] = "pos_changed";
22 static const char SIG_SELECTED[] = "selected";
23
24 static const Evas_Smart_Cb_Description _signals[] =
25 {
26      {SIG_CHANGED, ""},
27      {SIG_SELECTED, ""},
28      {NULL, NULL}
29 };
30
31
32 static void
33 _del_hook(Evas_Object *obj)
34 {
35    Widget_Data *wd = elm_widget_data_get(obj);
36    if (!wd) return;
37    if (wd->drag_button_base)
38      {
39         evas_object_del(wd->drag_button_base);
40         wd->drag_button_base = NULL;
41      }
42    if (wd->text_left) eina_stringshare_del(wd->text_left);
43    if (wd->text_right) eina_stringshare_del(wd->text_right);
44    if (wd->text_center) eina_stringshare_del(wd->text_center);
45    if (wd->indicator_label) eina_stringshare_del(wd->indicator_label);
46    free(wd);
47 }
48
49 static Elm_Actionslider_Pos
50 _get_pos_by_orientation(const Evas_Object *obj, Elm_Actionslider_Pos pos)
51 {
52    if (elm_widget_mirrored_get(obj))
53      {
54         switch (pos)
55           {
56            case ELM_ACTIONSLIDER_LEFT:
57               pos = ELM_ACTIONSLIDER_RIGHT;
58               break;
59            case ELM_ACTIONSLIDER_RIGHT:
60               pos = ELM_ACTIONSLIDER_LEFT;
61               break;
62            default:
63               break;
64           }
65      }
66    return pos;
67 }
68
69 static void
70 _mirrored_set(Evas_Object *obj, Eina_Bool rtl)
71 {
72    Widget_Data *wd = elm_widget_data_get(obj);
73    double pos;
74
75    if (!wd) return;
76    if (edje_object_mirrored_get(wd->as) == rtl)
77      return;
78
79    edje_object_mirrored_set(wd->as, rtl);
80    if (!elm_widget_mirrored_get(obj))
81      {
82         edje_object_part_text_escaped_set(wd->as, "elm.text.left", wd->text_left);
83         edje_object_part_text_escaped_set(wd->as, "elm.text.right", wd->text_right);
84      }
85    else
86      {
87         edje_object_part_text_escaped_set(wd->as, "elm.text.left", wd->text_right);
88         edje_object_part_text_escaped_set(wd->as, "elm.text.right", wd->text_left);
89      }
90    edje_object_part_drag_value_get(wd->as, "elm.drag_button_base", &pos, NULL);
91    edje_object_part_drag_value_set(wd->as, "elm.drag_button_base", 1.0 - pos, 0.5);
92 }
93
94 static void
95 _sizing_eval(Evas_Object *obj)
96 {
97    Widget_Data *wd = elm_widget_data_get(obj);
98    Evas_Coord minw = -1, minh = -1;
99
100    if (!wd) return;
101    elm_coords_finger_size_adjust(1, &minw, 1, &minh);
102    evas_object_size_hint_min_set(wd->drag_button_base, minw, minh);
103    evas_object_size_hint_max_set(wd->drag_button_base, -1, -1);
104
105    minw = -1;
106    minh = -1;
107    elm_coords_finger_size_adjust(3, &minw, 1, &minh);
108    edje_object_size_min_restricted_calc(wd->as, &minw, &minh, minw, minh);
109    evas_object_size_hint_min_set(obj, minw, minh);
110    evas_object_size_hint_max_set(obj, -1, -1);
111 }
112
113 static void
114 _theme_hook(Evas_Object *obj)
115 {
116    Widget_Data *wd = elm_widget_data_get(obj);
117    if (!wd) return;
118    _elm_widget_mirrored_reload(obj);
119    if (!edje_object_part_swallow_get(wd->as, "elm.drag_button_base"))
120      edje_object_part_unswallow(wd->as, wd->drag_button_base);
121
122    _elm_theme_object_set(obj, wd->as, "actionslider",
123                          "base", elm_widget_style_get(obj));
124    _elm_theme_object_set(obj, wd->drag_button_base, "actionslider",
125                          "drag_button", elm_widget_style_get(obj));
126    edje_object_part_swallow(wd->as, "elm.drag_button_base", wd->drag_button_base);
127
128    _mirrored_set(obj, elm_widget_mirrored_get(obj));
129    edje_object_part_text_escaped_set(wd->as, "elm.text.center", wd->text_center);
130    edje_object_part_text_escaped_set(wd->as, "elm.text.indicator", wd->indicator_label);
131    edje_object_message_signal_process(wd->as);
132    _sizing_eval(obj);
133 }
134
135 static void
136 _drag_button_down_cb(void *data, Evas_Object *o __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__)
137 {
138    Widget_Data *wd = elm_widget_data_get((Evas_Object *) data);
139    if (!wd) return;
140    wd->mouse_down = EINA_TRUE;
141 }
142
143 static void
144 _drag_button_move_cb(void *data, Evas_Object *o __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__)
145 {
146    Evas_Object *obj = (Evas_Object *) data;
147    Widget_Data *wd = elm_widget_data_get(obj);
148    double pos = 0.0;
149    if (!wd) return;
150
151    if (!wd->mouse_down) return;
152    edje_object_part_drag_value_get(wd->as, "elm.drag_button_base", &pos, NULL);
153    if (pos == 0.0)
154      evas_object_smart_callback_call(obj, SIG_CHANGED,
155                                      (void *) ((!elm_widget_mirrored_get(obj)) ?
156                                                "left" : "right"));
157    else if (pos == 1.0)
158      evas_object_smart_callback_call(obj, SIG_CHANGED,
159                                      (void *) ((!elm_widget_mirrored_get(obj)) ?
160                                                "right" : "left"));
161    else if (pos >= 0.45 && pos <= 0.55)
162      evas_object_smart_callback_call(obj, SIG_CHANGED, (void *)"center");
163 }
164
165 static Eina_Bool
166 _button_animation(void *data)
167 {
168    Evas_Object *obj = data;
169    Widget_Data *wd = elm_widget_data_get(obj);
170    double cur_position = 0.0, new_position = 0.0;
171    double move_amount = 0.05;
172    Eina_Bool flag_finish_animation = EINA_FALSE;
173    if (!wd) return ECORE_CALLBACK_CANCEL;
174
175    edje_object_part_drag_value_get(wd->as,
176                                    "elm.drag_button_base", &cur_position, NULL);
177      {
178         double adjusted_final;
179         adjusted_final = (!elm_widget_mirrored_get(obj)) ?
180            wd->final_position : 1.0 - wd->final_position;
181         if ((adjusted_final == 0.0) ||
182             (adjusted_final == 0.5 && cur_position >= adjusted_final))
183           {
184              new_position = cur_position - move_amount;
185              if (new_position <= adjusted_final)
186                {
187                   new_position = adjusted_final;
188                   flag_finish_animation = EINA_TRUE;
189                }
190           }
191         else if ((adjusted_final == 1.0) ||
192                  (adjusted_final == 0.5 && cur_position < adjusted_final))
193           {
194              new_position = cur_position + move_amount;
195              if (new_position >= adjusted_final)
196                {
197                   new_position = adjusted_final;
198                   flag_finish_animation = EINA_TRUE;
199                }
200           }
201         edje_object_part_drag_value_set(wd->as,
202                                         "elm.drag_button_base", new_position, 0.5);
203      }
204
205    if (flag_finish_animation)
206      {
207         if ((!wd->final_position) &&
208             (wd->enabled_position & ELM_ACTIONSLIDER_LEFT))
209           evas_object_smart_callback_call(data, SIG_SELECTED,
210                                           (void *)wd->text_left);
211         else if ((wd->final_position == 0.5) &&
212                  (wd->enabled_position & ELM_ACTIONSLIDER_CENTER))
213           evas_object_smart_callback_call(data, SIG_SELECTED,
214                                           (void *)wd->text_center);
215         else if ((wd->final_position == 1) &&
216                  (wd->enabled_position & ELM_ACTIONSLIDER_RIGHT))
217           evas_object_smart_callback_call(data, SIG_SELECTED,
218                                           (void *)wd->text_right);
219         wd->button_animator = NULL;
220         return ECORE_CALLBACK_CANCEL;
221      }
222    return ECORE_CALLBACK_RENEW;
223 }
224
225 static void
226 _drag_button_up_cb(void *data, Evas_Object *o __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__)
227 {
228    Evas_Object *obj = (Evas_Object *) data;
229    Widget_Data *wd = elm_widget_data_get(obj);
230    double position = 0.0;
231    if (!wd) return;
232
233    wd->mouse_down = EINA_FALSE;
234
235    edje_object_part_drag_value_get(wd->as, "elm.drag_button_base",
236                                    &position, NULL);
237
238    if ((wd->enabled_position & ELM_ACTIONSLIDER_LEFT) &&
239        ((!elm_widget_mirrored_get(obj) && position == 0.0) ||
240         (elm_widget_mirrored_get(obj) && position == 1.0)))
241      {
242         wd->final_position = 0;
243         evas_object_smart_callback_call(data, SIG_SELECTED,
244                                         (void *) wd->text_left);
245         return;
246      }
247    if (position >= 0.45 && position <= 0.55 &&
248        (wd->enabled_position & ELM_ACTIONSLIDER_CENTER))
249      {
250         wd->final_position = 0.5;
251         evas_object_smart_callback_call(data, SIG_SELECTED,
252                                         (void *)wd->text_center);
253         if (wd->button_animator) ecore_animator_del(wd->button_animator);
254         wd->button_animator = ecore_animator_add(_button_animation, data);
255         return;
256      }
257    if ((wd->enabled_position & ELM_ACTIONSLIDER_RIGHT) &&
258        ((!elm_widget_mirrored_get(obj) && position == 1.0) ||
259         (elm_widget_mirrored_get(obj) && position == 0.0)))
260      {
261         wd->final_position = 1;
262         evas_object_smart_callback_call(data, SIG_SELECTED,
263                                         (void *) wd->text_right);
264         return;
265      }
266
267    if (wd->magnet_position == ELM_ACTIONSLIDER_NONE) return;
268
269 #define _FINAL_POS_BY_ORIENTATION(x) (x)
270 #define _POS_BY_ORIENTATION(x) \
271    ((!elm_widget_mirrored_get(obj)) ? \
272     x : 1.0 - x)
273
274    position = _POS_BY_ORIENTATION(position);
275
276    if (position < 0.3)
277      {
278         if (wd->magnet_position & ELM_ACTIONSLIDER_LEFT)
279           wd->final_position = _FINAL_POS_BY_ORIENTATION(0);
280         else if (wd->magnet_position & ELM_ACTIONSLIDER_CENTER)
281           wd->final_position = 0.5;
282         else if (wd->magnet_position & ELM_ACTIONSLIDER_RIGHT)
283           wd->final_position = _FINAL_POS_BY_ORIENTATION(1);
284      }
285    else if ((position >= 0.3) && (position <= 0.7))
286      {
287         if (wd->magnet_position & ELM_ACTIONSLIDER_CENTER)
288           wd->final_position = 0.5;
289         else if (position < 0.5)
290           {
291              if (wd->magnet_position & ELM_ACTIONSLIDER_LEFT)
292                wd->final_position = _FINAL_POS_BY_ORIENTATION(0);
293              else
294                wd->final_position = _FINAL_POS_BY_ORIENTATION(1);
295           }
296         else
297           {
298              if (wd->magnet_position & ELM_ACTIONSLIDER_RIGHT)
299                wd->final_position = _FINAL_POS_BY_ORIENTATION(1);
300              else
301                wd->final_position = _FINAL_POS_BY_ORIENTATION(0);
302           }
303      }
304    else
305      {
306         if (wd->magnet_position & ELM_ACTIONSLIDER_RIGHT)
307           wd->final_position = _FINAL_POS_BY_ORIENTATION(1);
308         else if (wd->magnet_position & ELM_ACTIONSLIDER_CENTER)
309           wd->final_position = 0.5;
310         else
311           wd->final_position = _FINAL_POS_BY_ORIENTATION(0);
312      }
313    if (wd->button_animator) ecore_animator_del(wd->button_animator);
314    wd->button_animator = ecore_animator_add(_button_animation, data);
315
316 #undef _FINAL_POS_BY_ORIENTATION
317 }
318
319 static void
320 _elm_actionslider_label_set(Evas_Object *obj, const char *item, const char *label)
321 {
322    ELM_CHECK_WIDTYPE(obj, widtype);
323    Widget_Data *wd = elm_widget_data_get(obj);
324    if (!wd) return;
325
326    if (!item || !strcmp(item, "indicator"))
327      {
328         eina_stringshare_replace(&wd->indicator_label, label);
329         edje_object_part_text_escaped_set(wd->as, "elm.text.indicator",
330                                           wd->indicator_label);
331      }
332    else if (!strcmp(item, "left"))
333      {
334         eina_stringshare_replace(&wd->text_left, label);
335         if (!elm_widget_mirrored_get(obj))
336           edje_object_part_text_escaped_set(wd->as, "elm.text.left", wd->text_left);
337         else
338           edje_object_part_text_escaped_set(wd->as, "elm.text.right", wd->text_left);
339      }
340    else if (!strcmp(item, "center"))
341      {
342         eina_stringshare_replace(&wd->text_center, label);
343         edje_object_part_text_escaped_set(wd->as, "elm.text.center", wd->text_center);
344      }
345    else if (!strcmp(item, "right"))
346      {
347         eina_stringshare_replace(&wd->text_right, label);
348         if (!elm_widget_mirrored_get(obj))
349           edje_object_part_text_escaped_set(wd->as, "elm.text.right", wd->text_right);
350         else
351           edje_object_part_text_escaped_set(wd->as, "elm.text.left", wd->text_right);
352      }
353 }
354
355 static const char *
356 _elm_actionslider_label_get(const Evas_Object *obj, const char *item)
357 {
358    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
359    Widget_Data *wd = elm_widget_data_get(obj);
360    if (!wd) return NULL;
361
362    if (!item || !strcmp(item, "indicator"))
363      return wd->indicator_label;
364    else if (!strcmp(item, "left"))
365      return wd->text_left;
366    else if (!strcmp(item, "center"))
367      return wd->text_center;
368    else if (!strcmp(item, "right"))
369      return wd->text_right;
370
371    return NULL;
372 }
373
374 EAPI Evas_Object *
375 elm_actionslider_add(Evas_Object *parent)
376 {
377    Evas_Object *obj;
378    Widget_Data *wd;
379    Evas *e;
380
381    ELM_WIDGET_STANDARD_SETUP(wd, Widget_Data, parent, e, obj, NULL);
382
383    ELM_SET_WIDTYPE(widtype, "actionslider");
384    elm_widget_type_set(obj, "actionslider");
385    elm_widget_sub_object_add(parent, obj);
386    elm_widget_data_set(obj, wd);
387    elm_widget_del_hook_set(obj, _del_hook);
388    elm_widget_theme_hook_set(obj, _theme_hook);
389    elm_widget_text_set_hook_set(obj, _elm_actionslider_label_set);
390    elm_widget_text_get_hook_set(obj, _elm_actionslider_label_get);
391
392    wd->mouse_down = EINA_FALSE;
393    wd->enabled_position = ELM_ACTIONSLIDER_ALL;
394
395    wd->as = edje_object_add(e);
396    _elm_theme_object_set(obj, wd->as, "actionslider", "base", "default");
397    elm_widget_resize_object_set(obj, wd->as);
398
399    wd->drag_button_base = evas_object_rectangle_add(e);
400    evas_object_color_set(wd->drag_button_base, 0, 0, 0, 0);
401    edje_object_part_swallow(wd->as, "elm.drag_button_base", wd->drag_button_base);
402
403    edje_object_signal_callback_add(wd->as,
404                                    "elm.drag_button,mouse,up", "",
405                                    _drag_button_up_cb, obj);
406    edje_object_signal_callback_add(wd->as,
407                                    "elm.drag_button,mouse,down", "",
408                                    _drag_button_down_cb, obj);
409    edje_object_signal_callback_add(wd->as,
410                                    "elm.drag_button,mouse,move", "",
411                                    _drag_button_move_cb, obj);
412
413    evas_object_smart_callbacks_descriptions_set(obj, _signals);
414    _mirrored_set(obj, elm_widget_mirrored_get(obj));
415    _sizing_eval(obj);
416    return obj;
417 }
418
419 EAPI void
420 elm_actionslider_indicator_pos_set(Evas_Object *obj, Elm_Actionslider_Pos pos)
421 {
422    ELM_CHECK_WIDTYPE(obj, widtype);
423    Widget_Data *wd = elm_widget_data_get(obj);
424    double position = 0.0;
425    if (!wd) return;
426    pos = _get_pos_by_orientation(obj, pos);
427    if (pos == ELM_ACTIONSLIDER_CENTER) position = 0.5;
428    else if (pos == ELM_ACTIONSLIDER_RIGHT) position = 1.0;
429    edje_object_part_drag_value_set(wd->as, "elm.drag_button_base", position, 0.5);
430 }
431
432 EAPI Elm_Actionslider_Pos
433 elm_actionslider_indicator_pos_get(const Evas_Object *obj)
434 {
435    ELM_CHECK_WIDTYPE(obj, widtype) ELM_ACTIONSLIDER_NONE;
436    Widget_Data *wd = elm_widget_data_get(obj);
437    double position;
438    if (!wd) return ELM_ACTIONSLIDER_NONE;
439
440    edje_object_part_drag_value_get(wd->as, "elm.drag_button_base", &position, NULL);
441    if (position < 0.3)
442      return _get_pos_by_orientation(obj, ELM_ACTIONSLIDER_LEFT);
443    else if (position < 0.7)
444      return ELM_ACTIONSLIDER_CENTER;
445    else
446      return _get_pos_by_orientation(obj, ELM_ACTIONSLIDER_RIGHT);
447 }
448
449 EAPI void
450 elm_actionslider_magnet_pos_set(Evas_Object *obj, Elm_Actionslider_Pos pos)
451 {
452    ELM_CHECK_WIDTYPE(obj, widtype);
453    Widget_Data *wd = elm_widget_data_get(obj);
454    if (!wd) return;
455    wd->magnet_position = pos;
456 }
457
458 EAPI Elm_Actionslider_Pos
459 elm_actionslider_magnet_pos_get(const Evas_Object *obj)
460 {
461    ELM_CHECK_WIDTYPE(obj, widtype) ELM_ACTIONSLIDER_NONE;
462    Widget_Data *wd = elm_widget_data_get(obj);
463    if (!wd) return ELM_ACTIONSLIDER_NONE;
464    return wd->magnet_position;
465 }
466
467 EAPI void
468 elm_actionslider_enabled_pos_set(Evas_Object *obj, Elm_Actionslider_Pos pos)
469 {
470    ELM_CHECK_WIDTYPE(obj, widtype);
471    Widget_Data *wd = elm_widget_data_get(obj);
472    if (!wd) return;
473    wd->enabled_position = pos;
474 }
475
476 EAPI Elm_Actionslider_Pos
477 elm_actionslider_enabled_pos_get(const Evas_Object *obj)
478 {
479    ELM_CHECK_WIDTYPE(obj, widtype) ELM_ACTIONSLIDER_NONE;
480    Widget_Data *wd = elm_widget_data_get(obj);
481    if (!wd) return ELM_ACTIONSLIDER_NONE;
482    return wd->enabled_position;
483 }
484
485 EAPI const char *
486 elm_actionslider_selected_label_get(const Evas_Object *obj)
487 {
488    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
489    Widget_Data *wd = elm_widget_data_get(obj);
490    if (!wd) return NULL;
491
492    if ((wd->final_position == 0.0) &&
493        (wd->enabled_position & ELM_ACTIONSLIDER_LEFT))
494      return wd->text_left;
495
496    if ((wd->final_position == 0.5) &&
497        (wd->enabled_position & ELM_ACTIONSLIDER_CENTER))
498      return wd->text_center;
499
500    if ((wd->final_position == 1.0) &&
501        (wd->enabled_position & ELM_ACTIONSLIDER_RIGHT))
502      return wd->text_right;
503
504    return NULL;
505 }