Merge "[prevent issues ID:55300,38821,19282,55298 for test_fileselector.c and elc_fil...
[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,
222                                    (void *)(unsigned long)it->seg_index);
223 }
224
225 static void
226 _position_items(Widget_Data *wd)
227 {
228    Eina_List *l, *ll;
229    Elm_Segment_Item *it;
230    Eina_Bool rtl;
231    int bx, by, bw, bh, pos;
232
233    wd->item_count = eina_list_count(wd->seg_items);
234    if (wd->item_count <= 0) return;
235
236    evas_object_geometry_get(wd->base, &bx, &by, &bw, &bh);
237    wd->item_width = bw / wd->item_count;
238    rtl = elm_widget_mirrored_get(wd->obj);
239
240    if (rtl)
241      pos = bx + bw - wd->item_width;
242    else
243      pos = bx;
244
245    EINA_LIST_FOREACH(wd->seg_items, l, it)
246      {
247         evas_object_move(VIEW(it), pos, by);
248         evas_object_resize(VIEW(it), wd->item_width, bh);
249         if (rtl)
250           pos -= wd->item_width;
251         else
252           pos += wd->item_width;
253      }
254    ll = eina_list_last(wd->seg_items);
255    it = ll->data;
256    evas_object_resize(it->base.view,
257                         (bw - ((wd->item_count - 1) * wd->item_width)),
258                         bh);
259    _sizing_eval(wd->obj);
260 }
261
262 static void
263 _on_move_resize(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__,
264                 void *event_info __UNUSED__)
265 {
266    Widget_Data *wd;
267
268    wd = elm_widget_data_get(data);
269    if (!wd) return;
270
271    _position_items(wd);
272
273 }
274
275 static void
276 _mouse_up(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__,
277           void *event_info)
278 {
279    Widget_Data *wd;
280    Elm_Segment_Item *it;
281    Evas_Event_Mouse_Up *ev;
282    Evas_Coord x, y, w, h;
283
284    it = data;
285    if (!it) return;
286
287    wd = elm_widget_item_data_get(it);
288    if (!wd) return;
289
290    if (elm_widget_disabled_get(wd->obj)) return;
291
292    if (it == wd->selected_item) return;
293
294    ev = event_info;
295    evas_object_geometry_get(VIEW(it), &x, &y, &w, &h);
296
297    if ((ev->canvas.x >= x) && (ev->output.x <= (x + w)) && (ev->canvas.y >= y)
298        && (ev->canvas.y <= (y + h)))
299      _segment_on(it);
300    else
301      edje_object_signal_emit(VIEW(it), "elm,state,segment,normal", "elm");
302 }
303
304 static void
305 _mouse_down(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__,
306             void *event_info __UNUSED__)
307 {
308    Widget_Data *wd;
309    Elm_Segment_Item *it;
310
311    it = data;
312    if (!it) return;
313
314    wd = elm_widget_item_data_get(it);
315    if (!wd) return;
316
317    if (elm_widget_disabled_get(wd->obj)) return;
318
319    if (it == wd->selected_item) return;
320
321    edje_object_signal_emit(VIEW(it), "elm,state,segment,pressed", "elm");
322 }
323
324 static void
325 _swallow_item_objects(Elm_Segment_Item *it)
326 {
327    if (!it) return;
328
329    if (it->icon)
330      {
331         edje_object_part_swallow(VIEW(it), "elm.swallow.icon", it->icon);
332         edje_object_signal_emit(VIEW(it), "elm,state,icon,visible", "elm");
333      }
334    else
335      edje_object_signal_emit(VIEW(it), "elm,state,icon,hidden", "elm");
336
337    if (it->label)
338      {
339         edje_object_part_text_set(VIEW(it), "elm.text", it->label);
340         edje_object_signal_emit(VIEW(it), "elm,state,text,visible", "elm");
341      }
342    else
343      edje_object_signal_emit(VIEW(it), "elm,state,text,hidden", "elm");
344    edje_object_message_signal_process(VIEW(it));
345 }
346
347 static void
348 _update_list(Widget_Data *wd)
349 {
350    Eina_List *l;
351    Elm_Segment_Item *it;
352    Eina_Bool rtl;
353    int idx = 0;
354
355    _position_items(wd);
356
357    if (wd->item_count == 1)
358      {
359         it = eina_list_nth(wd->seg_items, 0);
360         it->seg_index = 0;
361
362         //Set the segment type
363         edje_object_signal_emit(VIEW(it),
364                                 "elm,type,segment,single", "elm");
365
366         //Set the segment state
367         if (wd->selected_item == it)
368           edje_object_signal_emit(VIEW(it),
369                                   "elm,state,segment,selected", "elm");
370         else
371           edje_object_signal_emit(VIEW(it),
372                                   "elm,state,segment,normal", "elm");
373
374         if (elm_widget_disabled_get(wd->obj))
375           edje_object_signal_emit(VIEW(it), "elm,state,disabled", "elm");
376         else
377           edje_object_signal_emit(VIEW(it), "elm,state,enabled", "elm");
378
379         _swallow_item_objects(it);
380         return;
381      }
382
383    rtl = elm_widget_mirrored_get(wd->obj);
384    EINA_LIST_FOREACH(wd->seg_items, l, it)
385      {
386         it->seg_index = idx;
387
388         //Set the segment type
389         if (idx == 0)
390           {
391              if (rtl)
392                edje_object_signal_emit(VIEW(it),
393                                        "elm,type,segment,right", "elm");
394              else
395                edje_object_signal_emit(VIEW(it),
396                                        "elm,type,segment,left", "elm");
397           }
398         else if (idx == (wd->item_count - 1))
399           {
400              if (rtl)
401                edje_object_signal_emit(VIEW(it),
402                                        "elm,type,segment,left", "elm");
403              else
404                edje_object_signal_emit(VIEW(it),
405                                        "elm,type,segment,right", "elm");
406           }
407         else
408           edje_object_signal_emit(VIEW(it),
409                                   "elm,type,segment,middle", "elm");
410
411         //Set the segment state
412         if (wd->selected_item == it)
413           edje_object_signal_emit(VIEW(it),
414                                   "elm,state,segment,selected", "elm");
415         else
416           edje_object_signal_emit(VIEW(it),
417                                   "elm,state,segment,normal", "elm");
418
419         if (elm_widget_disabled_get(wd->obj))
420           edje_object_signal_emit(VIEW(it), "elm,state,disabled", "elm");
421         else
422           edje_object_signal_emit(VIEW(it), "elm,state,enabled", "elm");
423
424         _swallow_item_objects(it);
425         idx++;
426      }
427 }
428
429 static Elm_Segment_Item *
430 _item_find(const Evas_Object *obj, int idx)
431 {
432    Widget_Data *wd;
433    Elm_Segment_Item *it;
434
435    wd = elm_widget_data_get(obj);
436    if (!wd) return NULL;
437
438    it = eina_list_nth(wd->seg_items, idx);
439    return it;
440 }
441
442 static Elm_Segment_Item*
443 _item_new(Evas_Object *obj, Evas_Object *icon, const char *label)
444 {
445    Elm_Segment_Item *it;
446    Widget_Data *wd;
447
448    wd = elm_widget_data_get(obj);
449    if (!wd) return NULL;
450
451    it = elm_widget_item_new(obj, Elm_Segment_Item);
452    if (!it) return NULL;
453    elm_widget_item_data_set(it, wd);
454
455    VIEW(it) = edje_object_add(evas_object_evas_get(obj));
456    edje_object_scale_set(VIEW(it), elm_widget_scale_get(VIEW(it))
457                          *_elm_config->scale);
458    evas_object_smart_member_add(VIEW(it), obj);
459    elm_widget_sub_object_add(obj, VIEW(it));
460    evas_object_clip_set(VIEW(it), evas_object_clip_get(obj));
461
462    _elm_theme_object_set(obj, VIEW(it), "segment_control", "item",
463                          elm_object_style_get(obj));
464    edje_object_mirrored_set(VIEW(it),
465                             elm_widget_mirrored_get(WIDGET(it)));
466
467    if (label)
468      eina_stringshare_replace(&it->label, label);
469    if (it->label)
470      edje_object_signal_emit(VIEW(it), "elm,state,text,visible", "elm");
471    else
472      edje_object_signal_emit(VIEW(it), "elm,state,text,hidden", "elm");
473    edje_object_message_signal_process(VIEW(it));
474    edje_object_part_text_set(VIEW(it), "elm.text", label);
475
476    it->icon = icon;
477    if (it->icon) elm_widget_sub_object_add(VIEW(it), it->icon);
478    evas_object_event_callback_add(VIEW(it), EVAS_CALLBACK_MOUSE_DOWN,
479                                   _mouse_down, it);
480    evas_object_event_callback_add(VIEW(it), EVAS_CALLBACK_MOUSE_UP,
481                                   _mouse_up, it);
482    evas_object_show(VIEW(it));
483
484    return it;
485 }
486
487 EAPI Evas_Object *
488 elm_segment_control_add(Evas_Object *parent)
489 {
490    Evas_Object *obj;
491    Evas *e;
492    Widget_Data *wd;
493
494    ELM_WIDGET_STANDARD_SETUP(wd, Widget_Data, parent, e, obj, NULL);
495
496    ELM_SET_WIDTYPE(widtype, "segment_control");
497    elm_widget_type_set(obj, "segment_control");
498    elm_widget_sub_object_add(parent, obj);
499    elm_widget_data_set(obj, wd);
500    elm_widget_del_hook_set(obj, _del_hook);
501    elm_widget_theme_hook_set(obj, _theme_hook);
502    elm_widget_disable_hook_set(obj, _disable_hook);
503
504    // TODO: Focus switch support to Elm_widget_Item not supported yet.
505 #if 0
506    elm_widget_focus_next_hook_set(obj, _focus_next_hook);
507 #endif
508
509    wd->obj = obj;
510
511    wd->base = edje_object_add(e);
512    edje_object_scale_set(wd->base, elm_widget_scale_get(wd->base)
513                          *_elm_config->scale);
514    _elm_theme_object_set(obj, wd->base, "segment_control", "base", "default");
515    elm_widget_resize_object_set(obj, wd->base);
516
517    evas_object_event_callback_add(obj, EVAS_CALLBACK_RESIZE,
518                                   _on_move_resize, obj);
519    evas_object_event_callback_add(obj, EVAS_CALLBACK_MOVE,
520                                   _on_move_resize, obj);
521
522    evas_object_smart_callbacks_descriptions_set(obj, _signals);
523
524    return obj;
525 }
526
527 EAPI Elm_Segment_Item *
528 elm_segment_control_item_add(Evas_Object *obj, Evas_Object *icon,
529                              const char *label)
530 {
531    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
532    Elm_Segment_Item *it;
533    Widget_Data *wd;
534
535    wd = elm_widget_data_get(obj);
536    if (!wd) return NULL;
537
538    it = _item_new(obj, icon, label);
539    if (!it) return NULL;
540
541    wd->seg_items = eina_list_append(wd->seg_items, it);
542    _update_list(wd);
543
544    return it;
545 }
546
547 EAPI Elm_Segment_Item *
548 elm_segment_control_item_insert_at(Evas_Object *obj, Evas_Object *icon,
549                                    const char *label, int idx)
550 {
551    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
552    Elm_Segment_Item *it, *it_rel;
553    Widget_Data *wd;
554
555    wd = elm_widget_data_get(obj);
556    if (!wd) return NULL;
557    if (idx < 0) idx = 0;
558
559    it = _item_new(obj, icon, label);
560    if (!it) return NULL;
561
562    it_rel = _item_find(obj, idx);
563    if (it_rel)
564      wd->seg_items = eina_list_prepend_relative(wd->seg_items, it, it_rel);
565    else
566      wd->seg_items = eina_list_append(wd->seg_items, it);
567
568    _update_list(wd);
569    return it;
570 }
571
572 EAPI void
573 elm_segment_control_item_del(Elm_Segment_Item *it)
574 {
575    ELM_WIDGET_ITEM_WIDTYPE_CHECK_OR_RETURN(it);
576    Widget_Data *wd;
577
578    wd = elm_widget_item_data_get(it);
579    if (!wd) return;
580
581    _item_free(it);
582    _update_list(wd);
583 }
584
585 EAPI void
586 elm_segment_control_item_del_at(Evas_Object *obj, int idx)
587 {
588    ELM_CHECK_WIDTYPE(obj, widtype);
589    Elm_Segment_Item *it;
590    Widget_Data *wd;
591
592    wd = elm_widget_data_get(obj);
593    if (!wd) return;
594
595    it = _item_find(obj, idx);
596    if (!it) return;
597    _item_free(it);
598    _update_list(wd);
599 }
600
601 EAPI const char*
602 elm_segment_control_item_label_get(const Evas_Object *obj, int idx)
603 {
604    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
605    Elm_Segment_Item *it;
606
607    it = _item_find(obj, idx);
608    if (it) return it->label;
609
610    return NULL;
611 }
612
613 EAPI void
614 elm_segment_control_item_label_set(Elm_Segment_Item* it, const char* label)
615 {
616    ELM_WIDGET_ITEM_WIDTYPE_CHECK_OR_RETURN(it);
617    Widget_Data *wd;
618
619    wd = elm_widget_item_data_get(it);
620    if (!wd) return;
621
622    eina_stringshare_replace(&it->label, label);
623    if (it->label)
624      edje_object_signal_emit(VIEW(it), "elm,state,text,visible", "elm");
625    else
626      edje_object_signal_emit(VIEW(it), "elm,state,text,hidden", "elm");
627    edje_object_message_signal_process(VIEW(it));
628    //label can be NULL also.
629    edje_object_part_text_set(VIEW(it), "elm.text", it->label);
630 }
631
632 EAPI Evas_Object *
633 elm_segment_control_item_icon_get(const Evas_Object *obj, int idx)
634 {
635    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
636    Elm_Segment_Item *it;
637
638    it = _item_find(obj, idx);
639    if (it) return it->icon;
640
641    return NULL;
642 }
643
644 EAPI void
645 elm_segment_control_item_icon_set(Elm_Segment_Item *it, Evas_Object *icon)
646 {
647    ELM_WIDGET_ITEM_WIDTYPE_CHECK_OR_RETURN(it);
648
649    //Remove the existing icon
650    if (it->icon)
651      {
652         edje_object_part_unswallow(VIEW(it), it->icon);
653         evas_object_del(it->icon);
654         it->icon = NULL;
655      }
656
657    it->icon = icon;
658    if (it->icon)
659      {
660         elm_widget_sub_object_add(VIEW(it), it->icon);
661         edje_object_part_swallow(VIEW(it), "elm.swallow.icon", it->icon);
662         edje_object_signal_emit(VIEW(it), "elm,state,icon,visible", "elm");
663      }
664    else
665      edje_object_signal_emit(VIEW(it), "elm,state,icon,hidden", "elm");
666 }
667
668 EAPI int
669 elm_segment_control_item_count_get(const Evas_Object *obj)
670 {
671    ELM_CHECK_WIDTYPE(obj, widtype) 0;
672    Widget_Data *wd;
673
674    wd = elm_widget_data_get(obj);
675    if (!wd) return 0;
676
677    return eina_list_count(wd->seg_items);
678 }
679
680 EAPI Evas_Object*
681 elm_segment_control_item_object_get(const Elm_Segment_Item *it)
682 {
683    ELM_WIDGET_ITEM_WIDTYPE_CHECK_OR_RETURN(it, NULL);
684
685    return VIEW(it);
686 }
687
688 EAPI Elm_Segment_Item*
689 elm_segment_control_item_selected_get(const Evas_Object *obj)
690 {
691    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
692    Widget_Data *wd;
693
694    wd = elm_widget_data_get(obj);
695    if (!wd) return NULL;
696
697    return wd->selected_item;
698 }
699
700 EAPI void
701 elm_segment_control_item_selected_set(Elm_Segment_Item *it, Eina_Bool selected)
702 {
703    ELM_WIDGET_ITEM_WIDTYPE_CHECK_OR_RETURN(it);
704    Widget_Data *wd;
705
706    wd = elm_widget_item_data_get(it);
707    if (!wd) return;
708
709    if (it == wd->selected_item)
710      {
711         //already in selected state.
712         if (selected) return;
713
714         //unselect case
715         _segment_off(it);
716      }
717    else if (selected)
718      _segment_on(it);
719
720    return;
721 }
722
723 EAPI Elm_Segment_Item *
724 elm_segment_control_item_get(const Evas_Object *obj, int idx)
725 {
726    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
727    Elm_Segment_Item *it;
728
729    it = _item_find(obj, idx);
730
731    return it;
732 }
733
734 EAPI int
735 elm_segment_control_item_index_get(const Elm_Segment_Item *it)
736 {
737    ELM_WIDGET_ITEM_WIDTYPE_CHECK_OR_RETURN(it, -1);
738
739    return it->seg_index;
740 }