really the event_info is a pointer and we should use it as such:
[framework/uifw/elementary.git] / src / lib / elm_segment_control.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 *obj;
9    Evas_Object *base;
10    Eina_List *seg_items;
11    int item_count;
12    Elm_Segment_Item *selected_item;
13    int item_width;
14 };
15
16 struct _Elm_Segment_Item
17 {
18    ELM_WIDGET_ITEM;
19    Evas_Object *icon;
20    const char *label;
21    int seg_index;
22 };
23
24 static const char *widtype = NULL;
25 static void _sizing_eval(Evas_Object *obj);
26 static void _del_hook(Evas_Object *obj);
27 static void _theme_hook(Evas_Object *obj);
28 static void _disable_hook(Evas_Object *obj);
29 static void _item_free(Elm_Segment_Item *it);
30 static void _segment_off(Elm_Segment_Item *it);
31 static void _segment_on(Elm_Segment_Item *it);
32 static void _position_items(Widget_Data *wd);
33 static void _on_move_resize(void *data, Evas *e __UNUSED__, Evas_Object *obj
34                             __UNUSED__, void *event_info __UNUSED__);
35 static void _mouse_up(void *data, Evas *e __UNUSED__, Evas_Object *obj
36                       __UNUSED__, void *event_info __UNUSED__);
37 static void _mouse_down(void *data, Evas *e __UNUSED__, Evas_Object *obj
38                         __UNUSED__, void *event_info __UNUSED__);
39 static void _swallow_item_objects(Elm_Segment_Item *it);
40 static void _update_list(Widget_Data *wd);
41 static Elm_Segment_Item * _item_find(const Evas_Object *obj, int index);
42 static Elm_Segment_Item* _item_new(Evas_Object *obj, Evas_Object *icon,
43                                    const char *label);
44
45 static const char SIG_CHANGED[] = "changed";
46
47 static const Evas_Smart_Cb_Description _signals[] = {
48    {SIG_CHANGED, ""},
49    {NULL, NULL}
50 };
51
52 static void
53 _sizing_eval(Evas_Object *obj)
54 {
55    Widget_Data *wd;
56    Evas_Coord minw = -1, minh = -1;
57    Evas_Coord w, h;
58
59    wd = elm_widget_data_get(obj);
60    if (!wd) return;
61
62    elm_coords_finger_size_adjust(wd->item_count, &minw, 1, &minh);
63    edje_object_size_min_restricted_calc(wd->base, &minw, &minh, minw, minh);
64    elm_coords_finger_size_adjust(wd->item_count, &minw, 1, &minh);
65
66    evas_object_size_hint_min_get(obj, &w, &h);
67    if (w > minw) minw = w;
68    if (h > minh) minh = h;
69    evas_object_size_hint_min_set(obj, minw, minh);
70 }
71
72 static void
73 _del_hook(Evas_Object *obj)
74 {
75    Elm_Segment_Item *it;
76    Widget_Data *wd;
77
78    wd = elm_widget_data_get(obj);
79    if (!wd) return;
80
81    EINA_LIST_FREE(wd->seg_items, it) _item_free(it);
82
83    free(wd);
84 }
85
86 static void
87 _theme_hook(Evas_Object *obj)
88 {
89    Eina_List *l;
90    Eina_Bool rtl;
91    Elm_Segment_Item *it;
92    Widget_Data *wd;
93
94    wd = elm_widget_data_get(obj);
95    if (!wd) return;
96
97    _elm_widget_mirrored_reload(obj);
98    rtl = elm_widget_mirrored_get(obj);
99    edje_object_mirrored_set(wd->base, rtl);
100
101    _elm_theme_object_set(obj, wd->base, "segment_control", "base",
102                          elm_widget_style_get(obj));
103    edje_object_scale_set(wd->base, elm_widget_scale_get(wd->base)
104                          *_elm_config->scale);
105
106    EINA_LIST_FOREACH(wd->seg_items, l, it)
107      {
108         _elm_theme_object_set(obj, VIEW(it), "segment_control",
109                               "item", elm_widget_style_get(obj));
110         edje_object_scale_set(VIEW(it), elm_widget_scale_get(VIEW(it))
111                               *_elm_config->scale);
112         edje_object_mirrored_set(VIEW(it), rtl);
113      }
114
115    _update_list(wd);
116 }
117
118 static void
119 _disable_hook(Evas_Object *obj)
120 {
121    Widget_Data *wd;
122
123    wd = elm_widget_data_get(obj);
124    if (!wd) return;
125    _update_list(wd);
126 }
127
128 // TODO:  Elm_widget elm_widget_focus_list_next_get  supports only Elm_widget list,
129 // Not the Elm_Widget_item. Focus switching with in widget not supported until
130 // it is supported in elm_widget
131 #if 0
132 static void *
133 _elm_list_data_get(const Eina_List *list)
134 {
135    Elm_Segment_Item *it = eina_list_data_get(list);
136
137    if (it) return NULL;
138
139    edje_object_signal_emit(VIEW(it), "elm,state,segment,selected", "elm");
140    return VIEW(it);
141 }
142
143 static Eina_Bool
144 _focus_next_hook(const Evas_Object *obj, Elm_Focus_Direction dir,
145                  Evas_Object **next)
146 {
147    static int count=0;
148    Widget_Data *;
149    const Eina_List *items;
150    void *(*list_data_get) (const Eina_List *list);
151
152    wd = elm_widget_data_get(obj);
153    if ((!wd)) return EINA_FALSE;
154
155    /* Focus chain */
156    /* TODO: Change this to use other chain */
157    if ((items = elm_widget_focus_custom_chain_get(obj)))
158      list_data_get = eina_list_data_get;
159    else
160      {
161         items = wd->seg_items;
162         list_data_get = _elm_list_data_get;
163         if (!items) return EINA_FALSE;
164      }
165    return elm_widget_focus_list_next_get(obj, items, list_data_get, dir, next);
166 }
167 #endif
168
169 static void
170 _item_free(Elm_Segment_Item *it)
171 {
172    Widget_Data *wd;
173
174    if (!it) return;
175
176    wd = elm_widget_item_data_get(it);
177    if (!wd) return;
178
179    if (wd->selected_item == it) wd->selected_item = NULL;
180    if (wd->seg_items) wd->seg_items = eina_list_remove(wd->seg_items, it);
181
182    elm_widget_item_pre_notify_del(it);
183
184    if (it->icon) evas_object_del(it->icon);
185    if (it->label) eina_stringshare_del(it->label);
186
187    elm_widget_item_del(it);
188 }
189
190 static void
191 _segment_off(Elm_Segment_Item *it)
192 {
193    Widget_Data *wd;
194
195    if (!it) return;
196
197    wd = elm_widget_item_data_get(it);
198    if (!wd) return;
199
200    edje_object_signal_emit(VIEW(it), "elm,state,segment,normal", "elm");
201
202    if (wd->selected_item == it) wd->selected_item = NULL;
203 }
204
205 static void
206 _segment_on(Elm_Segment_Item *it)
207 {
208    Widget_Data *wd;
209
210    if (!it) return;
211
212    wd = elm_widget_item_data_get(it);
213    if (!wd) return;
214    if (it == wd->selected_item) return;
215
216    if (wd->selected_item) _segment_off(wd->selected_item);
217
218    edje_object_signal_emit(VIEW(it), "elm,state,segment,selected", "elm");
219
220    wd->selected_item = it;
221    evas_object_smart_callback_call(wd->obj, SIG_CHANGED, it);
222 }
223
224 static void
225 _position_items(Widget_Data *wd)
226 {
227    Eina_List *l;
228    Elm_Segment_Item *it;
229    Eina_Bool rtl;
230    int bx, by, bw, bh, pos;
231
232    wd->item_count = eina_list_count(wd->seg_items);
233    if (wd->item_count <= 0) return;
234
235    evas_object_geometry_get(wd->base, &bx, &by, &bw, &bh);
236    wd->item_width = bw / wd->item_count;
237    rtl = elm_widget_mirrored_get(wd->obj);
238
239    if (rtl)
240      pos = bx + bw - wd->item_width;
241    else
242      pos = bx;
243
244    EINA_LIST_FOREACH(wd->seg_items, l, it)
245      {
246         evas_object_move(VIEW(it), pos, by);
247         evas_object_resize(VIEW(it), wd->item_width, bh);
248         if (rtl)
249           pos -= wd->item_width;
250         else
251           pos += wd->item_width;
252      }
253    _sizing_eval(wd->obj);
254 }
255
256 static void
257 _on_move_resize(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__,
258                 void *event_info __UNUSED__)
259 {
260    Widget_Data *wd;
261
262    wd = elm_widget_data_get(data);
263    if (!wd) return;
264
265    _position_items(wd);
266
267 }
268
269 static void
270 _mouse_up(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__,
271           void *event_info)
272 {
273    Widget_Data *wd;
274    Elm_Segment_Item *it;
275    Evas_Event_Mouse_Up *ev;
276    Evas_Coord x, y, w, h;
277
278    it = data;
279    if (!it) return;
280
281    wd = elm_widget_item_data_get(it);
282    if (!wd) return;
283
284    if (elm_widget_disabled_get(wd->obj)) return;
285
286    if (it == wd->selected_item) return;
287
288    ev = event_info;
289    evas_object_geometry_get(VIEW(it), &x, &y, &w, &h);
290
291    if ((ev->canvas.x >= x) && (ev->output.x <= (x + w)) && (ev->canvas.y >= y)
292        && (ev->canvas.y <= (y + h)))
293      _segment_on(it);
294    else
295      edje_object_signal_emit(VIEW(it), "elm,state,segment,normal", "elm");
296 }
297
298 static void
299 _mouse_down(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__,
300             void *event_info __UNUSED__)
301 {
302    Widget_Data *wd;
303    Elm_Segment_Item *it;
304
305    it = data;
306    if (!it) return;
307
308    wd = elm_widget_item_data_get(it);
309    if (!wd) return;
310
311    if (elm_widget_disabled_get(wd->obj)) return;
312
313    if (it == wd->selected_item) return;
314
315    edje_object_signal_emit(VIEW(it), "elm,state,segment,pressed", "elm");
316 }
317
318 static void
319 _swallow_item_objects(Elm_Segment_Item *it)
320 {
321    if (!it) return;
322
323    if (it->icon)
324      {
325         edje_object_part_swallow(VIEW(it), "elm.swallow.icon", it->icon);
326         edje_object_signal_emit(VIEW(it), "elm,state,icon,visible", "elm");
327      }
328    else
329      edje_object_signal_emit(VIEW(it), "elm,state,icon,hidden", "elm");
330
331    if (it->label)
332      {
333         edje_object_part_text_set(VIEW(it), "elm.text", it->label);
334         edje_object_signal_emit(VIEW(it), "elm,state,text,visible", "elm");
335      }
336    else
337      edje_object_signal_emit(VIEW(it), "elm,state,text,hidden", "elm");
338    edje_object_message_signal_process(VIEW(it));
339 }
340
341 static void
342 _update_list(Widget_Data *wd)
343 {
344    Eina_List *l;
345    Elm_Segment_Item *it;
346    Eina_Bool rtl;
347    int idx = 0;
348
349    _position_items(wd);
350
351    if (wd->item_count == 1)
352      {
353         it = eina_list_nth(wd->seg_items, 0);
354         it->seg_index = 0;
355
356         //Set the segment type
357         edje_object_signal_emit(VIEW(it),
358                                 "elm,type,segment,single", "elm");
359
360         //Set the segment state
361         if (wd->selected_item == it)
362           edje_object_signal_emit(VIEW(it),
363                                   "elm,state,segment,selected", "elm");
364         else
365           edje_object_signal_emit(VIEW(it),
366                                   "elm,state,segment,normal", "elm");
367
368         if (elm_widget_disabled_get(wd->obj))
369           edje_object_signal_emit(VIEW(it), "elm,state,disabled", "elm");
370         else
371           edje_object_signal_emit(VIEW(it), "elm,state,enabled", "elm");
372
373         _swallow_item_objects(it);
374         return;
375      }
376
377    rtl = elm_widget_mirrored_get(wd->obj);
378    EINA_LIST_FOREACH(wd->seg_items, l, it)
379      {
380         it->seg_index = idx;
381
382         //Set the segment type
383         if (idx == 0)
384           {
385              if (rtl)
386                edje_object_signal_emit(VIEW(it),
387                                        "elm,type,segment,right", "elm");
388              else
389                edje_object_signal_emit(VIEW(it),
390                                        "elm,type,segment,left", "elm");
391           }
392         else if (idx == (wd->item_count - 1))
393           {
394              if (rtl)
395                edje_object_signal_emit(VIEW(it),
396                                        "elm,type,segment,left", "elm");
397              else
398                edje_object_signal_emit(VIEW(it),
399                                        "elm,type,segment,right", "elm");
400           }
401         else
402           edje_object_signal_emit(VIEW(it),
403                                   "elm,type,segment,middle", "elm");
404
405         //Set the segment state
406         if (wd->selected_item == it)
407           edje_object_signal_emit(VIEW(it),
408                                   "elm,state,segment,selected", "elm");
409         else
410           edje_object_signal_emit(VIEW(it),
411                                   "elm,state,segment,normal", "elm");
412
413         if (elm_widget_disabled_get(wd->obj))
414           edje_object_signal_emit(VIEW(it), "elm,state,disabled", "elm");
415         else
416           edje_object_signal_emit(VIEW(it), "elm,state,enabled", "elm");
417
418         _swallow_item_objects(it);
419         idx++;
420      }
421 }
422
423 static Elm_Segment_Item *
424 _item_find(const Evas_Object *obj, int idx)
425 {
426    Widget_Data *wd;
427    Elm_Segment_Item *it;
428
429    wd = elm_widget_data_get(obj);
430    if (!wd) return NULL;
431
432    it = eina_list_nth(wd->seg_items, idx);
433    return it;
434 }
435
436 static Elm_Segment_Item*
437 _item_new(Evas_Object *obj, Evas_Object *icon, const char *label)
438 {
439    Elm_Segment_Item *it;
440    Widget_Data *wd;
441
442    wd = elm_widget_data_get(obj);
443    if (!wd) return NULL;
444
445    it = elm_widget_item_new(obj, Elm_Segment_Item);
446    if (!it) return NULL;
447    elm_widget_item_data_set(it, wd);
448
449    VIEW(it) = edje_object_add(evas_object_evas_get(obj));
450    edje_object_scale_set(VIEW(it), elm_widget_scale_get(VIEW(it))
451                          *_elm_config->scale);
452    evas_object_smart_member_add(VIEW(it), obj);
453    elm_widget_sub_object_add(obj, VIEW(it));
454    evas_object_clip_set(VIEW(it), evas_object_clip_get(obj));
455
456    _elm_theme_object_set(obj, VIEW(it), "segment_control", "item",
457                          elm_object_style_get(obj));
458    edje_object_mirrored_set(VIEW(it),
459                             elm_widget_mirrored_get(WIDGET(it)));
460
461    if (label)
462      eina_stringshare_replace(&it->label, label);
463    if (it->label)
464      edje_object_signal_emit(VIEW(it), "elm,state,text,visible", "elm");
465    else
466      edje_object_signal_emit(VIEW(it), "elm,state,text,hidden", "elm");
467    edje_object_message_signal_process(VIEW(it));
468    edje_object_part_text_set(VIEW(it), "elm.text", label);
469
470    it->icon = icon;
471    if (it->icon) elm_widget_sub_object_add(VIEW(it), it->icon);
472    evas_object_event_callback_add(VIEW(it), EVAS_CALLBACK_MOUSE_DOWN,
473                                   _mouse_down, it);
474    evas_object_event_callback_add(VIEW(it), EVAS_CALLBACK_MOUSE_UP,
475                                   _mouse_up, it);
476    evas_object_show(VIEW(it));
477
478    return it;
479 }
480
481 EAPI Evas_Object *
482 elm_segment_control_add(Evas_Object *parent)
483 {
484    Evas_Object *obj;
485    Evas *e;
486    Widget_Data *wd;
487
488    ELM_WIDGET_STANDARD_SETUP(wd, Widget_Data, parent, e, obj, NULL);
489
490    ELM_SET_WIDTYPE(widtype, "segment_control");
491    elm_widget_type_set(obj, "segment_control");
492    elm_widget_sub_object_add(parent, obj);
493    elm_widget_data_set(obj, wd);
494    elm_widget_del_hook_set(obj, _del_hook);
495    elm_widget_theme_hook_set(obj, _theme_hook);
496    elm_widget_disable_hook_set(obj, _disable_hook);
497
498    // TODO: Focus switch support to Elm_widget_Item not supported yet.
499 #if 0
500    elm_widget_focus_next_hook_set(obj, _focus_next_hook);
501 #endif
502
503    wd->obj = obj;
504
505    wd->base = edje_object_add(e);
506    edje_object_scale_set(wd->base, elm_widget_scale_get(wd->base)
507                          *_elm_config->scale);
508    _elm_theme_object_set(obj, wd->base, "segment_control", "base", "default");
509    elm_widget_resize_object_set(obj, wd->base);
510
511    evas_object_event_callback_add(obj, EVAS_CALLBACK_RESIZE,
512                                   _on_move_resize, obj);
513    evas_object_event_callback_add(obj, EVAS_CALLBACK_MOVE,
514                                   _on_move_resize, obj);
515
516    evas_object_smart_callbacks_descriptions_set(obj, _signals);
517
518    return obj;
519 }
520
521 EAPI Elm_Segment_Item *
522 elm_segment_control_item_add(Evas_Object *obj, Evas_Object *icon,
523                              const char *label)
524 {
525    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
526    Elm_Segment_Item *it;
527    Widget_Data *wd;
528
529    wd = elm_widget_data_get(obj);
530    if (!wd) return NULL;
531
532    it = _item_new(obj, icon, label);
533    if (!it) return NULL;
534
535    wd->seg_items = eina_list_append(wd->seg_items, it);
536    _update_list(wd);
537
538    return it;
539 }
540
541 EAPI Elm_Segment_Item *
542 elm_segment_control_item_insert_at(Evas_Object *obj, Evas_Object *icon,
543                                    const char *label, int idx)
544 {
545    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
546    Elm_Segment_Item *it, *it_rel;
547    Widget_Data *wd;
548
549    wd = elm_widget_data_get(obj);
550    if (!wd) return NULL;
551    if (idx < 0) idx = 0;
552
553    it = _item_new(obj, icon, label);
554    if (!it) return NULL;
555
556    it_rel = _item_find(obj, idx);
557    if (it_rel)
558      wd->seg_items = eina_list_prepend_relative(wd->seg_items, it, it_rel);
559    else
560      wd->seg_items = eina_list_append(wd->seg_items, it);
561
562    _update_list(wd);
563    return it;
564 }
565
566 EAPI void
567 elm_segment_control_item_del(Elm_Segment_Item *it)
568 {
569    ELM_WIDGET_ITEM_WIDTYPE_CHECK_OR_RETURN(it);
570    Widget_Data *wd;
571
572    wd = elm_widget_item_data_get(it);
573    if (!wd) return;
574
575    _item_free(it);
576    _update_list(wd);
577 }
578
579 EAPI void
580 elm_segment_control_item_del_at(Evas_Object *obj, int idx)
581 {
582    ELM_CHECK_WIDTYPE(obj, widtype);
583    Elm_Segment_Item *it;
584    Widget_Data *wd;
585
586    wd = elm_widget_data_get(obj);
587    if (!wd) return;
588
589    it = _item_find(obj, idx);
590    if (!it) return;
591    _item_free(it);
592    _update_list(wd);
593 }
594
595 EAPI const char*
596 elm_segment_control_item_label_get(const Evas_Object *obj, int idx)
597 {
598    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
599    Elm_Segment_Item *it;
600
601    it = _item_find(obj, idx);
602    if (it) return it->label;
603
604    return NULL;
605 }
606
607 EAPI void
608 elm_segment_control_item_label_set(Elm_Segment_Item* it, const char* label)
609 {
610    ELM_WIDGET_ITEM_WIDTYPE_CHECK_OR_RETURN(it);
611    Widget_Data *wd;
612
613    wd = elm_widget_item_data_get(it);
614    if (!wd) return;
615
616    eina_stringshare_replace(&it->label, label);
617    if (it->label)
618      edje_object_signal_emit(VIEW(it), "elm,state,text,visible", "elm");
619    else
620      edje_object_signal_emit(VIEW(it), "elm,state,text,hidden", "elm");
621    edje_object_message_signal_process(VIEW(it));
622    //label can be NULL also.
623    edje_object_part_text_set(VIEW(it), "elm.text", it->label);
624 }
625
626 EAPI Evas_Object *
627 elm_segment_control_item_icon_get(const Evas_Object *obj, int idx)
628 {
629    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
630    Elm_Segment_Item *it;
631
632    it = _item_find(obj, idx);
633    if (it) return it->icon;
634
635    return NULL;
636 }
637
638 EAPI void
639 elm_segment_control_item_icon_set(Elm_Segment_Item *it, Evas_Object *icon)
640 {
641    ELM_WIDGET_ITEM_WIDTYPE_CHECK_OR_RETURN(it);
642
643    //Remove the existing icon
644    if (it->icon)
645      {
646         edje_object_part_unswallow(VIEW(it), it->icon);
647         evas_object_del(it->icon);
648         it->icon = NULL;
649      }
650
651    it->icon = icon;
652    if (it->icon)
653      {
654         elm_widget_sub_object_add(VIEW(it), it->icon);
655         edje_object_part_swallow(VIEW(it), "elm.swallow.icon", it->icon);
656         edje_object_signal_emit(VIEW(it), "elm,state,icon,visible", "elm");
657      }
658    else
659      edje_object_signal_emit(VIEW(it), "elm,state,icon,hidden", "elm");
660 }
661
662 EAPI int
663 elm_segment_control_item_count_get(const Evas_Object *obj)
664 {
665    ELM_CHECK_WIDTYPE(obj, widtype) 0;
666    Widget_Data *wd;
667
668    wd = elm_widget_data_get(obj);
669    if (!wd) return 0;
670
671    return eina_list_count(wd->seg_items);
672 }
673
674 EAPI Evas_Object*
675 elm_segment_control_item_object_get(const Elm_Segment_Item *it)
676 {
677    ELM_WIDGET_ITEM_WIDTYPE_CHECK_OR_RETURN(it, NULL);
678
679    return VIEW(it);
680 }
681
682 EAPI Elm_Segment_Item*
683 elm_segment_control_item_selected_get(const Evas_Object *obj)
684 {
685    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
686    Widget_Data *wd;
687
688    wd = elm_widget_data_get(obj);
689    if (!wd) return NULL;
690
691    return wd->selected_item;
692 }
693
694 EAPI void
695 elm_segment_control_item_selected_set(Elm_Segment_Item *it, Eina_Bool selected)
696 {
697    ELM_WIDGET_ITEM_WIDTYPE_CHECK_OR_RETURN(it);
698    Widget_Data *wd;
699
700    wd = elm_widget_item_data_get(it);
701    if (!wd) return;
702
703    if (it == wd->selected_item)
704      {
705         //already in selected state.
706         if (selected) return;
707
708         //unselect case
709         _segment_off(it);
710      }
711    else if (selected)
712      _segment_on(it);
713
714    return;
715 }
716
717 EAPI Elm_Segment_Item *
718 elm_segment_control_item_get(const Evas_Object *obj, int idx)
719 {
720    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
721    Elm_Segment_Item *it;
722
723    it = _item_find(obj, idx);
724
725    return it;
726 }
727
728 EAPI int
729 elm_segment_control_item_index_get(const Elm_Segment_Item *it)
730 {
731    ELM_WIDGET_ITEM_WIDTYPE_CHECK_OR_RETURN(it, -1);
732
733    return it->seg_index;
734 }