elementary - removed deprecated widgets & APIs.
[framework/uifw/elementary.git] / src / lib / elm_pager.c
1 #include <Elementary.h>
2 #include "elm_priv.h"
3
4 typedef struct _Widget_Data Widget_Data;
5 typedef struct _Item Item;
6
7 struct _Widget_Data
8 {
9    Eina_List *stack;
10    Item *top, *oldtop;
11    Evas_Object *rect, *clip;
12 };
13
14 struct _Item
15 {
16    Evas_Object *obj, *base, *content;
17    Evas_Coord minw, minh;
18    Eina_Bool popme : 1;
19 };
20
21 static const char *widtype = NULL;
22 static void _del_hook(Evas_Object *obj);
23 static void _mirrored_set(Evas_Object *obj, Eina_Bool rtl);
24 static void _theme_hook(Evas_Object *obj);
25 static void _sizing_eval(Evas_Object *obj);
26 static void _changed_size_hints(void *data,
27                                 Evas *e,
28                                 Evas_Object *obj,
29                                 void *event_info);
30 static void _content_del(void *data,
31                          Evas *e,
32                          Evas_Object *obj,
33                          void *event_info);
34 static Eina_List *_item_get(Evas_Object *obj, Evas_Object *content);
35
36 static const char SIG_HIDE_FINISHED[] = "hide,finished";
37
38 static const Evas_Smart_Cb_Description _signals[] = {
39    {SIG_HIDE_FINISHED, ""},
40    {NULL, NULL}
41 };
42
43 static void
44 _del_hook(Evas_Object *obj)
45 {
46    Widget_Data *wd = elm_widget_data_get(obj);
47    if (!wd) return;
48    free(wd);
49 }
50
51 static void
52 _mirrored_set(Evas_Object *obj, Eina_Bool rtl)
53 {
54    Widget_Data *wd = elm_widget_data_get(obj);
55    Eina_List *l;
56    Item *it;
57    if (!wd) return;
58    EINA_LIST_FOREACH(wd->stack, l, it)
59       edje_object_mirrored_set(it->base, rtl);
60 }
61
62 static void
63 _theme_hook(Evas_Object *obj)
64 {
65    Widget_Data *wd = elm_widget_data_get(obj);
66    Eina_List *l;
67    Item *it;
68    if (!wd) return;
69    _elm_widget_mirrored_reload(obj);
70    _mirrored_set(obj, elm_widget_mirrored_get(obj));
71    EINA_LIST_FOREACH(wd->stack, l, it)
72      {
73         _elm_theme_object_set(obj, it->base,  "pager", "base",
74                               elm_widget_style_get(obj));
75         edje_object_scale_set(it->base, elm_widget_scale_get(obj) *
76                               _elm_config->scale);
77      }
78    _sizing_eval(obj);
79 }
80
81 static Eina_List *
82 _item_get(Evas_Object *obj, Evas_Object *content)
83 {
84    Widget_Data *wd = elm_widget_data_get(obj);
85    Item *it;
86    Eina_List *l;
87    if (!wd) return NULL;
88
89    EINA_LIST_FOREACH(wd->stack, l, it)
90      {
91         if (it->content == content)
92           return l;
93      }
94
95    return NULL;
96 }
97
98 static Eina_Bool
99 _elm_pager_focus_next_hook(const Evas_Object *obj, Elm_Focus_Direction dir, Evas_Object **next)
100 {
101    Widget_Data *wd = elm_widget_data_get(obj);
102    Evas_Object *cur;
103
104    if ((!wd) || (!wd->top))
105      return EINA_FALSE;
106
107    cur = wd->top->content;
108
109    /* Try Focus cycle in subitem */
110    return elm_widget_focus_next_get(cur, dir, next);
111 }
112
113 static void
114 _sizing_eval(Evas_Object *obj)
115 {
116    Widget_Data *wd = elm_widget_data_get(obj);
117    Evas_Coord minw = -1, minh = -1;
118    Eina_List *l;
119    Item *it;
120    if (!wd) return;
121    EINA_LIST_FOREACH(wd->stack, l, it)
122      {
123         if (it->minw > minw) minw = it->minw;
124         if (it->minh > minh) minh = it->minh;
125      }
126    evas_object_size_hint_min_set(obj, minw, minh);
127    evas_object_size_hint_max_set(obj, -1, -1);
128 }
129
130 static void
131 _changed_size_hints(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
132 {
133    Item *it = data;
134    Evas_Coord minw = -1, minh = -1;
135    evas_object_size_hint_min_get(it->content, &minw, &minh);
136    // FIXME: why is this needed? how does edje get this unswallowed or
137    // lose its callbacks to edje
138    edje_object_part_swallow(it->base, "elm.swallow.content", it->content);
139    edje_object_size_min_calc(it->base, &it->minw, &it->minh);
140    _sizing_eval(it->obj);
141 }
142
143 static void
144 _eval_top(Evas_Object *obj)
145 {
146    Widget_Data *wd = elm_widget_data_get(obj);
147    Item *ittop;
148    if (!wd) return;
149    if (!wd->stack) return;
150    ittop = eina_list_last(wd->stack)->data;
151    if (ittop != wd->top)
152      {
153         Evas_Object *o;
154         const char *onshow, *onhide;
155
156         if (wd->top)
157           {
158              o = wd->top->base;
159              if (wd->top->popme)
160                {
161                   edje_object_signal_emit(o, "elm,action,pop", "elm");
162                   wd->stack = eina_list_remove(wd->stack, wd->top);
163                }
164              else
165                edje_object_signal_emit(o, "elm,action,hide", "elm");
166              onhide = edje_object_data_get(o, "onhide");
167              if (onhide)
168                {
169                   if (!strcmp(onhide, "raise")) evas_object_raise(o);
170                   else if (!strcmp(onhide, "lower")) evas_object_lower(o);
171                }
172           }
173         wd->oldtop = wd->top;
174         wd->top = ittop;
175         o = wd->top->base;
176         evas_object_show(o);
177         if (wd->oldtop)
178           {
179              if (elm_object_focus_get(wd->oldtop->content))
180                elm_widget_focused_object_clear(wd->oldtop->content);
181              if (wd->oldtop->popme)
182                edje_object_signal_emit(o, "elm,action,show", "elm");
183              else
184                edje_object_signal_emit(o, "elm,action,push", "elm");
185           }
186         else
187           edje_object_signal_emit(o, "elm,action,push", "elm");
188         onshow = edje_object_data_get(o, "onshow");
189         if (onshow)
190           {
191              if (!strcmp(onshow, "raise")) evas_object_raise(o);
192              else if (!strcmp(onshow, "lower")) evas_object_lower(o);
193           }
194      }
195 }
196
197 static void
198 _move(void *data, Evas *e __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__)
199 {
200    Widget_Data *wd = elm_widget_data_get(data);
201    Evas_Coord x, y;
202    Eina_List *l;
203    Item *it;
204    if (!wd) return;
205    evas_object_geometry_get(obj, &x, &y, NULL, NULL);
206    EINA_LIST_FOREACH(wd->stack, l, it)
207      evas_object_move(it->base, x, y);
208 }
209
210 static void
211 _content_del(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
212 {
213    Widget_Data *wd = elm_widget_data_get(data);
214    Eina_List *l;
215    Item *it;
216    if (!wd) return;
217    l = _item_get(data, obj);
218    if (!l) return;
219    it = l->data;
220    wd->stack = eina_list_remove_list(wd->stack, l);
221    evas_object_event_callback_del_full
222       (obj, EVAS_CALLBACK_CHANGED_SIZE_HINTS, _changed_size_hints, it);
223    evas_object_del(it->base);
224    _eval_top(data);
225    free(it);
226 }
227
228 static void
229 _resize(void *data, Evas *e __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__)
230 {
231    Widget_Data *wd = elm_widget_data_get(data);
232    Evas_Coord w, h;
233    Eina_List *l;
234    Item *it;
235    if (!wd) return;
236    evas_object_geometry_get(obj, NULL, NULL, &w, &h);
237    EINA_LIST_FOREACH(wd->stack, l, it) evas_object_resize(it->base, w, h);
238 }
239
240 static void
241 _signal_hide_finished(void *data, Evas_Object *obj __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__)
242 {
243    Item *it = data;
244    Evas_Object *obj2 = it->obj;
245    Evas_Object *content = it->content;
246
247    if (it->popme)
248      {
249         evas_object_del(it->base);
250         evas_object_event_callback_del_full(content,
251                                             EVAS_CALLBACK_CHANGED_SIZE_HINTS,
252                                             _changed_size_hints,
253                                             it);
254         evas_object_event_callback_del(content,
255                                        EVAS_CALLBACK_DEL,
256                                        _content_del);
257         evas_object_del(content);
258         free(it);
259      }
260    else
261      {
262         evas_object_hide(it->base);
263         edje_object_signal_emit(it->base, "elm,action,reset", "elm");
264         edje_object_message_signal_process(it->base);
265         evas_object_hide(content);
266      }
267     evas_object_smart_callback_call(obj2, SIG_HIDE_FINISHED, content);
268     _sizing_eval(obj2);
269 }
270
271 EAPI Evas_Object *
272 elm_pager_add(Evas_Object *parent)
273 {
274    Evas_Object *obj;
275    Evas *e;
276    Widget_Data *wd;
277
278    ELM_WIDGET_STANDARD_SETUP(wd, Widget_Data, parent, e, obj, NULL);
279
280    ELM_SET_WIDTYPE(widtype, "pager");
281    elm_widget_type_set(obj, "pager");
282    elm_widget_sub_object_add(parent, obj);
283    elm_widget_data_set(obj, wd);
284    elm_widget_del_hook_set(obj, _del_hook);
285    elm_widget_theme_hook_set(obj, _theme_hook);
286    elm_widget_focus_next_hook_set(obj, _elm_pager_focus_next_hook);
287    elm_widget_can_focus_set(obj, EINA_FALSE);
288
289    wd->clip = evas_object_rectangle_add(e);
290    elm_widget_resize_object_set(obj, wd->clip);
291    elm_widget_sub_object_add(obj, wd->clip);
292
293    wd->rect = evas_object_rectangle_add(e);
294    elm_widget_sub_object_add(obj, wd->rect);
295    evas_object_color_set(wd->rect, 255, 255, 255, 0);
296    evas_object_clip_set(wd->rect, wd->clip);
297
298    evas_object_event_callback_add(obj, EVAS_CALLBACK_MOVE, _move, obj);
299    evas_object_event_callback_add(obj, EVAS_CALLBACK_RESIZE, _resize, obj);
300
301    evas_object_smart_callbacks_descriptions_set(obj, _signals);
302
303    _mirrored_set(obj, elm_widget_mirrored_get(obj));
304    _sizing_eval(obj);
305    return obj;
306 }
307
308 EAPI void
309 elm_pager_content_push(Evas_Object *obj, Evas_Object *content)
310 {
311    ELM_CHECK_WIDTYPE(obj, widtype);
312    Widget_Data *wd = elm_widget_data_get(obj);
313    Evas_Coord x, y, w, h;
314    Item *it;
315
316    if ((!wd) || (!content)) return;
317    if (_item_get(obj, content)) return;
318
319    it = ELM_NEW(Item);
320    if (!it) return;
321    it->obj = obj;
322    it->content = content;
323    it->base = edje_object_add(evas_object_evas_get(obj));
324    evas_object_smart_member_add(it->base, obj);
325    evas_object_geometry_get(obj, &x, &y, &w, &h);
326    evas_object_move(it->base, x, y);
327    evas_object_resize(it->base, w, h);
328    evas_object_clip_set(it->base, wd->clip);
329    elm_widget_sub_object_add(obj, it->base);
330    elm_widget_sub_object_add(obj, it->content);
331    _elm_theme_object_set(obj,
332                          it->base,
333                          "pager",
334                          "base",
335                          elm_widget_style_get(obj));
336    edje_object_signal_callback_add(it->base,
337                                    "elm,action,hide,finished",
338                                    "",
339                                    _signal_hide_finished,
340                                    it);
341    evas_object_event_callback_add(it->content,
342                                   EVAS_CALLBACK_CHANGED_SIZE_HINTS,
343                                   _changed_size_hints,
344                                   it);
345    evas_object_event_callback_add(it->content,
346                                   EVAS_CALLBACK_DEL,
347                                   _content_del,
348                                   obj);
349    edje_object_part_swallow(it->base, "elm.swallow.content", it->content);
350    edje_object_size_min_calc(it->base, &it->minw, &it->minh);
351    evas_object_data_set(it->base, "_elm_leaveme", obj);
352    evas_object_show(it->content);
353    wd->stack = eina_list_append(wd->stack, it);
354    _eval_top(obj);
355    _sizing_eval(obj);
356 }
357
358 EAPI void
359 elm_pager_content_pop(Evas_Object *obj)
360 {
361    ELM_CHECK_WIDTYPE(obj, widtype);
362    Widget_Data *wd = elm_widget_data_get(obj);
363    Eina_List *ll;
364    Item *it;
365    if (!wd) return;
366    if (!wd->stack) return;
367    it = eina_list_last(wd->stack)->data;
368    it->popme = EINA_TRUE;
369    ll = eina_list_last(wd->stack);
370    if (ll)
371      {
372         ll = ll->prev;
373         if (!ll)
374           {
375              Evas_Object *o;
376              const char *onhide;
377
378              wd->top = it;
379              o = wd->top->base;
380              edje_object_signal_emit(o, "elm,action,pop", "elm");
381              wd->stack = eina_list_remove(wd->stack, it);
382              onhide = edje_object_data_get(o, "onhide");
383              if (onhide)
384                {
385                   if (!strcmp(onhide, "raise")) evas_object_raise(o);
386                   else if (!strcmp(onhide, "lower")) evas_object_lower(o);
387                }
388              wd->top = NULL;
389           }
390         else
391           {
392              it = ll->data;
393              elm_pager_content_promote(obj, it->content);
394           }
395      }
396 }
397
398 static void
399 _del_job(void *data)
400 {
401    Evas_Object *obj = data;
402    evas_object_del(obj);
403 }
404
405 EAPI void
406 elm_pager_to_content_pop(Evas_Object *obj, Evas_Object *content)
407 {
408    ELM_CHECK_WIDTYPE(obj, widtype);
409    Widget_Data *wd = elm_widget_data_get(obj);
410    Eina_List *ll;
411    Item *it;
412    if (!wd) return;
413    if (!wd->stack) return;
414    it = eina_list_last(wd->stack)->data;
415    it->popme = EINA_TRUE;
416    ll = eina_list_last(wd->stack);
417    if (ll)
418      {
419         while(ll)
420           {
421              it = ll->data;
422              if(it->content != content)
423                {
424                   wd->stack = eina_list_remove_list(wd->stack, ll);
425                   ecore_job_add(_del_job, it->content);
426                   it->content = NULL;
427                }
428              else
429                break;
430
431              ll = ll->prev;
432           }
433      }
434    _eval_top(it->obj);
435 }
436
437 EAPI void
438 elm_pager_content_promote(Evas_Object *obj, Evas_Object *content)
439 {
440    ELM_CHECK_WIDTYPE(obj, widtype);
441    Widget_Data *wd = elm_widget_data_get(obj);
442    Eina_List *l;
443    Item *it;
444    if (!wd) return;
445    l = _item_get(obj, content);
446    if (!l) return;
447
448    it = l->data;
449    wd->stack = eina_list_remove_list(wd->stack, l);
450    wd->stack = eina_list_append(wd->stack, it);
451    _eval_top(obj);
452 }
453
454 EAPI Evas_Object *
455 elm_pager_content_bottom_get(const Evas_Object *obj)
456 {
457    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
458    Widget_Data *wd = elm_widget_data_get(obj);
459    Item *it;
460    if (!wd) return NULL;
461    if (!wd->stack) return NULL;
462    it = wd->stack->data;
463    return it->content;
464 }
465
466 EAPI Evas_Object *
467 elm_pager_content_top_get(const Evas_Object *obj)
468 {
469    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
470    Widget_Data *wd = elm_widget_data_get(obj);
471    if (!wd) return NULL;
472    if (!wd->top) return NULL;
473    return wd->top->content;
474 }