Merge branch 'master' of 165.213.180.234:/git/slp2.0/slp2.0-pkgs/EFL-pkgs/elementary
[framework/uifw/elementary.git] / src / lib / elm_pager.c
1 #include <Elementary.h>
2 #include "elm_priv.h"
3
4 /**
5  * @defgroup Pager Pager
6  * @ingroup Elementary
7  *
8  * The pager is an object that allows flipping (with animation) between 1 or
9  * more ?\9cpages??of objects, much like a stack of windows within the window.
10  *
11  * Objects can be pushed or popped from he stack or deleted as normal.
12  * Pushes and pops will animate (and a pop will delete the object once the
13  * animation is finished). Any object in the pager can be promoted to the top
14  * (from its current stacking position) as well. Objects are pushed to the
15  * top with elm_pager_content_push() and when the top item is no longer
16  * wanted, simply pop it with elm_pager_content_pop() and it will also be
17  * deleted. Any object you wish to promote to the top that is already in the
18  * pager, simply use elm_pager_content_promote(). If an object is no longer
19  * needed and is not the top item, just delete it as normal. You can query
20  * which objects are the top and bottom with elm_pager_content_bottom_get()
21  * and elm_pager_content_top_get().
22  */
23
24 typedef struct _Widget_Data Widget_Data;
25 typedef struct _Item Item;
26
27 struct _Widget_Data
28 {
29    Eina_List *stack;
30    Item *top, *oldtop;
31    Evas_Object *rect, *clip;
32 };
33
34 struct _Item
35 {
36    Evas_Object *obj, *base, *content;
37    Evas_Coord minw, minh;
38    Eina_Bool popme : 1;
39 };
40
41 static Eina_Bool ani = EINA_TRUE;
42 static const char *widtype = NULL;
43 static void _del_hook(Evas_Object *obj);
44 static void _theme_hook(Evas_Object *obj);
45 static void _sizing_eval(Evas_Object *obj);
46 static void _changed_size_hints(void *data, Evas *e, Evas_Object *obj, void *event_info);
47 static void _sub_del(void *data, Evas_Object *obj, void *event_info);
48 static void _signal_hide_finished(void *data, Evas_Object *obj, const char *emission, const char *source);
49
50 static void
51 _del_hook(Evas_Object *obj)
52 {
53    Widget_Data *wd = elm_widget_data_get(obj);
54    if (!wd) return;
55    free(wd);
56 }
57
58 static void
59 _theme_hook(Evas_Object *obj)
60 {
61    Widget_Data *wd = elm_widget_data_get(obj);
62    Eina_List *l;
63    Item *it;
64    if (!wd) return;
65    EINA_LIST_FOREACH(wd->stack, l, it)
66      edje_object_scale_set(it->base, elm_widget_scale_get(obj) * 
67                            _elm_config->scale);
68    _sizing_eval(obj);
69 }
70
71 static void
72 _sizing_eval(Evas_Object *obj)
73 {
74    Widget_Data *wd = elm_widget_data_get(obj);
75    Evas_Coord minw = -1, minh = -1;
76    Eina_List *l;
77    Item *it;
78    if (!wd) return;
79    EINA_LIST_FOREACH(wd->stack, l, it)
80      {
81         if (it->minw > minw) minw = it->minw;
82         if (it->minh > minh) minh = it->minh;
83      }
84    evas_object_size_hint_min_set(obj, minw, minh);
85    evas_object_size_hint_max_set(obj, -1, -1);
86 }
87
88 static void
89 _changed_size_hints(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
90 {
91    Item *it = data;
92    Evas_Coord minw = -1, minh = -1;
93    evas_object_size_hint_min_get(it->content, &minw, &minh);
94    // FIXME: why is this needed? how does edje get this unswallowed or
95    // lose its callbacks to edje
96    edje_object_part_swallow(it->base, "elm.swallow.content", it->content);
97    edje_object_size_min_calc(it->base, &it->minw, &it->minh);
98    _sizing_eval(it->obj);
99 }
100
101 static void 
102 _complete_cb( void* data )
103 {
104         Item *it = data;
105         Evas_Object *obj = it->obj;
106
107         evas_object_hide(it->base);
108         edje_object_signal_emit(it->base, "elm,action,reset", "elm");
109         evas_object_smart_callback_call(obj, "hide,finished", it->content);
110         edje_object_message_signal_process(it->base);
111         if (it->popme) evas_object_del(it->content);
112         _sizing_eval(obj);
113 }
114
115 static void
116 _eval_top(Evas_Object *obj)
117 {
118    Widget_Data *wd = elm_widget_data_get(obj);
119    Item *it, *ittop;
120
121    if (!wd) return;
122    if (!wd->stack) return;
123    ittop = eina_list_last(wd->stack)->data;
124    if (ittop != wd->top)
125      {
126         Evas_Object *o= NULL, *prev_o = NULL;
127         const char *onshow, *onhide;
128         Eina_Bool pop = EINA_FALSE;
129         Evas_Coord w, y;
130         Elm_Transit *transit;
131
132 //      transit = elm_transit_add(obj);
133         evas_object_geometry_get( obj, NULL, &y, &w, NULL );
134
135         if (wd->top)
136           {
137              it = wd->top;
138              o = wd->top->base;
139
140              if (wd->top->popme) pop = EINA_TRUE;
141              if (ani) 
142                 {
143                   if (pop) evas_object_move( o, w, y );//elm_transit_fx_insert( transit, elm_fx_translation_add(o , 0, y, w, y));
144                   else evas_object_move(o, -w, y);//elm_transit_fx_insert( transit, elm_fx_translation_add(o , 0, y, -w, y));
145                   //elm_transit_completion_callback_set( transit, _complete_cb, it);
146                   _complete_cb( it );
147                 }
148              else 
149                 {
150                   _signal_hide_finished(wd->top, o, "elm,action,hide,finished", "");
151                 }       
152                  
153              onhide = edje_object_data_get(o, "onhide");
154              if (onhide)
155                {
156                   if (!strcmp(onhide, "raise")) evas_object_raise(o);
157                   else if (!strcmp(onhide, "lower")) evas_object_lower(o);
158                }
159           }
160         wd->top = ittop;
161         prev_o = wd->top->base;
162         evas_object_show(prev_o);
163         
164         //add show/hide transition direction & animation on/off. 10.04.14 sohyun
165         if (ani && o) 
166           {
167             if (pop) evas_object_move(prev_o, 0, y );//elm_transit_fx_insert( transit, elm_fx_translation_add(prev_o, -w, y, 0, y));
168             else evas_object_move( prev_o, 0, y );//elm_transit_fx_insert( transit, elm_fx_translation_add(prev_o, w, y, 0, y));
169             //elm_transit_run( transit, 0.3 );
170           }
171         //elm_transit_del( transit ); 
172
173         onshow = edje_object_data_get(prev_o, "onshow");
174         if (onshow)
175           {
176              if (!strcmp(onshow, "raise")) evas_object_raise(prev_o);
177              else if (!strcmp(onshow, "lower")) evas_object_lower(prev_o);
178           }
179      }
180 }
181
182 static void
183 _move(void *data, Evas *e __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__)
184 {
185    Widget_Data *wd = elm_widget_data_get(data);
186    Evas_Coord x, y;
187    Eina_List *l;
188    Item *it;
189    if (!wd) return;
190    evas_object_geometry_get(obj, &x, &y, NULL, NULL);
191    EINA_LIST_FOREACH(wd->stack, l, it)
192      evas_object_move(it->base, x, y);
193 }
194
195 static void
196 _sub_del(void *data, Evas_Object *obj __UNUSED__, void *event_info)
197 {
198    Widget_Data *wd = elm_widget_data_get(data);
199    Evas_Object *sub = event_info;
200    Eina_List *l;
201    Item *it;
202    if (!wd) return;
203    EINA_LIST_FOREACH(wd->stack, l, it)
204      {
205         if (it->content == sub)
206           {
207              wd->stack = eina_list_remove_list(wd->stack, l);
208              evas_object_event_callback_del_full
209                (sub, EVAS_CALLBACK_CHANGED_SIZE_HINTS, _changed_size_hints, it);
210              evas_object_del(it->base);
211              _eval_top(it->obj);
212              free(it);
213              return;
214           }
215      }
216 }
217
218 static void
219 _resize(void *data, Evas *e __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__)
220 {   
221    Widget_Data *wd = elm_widget_data_get(data);
222    Evas_Coord w, h;
223    Eina_List *l;
224    Item *it;
225    if (!wd) return;
226    evas_object_geometry_get(obj, NULL, NULL, &w, &h);
227    EINA_LIST_FOREACH(wd->stack, l, it) evas_object_resize(it->base, w, h);
228 }
229
230 static void
231 _signal_hide_finished(void *data, Evas_Object *obj __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__)
232 {
233    Item *it = data;
234    Evas_Object *obj2 = it->obj;
235    evas_object_hide(it->base);
236    edje_object_signal_emit(it->base, "elm,action,reset", "elm");
237    evas_object_smart_callback_call(obj2, "hide,finished", it->content);
238    edje_object_message_signal_process(it->base);
239    if (it->popme) evas_object_del(it->content);
240    _sizing_eval(obj2);
241 }
242
243 /**
244  * Add a new pager to the parent
245  *
246  * @param parent The parent object
247  * @return The new object or NULL if it cannot be created
248  *
249  * @ingroup Pager
250  */
251 EAPI Evas_Object *
252 elm_pager_add(Evas_Object *parent)
253 {
254    Evas_Object *obj;
255    Evas *e;
256    Widget_Data *wd;
257
258    wd = ELM_NEW(Widget_Data);
259    e = evas_object_evas_get(parent);
260    obj = elm_widget_add(e);
261    ELM_SET_WIDTYPE(widtype, "pager");
262    elm_widget_type_set(obj, "pager");
263    elm_widget_sub_object_add(parent, obj);
264    elm_widget_data_set(obj, wd);
265    elm_widget_del_hook_set(obj, _del_hook);
266    elm_widget_theme_hook_set(obj, _theme_hook);
267
268    wd->clip = evas_object_rectangle_add(e);
269    elm_widget_resize_object_set(obj, wd->clip);
270    elm_widget_sub_object_add(obj, wd->clip);
271
272    wd->rect = evas_object_rectangle_add(e);
273    elm_widget_sub_object_add(obj, wd->rect);
274    evas_object_color_set(wd->rect, 255, 255, 255, 0); 
275    evas_object_clip_set(wd->rect, wd->clip);
276
277    evas_object_event_callback_add(obj, EVAS_CALLBACK_MOVE, _move, obj);
278    evas_object_event_callback_add(obj, EVAS_CALLBACK_RESIZE, _resize, obj);
279
280    evas_object_smart_callback_add(obj, "sub-object-del", _sub_del, obj);
281
282    _sizing_eval(obj);
283    return obj;
284 }
285
286 /**
287  * Push an object to the top of the pager stack (and show it)
288  *
289  * The object pushed becomes a child of the pager and will be controlled
290  * it and deleted when the pager is deleted.
291  *
292  * @param obj The pager object
293  * @param content The object to push
294  *
295  * @ingroup Pager
296  */
297 EAPI void
298 elm_pager_content_push(Evas_Object *obj, Evas_Object *content)
299 {
300    ELM_CHECK_WIDTYPE(obj, widtype);
301    Widget_Data *wd = elm_widget_data_get(obj);
302    Item *it = ELM_NEW(Item);
303    Evas_Coord x, y, w, h;
304    if (!wd) return;
305    if (!it) return;
306    it->obj = obj;
307    it->content = content;
308    it->base = edje_object_add(evas_object_evas_get(obj));
309    evas_object_smart_member_add(it->base, obj);
310    evas_object_geometry_get(obj, &x, &y, &w, &h);
311    evas_object_move(it->base, x, y);
312    evas_object_resize(it->base, w, h);
313    evas_object_clip_set(it->base, wd->clip);
314    elm_widget_sub_object_add(obj, it->base);
315    if (it->content) elm_widget_sub_object_add(obj, it->content);
316    _elm_theme_object_set(obj, it->base,  "pager", "base", elm_widget_style_get(obj));
317    edje_object_signal_callback_add(it->base, "elm,action,hide,finished", "", 
318                                    _signal_hide_finished, it);
319    evas_object_event_callback_add(it->content,
320                                   EVAS_CALLBACK_CHANGED_SIZE_HINTS,
321                                   _changed_size_hints, it);
322    edje_object_part_swallow(it->base, "elm.swallow.content", it->content);
323    edje_object_size_min_calc(it->base, &it->minw, &it->minh);
324    evas_object_show(it->content);
325    wd->stack = eina_list_append(wd->stack, it);
326    _eval_top(obj);
327    _sizing_eval(obj);
328 }
329
330 /**
331  * Pop the object that is on top of the stack
332  *
333  * This pops the object that is on top (visible) in the pager, makes it
334  * disappear, then deletes the object. The object that was underneath it
335  * on the stack will become visible.
336  *
337  * @param obj The pager object
338  *
339  * @ingroup Pager
340  */
341 EAPI void
342 elm_pager_content_pop(Evas_Object *obj)
343 {
344    ELM_CHECK_WIDTYPE(obj, widtype);
345    Widget_Data *wd = elm_widget_data_get(obj);
346    Eina_List *ll;
347    Item *it;
348    if (!wd) return;
349    if (!wd->stack) return;
350    it = eina_list_last(wd->stack)->data;
351    it->popme = EINA_TRUE;
352    ll = eina_list_last(wd->stack);
353    if (ll)
354      {
355         ll = ll->prev;
356         if (!ll)
357           {
358              Evas_Object *o;
359              const char *onhide;
360
361              wd->top = it;
362              o = wd->top->base;
363              if (ani) 
364                {
365                   Elm_Effect *transit;
366                   Evas_Coord w, y;
367 /*      
368                   transit = elm_transit_add(obj);
369                   evas_object_geometry_get(obj, NULL, &y, &w, NULL);
370                   elm_transit_fx_insert(transit, elm_fx_translation_add(o , 0, y, w, y));
371                   elm_transit_completion_callback_set(transit, _complete_cb, it);
372                   elm_transit_run(transit, 0.3 );
373                   elm_transit_del(transit);  */
374                   evas_object_move( o, w, y );
375                   _complete_cb( it );
376                   
377                   
378                 }
379              else _signal_hide_finished(wd->top, o, "elm,action,hide,finished", "");
380
381              onhide = edje_object_data_get(o, "onhide");
382              if (onhide)
383                {
384                   if (!strcmp(onhide, "raise")) evas_object_raise(o);
385                   else if (!strcmp(onhide, "lower")) evas_object_lower(o);
386                }
387              wd->top = NULL;
388           }
389         else
390           {
391              it = ll->data;
392              elm_pager_content_promote(obj, it->content);
393           }
394      }
395 }
396
397 /**
398  * Pop to the object that is on the stack
399  *
400  * This pops the objects that are on the stack, makes them
401  * disappear, then deletes the objects. The content will become visible.
402  *
403  * @param obj The pager object
404  * @param content The object to show
405  *
406  * @ingroup Pager
407  */
408 EAPI void
409 elm_pager_to_content_pop(Evas_Object *obj, Evas_Object *content)
410 {
411    ELM_CHECK_WIDTYPE(obj, widtype);
412    Widget_Data *wd = elm_widget_data_get(obj);
413    Eina_List *ll;
414    Item *it;
415    if (!wd) return;
416    if (!wd->stack) return;
417    it = eina_list_last(wd->stack)->data;
418    it->popme = EINA_TRUE;
419    ll = eina_list_last(wd->stack);
420    if (ll)
421      {
422        EINA_LIST_FOREACH(wd->stack, ll, it)
423           {
424             if (it->content == content)
425               {
426                  ll = eina_list_last(wd->stack);
427                  while (ll = ll->prev)
428                  {
429                    if (ll->data == it) break;
430                    else 
431                    {
432                      Item *itdel = ll->data;
433                      evas_object_del(itdel->content);
434                    }
435                  }
436                  elm_pager_content_promote(obj, content);   
437                  return;
438               }
439           }
440       }
441 }
442
443 /**
444  * Promote an object already in the pager stack to the top of the stack
445  *
446  * This will take the indicated object and promote it to the top of the stack
447  * as if it had been pushed there. The object must already be inside the
448  * pager stack to work.
449  *
450  * @param obj The pager object
451  * @param content The object to promote
452  *
453  * @ingroup Pager
454  */
455 EAPI void
456 elm_pager_content_promote(Evas_Object *obj, Evas_Object *content)
457 {
458    ELM_CHECK_WIDTYPE(obj, widtype);
459    Widget_Data *wd = elm_widget_data_get(obj);
460    Eina_List *l;
461    Item *it;
462    if (!wd) return;
463    EINA_LIST_FOREACH(wd->stack, l, it)
464      {
465         if (it->content == content)
466           {
467              wd->stack = eina_list_remove_list(wd->stack, l);
468              wd->stack = eina_list_append(wd->stack, it);
469              _eval_top(obj);
470              return;
471           }
472      }
473 }
474
475 /**
476  * Return the object at the bottom of the pager stack
477  *
478  * @param obj The pager object
479  * @return The bottom object or NULL if none
480  *
481  * @ingroup Pager
482  */
483 EAPI Evas_Object *
484 elm_pager_content_bottom_get(const Evas_Object *obj)
485 {
486    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
487    Widget_Data *wd = elm_widget_data_get(obj);
488    Item *it;
489    if (!wd) return NULL;
490    if (!wd->stack) return NULL;
491    it = wd->stack->data;
492    return it->content;
493 }
494
495 /**
496  * Return the object at the top of the pager stack
497  *
498  * @param obj The pager object
499  * @return The top object or NULL if none
500  *
501  * @ingroup Pager
502  */
503 EAPI Evas_Object *
504 elm_pager_content_top_get(const Evas_Object *obj)
505 {
506    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
507    Widget_Data *wd = elm_widget_data_get(obj);
508    Item *it;
509    if (!wd) return NULL;
510    if (!wd->stack) return NULL;
511    it = eina_list_last(wd->stack)->data;
512    return it->content;
513 }
514
515 /**
516  * set animation on/off for content transition effect
517  *
518  * @param obj The pager object
519  * @param animation transition when contents are changed (default value : TRUE)
520  *
521  * @ingroup Pager
522  */
523 EAPI void
524 elm_pager_animation_set(Evas_Object *obj, Eina_Bool animation)
525 {
526         ani = animation;
527 }
528