994665fef0b7ba14601e8f71c9d562accf19f45d
[framework/uifw/elementary.git] / src / lib / elm_layout.c
1 #include <Elementary.h>
2 #include "elm_priv.h"
3
4 /**
5  * @defgroup Layout Layout
6  *
7  * This takes a standard Edje design file and wraps it very thinly
8  * in a widget and handles swallowing widgets into swallow regions
9  * in the Edje object, allowing Edje to be used as a design and
10  * layout tool
11  */
12
13 typedef struct _Widget_Data Widget_Data;
14 typedef struct _Subinfo Subinfo;
15 typedef struct _Part_Cursor Part_Cursor;
16
17 struct _Widget_Data
18 {
19    Evas_Object *obj;
20    Evas_Object *lay;
21    Eina_List *subs;
22    Eina_List *parts_cursors;
23    Eina_Bool needs_size_calc:1;
24    const char *clas, *group, *style;
25 };
26
27 struct _Subinfo
28 {
29    const char *part;
30    Evas_Object *obj;
31    enum {
32      SWALLOW,
33      BOX_APPEND,
34      BOX_PREPEND,
35      BOX_INSERT_BEFORE,
36      BOX_INSERT_AT,
37      TABLE_PACK,
38      TEXT
39    } type;
40    union {
41       union {
42          const Evas_Object *reference;
43          unsigned int pos;
44       } box;
45       struct {
46          unsigned short col, row, colspan, rowspan;
47       } table;
48       struct {
49          const char *text;
50       } text;
51    } p;
52 };
53
54 struct _Part_Cursor
55 {
56    Evas_Object *obj;
57    const char *part;
58    const char *cursor;
59    const char *style;
60    Eina_Bool engine_only:1;
61 };
62
63 static const char *widtype = NULL;
64 static void _del_hook(Evas_Object *obj);
65 static void _mirrored_set(Evas_Object *obj, Eina_Bool rtl);
66 static void _theme_hook(Evas_Object *obj);
67 static void _sizing_eval(Widget_Data *wd);
68 static void _changed_size_hints(void *data, Evas *e, Evas_Object *obj, void *event_info);
69 static void _sub_del(void *data, Evas_Object *obj, void *event_info);
70 static void _part_cursor_free(Part_Cursor *pc);
71
72 static const char SIG_THEME_CHANGED[] = "theme,changed";
73
74 static const Evas_Smart_Cb_Description _signals[] = {
75    {SIG_THEME_CHANGED, ""},
76    {NULL, NULL}
77 };
78
79 static void
80 _del_hook(Evas_Object *obj)
81 {
82    Widget_Data *wd = elm_widget_data_get(obj);
83    Subinfo *si;
84    Part_Cursor *pc;
85
86    if (!wd) return;
87    EINA_LIST_FREE(wd->subs, si)
88      {
89         eina_stringshare_del(si->part);
90         if (si->type == TEXT)
91           eina_stringshare_del(si->p.text.text);
92         free(si);
93      }
94    EINA_LIST_FREE(wd->parts_cursors, pc) _part_cursor_free(pc);
95    free(wd);
96 }
97
98 static void
99 _mirrored_set(Evas_Object *obj, Eina_Bool rtl)
100 {
101    Widget_Data *wd = elm_widget_data_get(obj);
102    if (!wd) return;
103    edje_object_mirrored_set(wd->lay, rtl);
104 }
105
106 static void
107 _theme_hook(Evas_Object *obj)
108 {
109    Widget_Data *wd = elm_widget_data_get(obj);
110    if (!wd) return;
111    _elm_widget_mirrored_reload(obj);
112    _mirrored_set(obj, elm_widget_mirrored_get(obj));
113    _elm_theme_object_set(obj, wd->lay, wd->clas, wd->group, wd->style);
114    edje_object_scale_set(wd->lay, elm_widget_scale_get(obj) *
115                          _elm_config->scale);
116    evas_object_smart_callback_call(obj, SIG_THEME_CHANGED, NULL);
117    _sizing_eval(wd);
118 }
119
120 static void
121 _changed_hook(Evas_Object *obj)
122 {
123    Widget_Data *wd = elm_widget_data_get(obj);
124    if (!wd) return;
125    if (wd->needs_size_calc)
126      {
127         _sizing_eval(wd);
128         wd->needs_size_calc = 0;
129      }
130 }
131
132 static void
133 _signal_emit_hook(Evas_Object *obj, const char *emission, const char *source)
134 {
135    Widget_Data *wd = elm_widget_data_get(obj);
136    edje_object_signal_emit(wd->lay, emission, source);
137 }
138
139 static void
140 _signal_callback_add_hook(Evas_Object *obj, const char *emission, const char *source, Edje_Signal_Cb func_cb, void *data)
141 {
142    Widget_Data *wd = elm_widget_data_get(obj);
143    edje_object_signal_callback_add(wd->lay, emission, source, func_cb, data);
144 }
145
146 static void
147 _signal_callback_del_hook(Evas_Object *obj, const char *emission, const char *source, Edje_Signal_Cb func_cb, void *data)
148 {
149    Widget_Data *wd = elm_widget_data_get(obj);
150    edje_object_signal_callback_del_full(wd->lay, emission, source, func_cb,
151                                         data);
152 }
153
154
155 static void *
156 _elm_layout_list_data_get(const Eina_List *list)
157 {
158    Subinfo *si = eina_list_data_get(list);
159    return si->obj;
160 }
161
162 static Eina_Bool
163 _elm_layout_focus_next_hook(const Evas_Object *obj, Elm_Focus_Direction dir, Evas_Object **next)
164 {
165    Widget_Data *wd = elm_widget_data_get(obj);
166    const Eina_List *items;
167    void *(*list_data_get) (const Eina_List *list);
168
169    if ((!wd) || (!wd->subs))
170      return EINA_FALSE;
171
172    /* Focus chain (This block is diferent of elm_win cycle)*/
173    if ((items = elm_widget_focus_custom_chain_get(obj)))
174      list_data_get = eina_list_data_get;
175    else
176      {
177         items = wd->subs;
178         list_data_get = _elm_layout_list_data_get;
179
180         if (!items) return EINA_FALSE;
181      }
182
183    return elm_widget_focus_list_next_get(obj, items, list_data_get, dir,
184                                          next);
185 }
186
187 static void
188 _sizing_eval(Widget_Data *wd)
189 {
190    Evas_Coord minw = -1, minh = -1;
191    edje_object_size_min_calc(wd->lay, &minw, &minh);
192    evas_object_size_hint_min_set(wd->obj, minw, minh);
193    evas_object_size_hint_max_set(wd->obj, -1, -1);
194 }
195
196 static void
197 _request_sizing_eval(Widget_Data *wd)
198 {
199    if (wd->needs_size_calc) return;
200    wd->needs_size_calc = 1;
201    evas_object_smart_changed(wd->obj);
202 }
203
204 static void
205 _part_cursor_free(Part_Cursor *pc)
206 {
207    eina_stringshare_del(pc->part);
208    eina_stringshare_del(pc->style);
209    eina_stringshare_del(pc->cursor);
210    free(pc);
211 }
212
213 static void
214 _part_cursor_part_apply(const Part_Cursor *pc)
215 {
216    elm_object_cursor_set(pc->obj, pc->cursor);
217    elm_object_cursor_style_set(pc->obj, pc->style);
218    elm_object_cursor_engine_only_set(pc->obj, pc->engine_only);
219 }
220
221 static Part_Cursor *
222 _parts_cursors_find(Widget_Data *wd, const char *part)
223 {
224    const Eina_List *l;
225    Part_Cursor *pc;
226    EINA_LIST_FOREACH(wd->parts_cursors, l, pc)
227      {
228         if (!strcmp(pc->part, part))
229           return pc;
230      }
231    return NULL;
232 }
233
234 static void
235 _parts_cursors_apply(Widget_Data *wd)
236 {
237    const char *file, *group;
238    const Eina_List *l;
239    Part_Cursor *pc;
240
241    edje_object_file_get(wd->lay, &file, &group);
242
243    EINA_LIST_FOREACH(wd->parts_cursors, l, pc)
244      {
245         Evas_Object *obj = (Evas_Object *)edje_object_part_object_get
246            (wd->lay, pc->part);
247
248         if (!obj)
249           {
250              pc->obj = NULL;
251              WRN("no part '%s' in group '%s' of file '%s'. "
252                  "Cannot set cursor '%s'",
253                  pc->part, group, file, pc->cursor);
254              continue;
255           }
256         else if (evas_object_pass_events_get(obj))
257           {
258              pc->obj = NULL;
259              WRN("part '%s' in group '%s' of file '%s' has mouse_events: 0. "
260                  "Cannot set cursor '%s'",
261                  pc->part, group, file, pc->cursor);
262              continue;
263           }
264
265         pc->obj = obj;
266         _part_cursor_part_apply(pc);
267      }
268 }
269
270 static void
271 _changed_size_hints(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
272 {
273    _request_sizing_eval(data);
274 }
275
276 static void
277 _sub_del(void *data __UNUSED__, Evas_Object *obj, void *event_info)
278 {
279    Widget_Data *wd = elm_widget_data_get(obj);
280    Evas_Object *sub = event_info;
281    Eina_List *l;
282    Subinfo *si;
283    if (!wd) return;
284    EINA_LIST_FOREACH(wd->subs, l, si)
285      {
286         if (si->obj == sub)
287           {
288              evas_object_event_callback_del_full(sub,
289                                                  EVAS_CALLBACK_CHANGED_SIZE_HINTS,
290                                                  _changed_size_hints,
291                                                  wd);
292              wd->subs = eina_list_remove_list(wd->subs, l);
293              eina_stringshare_del(si->part);
294              free(si);
295              break;
296           }
297      }
298 }
299
300 static void
301 _signal_size_eval(void *data, Evas_Object *obj __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__)
302 {
303    _request_sizing_eval(data);
304 }
305
306 static void
307 _parts_text_fix(Widget_Data *wd)
308 {
309    const Eina_List *l;
310    Subinfo *si;
311
312    EINA_LIST_FOREACH(wd->subs, l, si)
313      {
314         if (si->type == TEXT)
315           edje_object_part_text_set(wd->lay, si->part, si->p.text.text);
316      }
317 }
318
319 static void
320 _elm_layout_label_set(Evas_Object *obj, const char *part, const char *text)
321 {
322    Widget_Data *wd = elm_widget_data_get(obj);
323    Subinfo *si = NULL;
324    Eina_List *l;
325    ELM_CHECK_WIDTYPE(obj, widtype);
326    if (!part) part = "elm.text";
327
328    EINA_LIST_FOREACH(wd->subs, l, si)
329      {
330         if ((si->type == TEXT) && (!strcmp(part, si->part)))
331           {
332              if (!text)
333                {
334                   eina_stringshare_del(si->part);
335                   eina_stringshare_del(si->p.text.text);
336                   free(si);
337                   edje_object_part_text_set(wd->lay, part, NULL);
338                   wd->subs = eina_list_remove_list(wd->subs, l);
339                   return;
340                }
341              else
342                break;
343           }
344         si = NULL;
345      }
346
347    if (!si)
348      {
349         si = ELM_NEW(Subinfo);
350         if (!si) return;
351         si->type = TEXT;
352         si->part = eina_stringshare_add(part);
353         wd->subs = eina_list_append(wd->subs, si);
354      }
355
356    eina_stringshare_replace(&si->p.text.text, text);
357    edje_object_part_text_set(wd->lay, part, text);
358    _request_sizing_eval(wd);
359 }
360
361 static const char *
362 _elm_layout_label_get(const Evas_Object *obj, const char *part)
363 {
364    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
365    Widget_Data *wd = elm_widget_data_get(obj);
366    if (!part) part = "elm.text";
367    return edje_object_part_text_get(wd->lay, part);
368 }
369
370 /**
371  * Add a new layout to the parent
372  *
373  * @param parent The parent object
374  * @return The new object or NULL if it cannot be created
375  *
376  * @ingroup Layout
377  */
378 EAPI Evas_Object *
379 elm_layout_add(Evas_Object *parent)
380 {
381    Evas_Object *obj;
382    Evas *e;
383    Widget_Data *wd;
384
385    ELM_WIDGET_STANDARD_SETUP(wd, Widget_Data, parent, e, obj, NULL);
386
387    ELM_SET_WIDTYPE(widtype, "layout");
388    elm_widget_type_set(obj, "layout");
389    elm_widget_sub_object_add(parent, obj);
390    elm_widget_data_set(obj, wd);
391    elm_widget_del_hook_set(obj, _del_hook);
392    elm_widget_theme_hook_set(obj, _theme_hook);
393    elm_widget_changed_hook_set(obj, _changed_hook);
394    elm_widget_can_focus_set(obj, EINA_FALSE);
395    elm_widget_focus_next_hook_set(obj, _elm_layout_focus_next_hook);
396    elm_widget_signal_emit_hook_set(obj, _signal_emit_hook);
397    elm_widget_signal_callback_add_hook_set(obj, _signal_callback_add_hook);
398    elm_widget_signal_callback_del_hook_set(obj, _signal_callback_del_hook);
399    elm_widget_text_set_hook_set(obj, _elm_layout_label_set);
400    elm_widget_text_get_hook_set(obj, _elm_layout_label_get);
401
402    wd->obj = obj;
403    wd->lay = edje_object_add(e);
404    elm_widget_resize_object_set(obj, wd->lay);
405    edje_object_signal_callback_add(wd->lay, "size,eval", "elm",
406                                    _signal_size_eval, wd);
407
408    evas_object_smart_callback_add(obj, "sub-object-del", _sub_del, obj);
409    evas_object_smart_callbacks_descriptions_set(obj, _signals);
410
411    _mirrored_set(obj, elm_widget_mirrored_get(obj));
412    _request_sizing_eval(wd);
413    return obj;
414 }
415
416 /**
417  * Set the file that will be used as layout
418  *
419  * @param obj The layout object
420  * @param file The path to file (edj) that will be used as layout
421  * @param group The group that the layout belongs in edje file
422  *
423  * @return (1 = success, 0 = error)
424  *
425  * @ingroup Layout
426  */
427 EAPI Eina_Bool
428 elm_layout_file_set(Evas_Object *obj, const char *file, const char *group)
429 {
430    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
431    Widget_Data *wd = elm_widget_data_get(obj);
432    if (!wd) return EINA_FALSE;
433    Eina_Bool ret = edje_object_file_set(wd->lay, file, group);
434    if (ret)
435      {
436         _parts_text_fix(wd);
437         _request_sizing_eval(wd);
438         _parts_cursors_apply(wd);
439      }
440    else DBG("failed to set edje file '%s', group '%s': %s",
441             file, group,
442             edje_load_error_str(edje_object_load_error_get(wd->lay)));
443    return ret;
444 }
445
446 /**
447  * Set the edje group from the elementary theme that will be used as layout
448  *
449  * @param obj The layout object
450  * @param clas the clas of the group
451  * @param group the group
452  * @param style the style to used
453  *
454  * @return (1 = success, 0 = error)
455  *
456  * @ingroup Layout
457  */
458 EAPI Eina_Bool
459 elm_layout_theme_set(Evas_Object *obj, const char *clas, const char *group, const char *style)
460 {
461    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
462    Widget_Data *wd = elm_widget_data_get(obj);
463    if (!wd) return EINA_FALSE;
464    Eina_Bool ret = _elm_theme_object_set(obj, wd->lay, clas, group, style);
465    wd->clas = clas;
466    wd->group = group;
467    wd->style = style;
468    if (ret)
469      {
470         _parts_text_fix(wd);
471         _request_sizing_eval(wd);
472         _parts_cursors_apply(wd);
473      }
474    return ret;
475 }
476
477 /**
478  * Set the layout content
479  *
480  * Once the content object is set, a previously set one will be deleted.
481  * If you want to keep that old content object, use the
482  * elm_layout_content_unset() function.
483  *
484  * @param obj The layout object
485  * @param swallow The swallow group name in the edje file
486  * @param content The content will be filled in this layout object
487  *
488  * @ingroup Layout
489  */
490 EAPI void
491 elm_layout_content_set(Evas_Object *obj, const char *swallow, Evas_Object *content)
492 {
493    ELM_CHECK_WIDTYPE(obj, widtype);
494    Widget_Data *wd = elm_widget_data_get(obj);
495    Subinfo *si;
496    const Eina_List *l;
497    if (!wd) return;
498    EINA_LIST_FOREACH(wd->subs, l, si)
499      {
500         if ((si->type == SWALLOW) && (!strcmp(swallow, si->part)))
501           {
502              if (content == si->obj) return;
503              evas_object_del(si->obj);
504              break;
505           }
506      }
507    if (content)
508      {
509         elm_widget_sub_object_add(obj, content);
510         evas_object_event_callback_add(content,
511                                        EVAS_CALLBACK_CHANGED_SIZE_HINTS,
512                                        _changed_size_hints, wd);
513         if (!edje_object_part_swallow(wd->lay, swallow, content))
514           WRN("could not swallow %p into part '%s'", content, swallow);
515         si = ELM_NEW(Subinfo);
516         si->type = SWALLOW;
517         si->part = eina_stringshare_add(swallow);
518         si->obj = content;
519         wd->subs = eina_list_append(wd->subs, si);
520      }
521    _request_sizing_eval(wd);
522 }
523
524 /**
525  * Get the swallowed object in the given part
526  *
527  * @param obj The layout object
528  * @param swallow The SWALLOW part to get its content
529  *
530  * @return The swallowed object or NULL if none or an error occurred
531  *
532  * @ingroup Layout
533  */
534 EAPI Evas_Object *
535 elm_layout_content_get(const Evas_Object *obj, const char *swallow)
536 {
537    Widget_Data *wd = elm_widget_data_get(obj);
538    const Eina_List *l;
539    Subinfo *si;
540    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
541
542    EINA_LIST_FOREACH(wd->subs, l, si)
543      {
544         if ((si->type == SWALLOW) && !strcmp(swallow, si->part))
545           return si->obj;
546      }
547    return NULL;
548 }
549
550 /**
551  * Unset the layout content
552  *
553  * Unparent and return the content object which was set for this widget
554  *
555  * @param obj The layout object
556  * @param swallow The swallow group name in the edje file
557  * @return The content that was being used
558  *
559  * @ingroup Layout
560  */
561 EAPI Evas_Object *
562 elm_layout_content_unset(Evas_Object *obj, const char *swallow)
563 {
564    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
565    Widget_Data *wd = elm_widget_data_get(obj);
566    Subinfo *si;
567    const Eina_List *l;
568    if (!wd) return NULL;
569    EINA_LIST_FOREACH(wd->subs, l, si)
570      {
571         if ((si->type == SWALLOW) && (!strcmp(swallow, si->part)))
572           {
573              Evas_Object *content;
574              if (!si->obj) return NULL;
575              content = si->obj; /* si will die in _sub_del due elm_widget_sub_object_del() */
576              elm_widget_sub_object_del(obj, content);
577              edje_object_part_unswallow(wd->lay, content);
578              return content;
579           }
580      }
581    return NULL;
582 }
583
584 /**
585  * Set the text of the given part
586  *
587  * @param obj The layout object
588  * @param part The TEXT part where to set the text
589  * @param text The text to set
590  *
591  * @ingroup Layout
592  * @deprecate use elm_object_text_* instead.
593  */
594 EAPI void
595 elm_layout_text_set(Evas_Object *obj, const char *part, const char *text)
596 {
597    _elm_layout_label_set(obj, part, text);
598 }
599
600 /**
601  * Get the text set in the given part
602  *
603  * @param obj The layout object
604  * @param part The TEXT part to retrieve the text off
605  *
606  * @return The text set in @p part
607  *
608  * @ingroup Layout
609  * @deprecate use elm_object_text_* instead.
610  */
611 EAPI const char *
612 elm_layout_text_get(const Evas_Object *obj, const char *part)
613 {
614    return _elm_layout_label_get(obj, part);
615 }
616
617 /**
618  * Append child to layout box part.
619  *
620  * Once the object is appended, its lifetime will be bound to the
621  * layout, whenever the layout dies the child will be deleted
622  * automatically. One should use elm_layout_box_remove() to make this
623  * layout forget about the object.
624  *
625  * @param obj the layout object
626  * @param part the box part to append.
627  * @param child the child object to append to box.
628  *
629  * @ingroup Layout
630  */
631 EAPI void
632 elm_layout_box_append(Evas_Object *obj, const char *part, Evas_Object *child)
633 {
634    ELM_CHECK_WIDTYPE(obj, widtype);
635    Widget_Data *wd = elm_widget_data_get(obj);
636    Subinfo *si;
637    if (!wd) return;
638
639    if (!edje_object_part_box_append(wd->lay, part, child))
640      WRN("child %p could not be appended to box part '%s'", child, part);
641    elm_widget_sub_object_add(obj, child);
642    evas_object_event_callback_add
643       (child, EVAS_CALLBACK_CHANGED_SIZE_HINTS, _changed_size_hints, wd);
644
645    si = ELM_NEW(Subinfo);
646    si->type = BOX_APPEND;
647    si->part = eina_stringshare_add(part);
648    si->obj = child;
649    wd->subs = eina_list_append(wd->subs, si);
650    _request_sizing_eval(wd);
651 }
652
653 /**
654  * Prepend child to layout box part.
655  *
656  * Once the object is prepended, its lifetime will be bound to the
657  * layout, whenever the layout dies the child will be deleted
658  * automatically. One should use elm_layout_box_remove() to make this
659  * layout forget about the object.
660  *
661  * @param obj the layout object
662  * @param part the box part to prepend.
663  * @param child the child object to prepend to box.
664  *
665  * @ingroup Layout
666  */
667 EAPI void
668 elm_layout_box_prepend(Evas_Object *obj, const char *part, Evas_Object *child)
669 {
670    ELM_CHECK_WIDTYPE(obj, widtype);
671    Widget_Data *wd = elm_widget_data_get(obj);
672    Subinfo *si;
673    if (!wd) return;
674
675    if (!edje_object_part_box_prepend(wd->lay, part, child))
676      WRN("child %p could not be prepended to box part '%s'", child, part);
677    elm_widget_sub_object_add(obj, child);
678    evas_object_event_callback_add
679       (child, EVAS_CALLBACK_CHANGED_SIZE_HINTS, _changed_size_hints, wd);
680
681    si = ELM_NEW(Subinfo);
682    si->type = BOX_PREPEND;
683    si->part = eina_stringshare_add(part);
684    si->obj = child;
685    wd->subs = eina_list_prepend(wd->subs, si);
686    _request_sizing_eval(wd);
687 }
688
689 static void
690 _box_reference_del(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
691 {
692    Subinfo *si = data;
693    si->p.box.reference = NULL;
694 }
695
696 /**
697  * Insert child to layout box part before a reference object.
698  *
699  * Once the object is inserted, its lifetime will be bound to the
700  * layout, whenever the layout dies the child will be deleted
701  * automatically. One should use elm_layout_box_remove() to make this
702  * layout forget about the object.
703  *
704  * @param obj the layout object
705  * @param part the box part to insert.
706  * @param child the child object to insert into box.
707  * @param reference another reference object to insert before in box.
708  *
709  * @ingroup Layout
710  */
711 EAPI void
712 elm_layout_box_insert_before(Evas_Object *obj, const char *part, Evas_Object *child, const Evas_Object *reference)
713 {
714    ELM_CHECK_WIDTYPE(obj, widtype);
715    Widget_Data *wd = elm_widget_data_get(obj);
716    Subinfo *si;
717    if (!wd) return;
718
719    if (!edje_object_part_box_insert_before(wd->lay, part, child, reference))
720      WRN("child %p could not be inserted before %p inf box part '%s'",
721          child, reference, part);
722
723    si = ELM_NEW(Subinfo);
724    si->type = BOX_INSERT_BEFORE;
725    si->part = eina_stringshare_add(part);
726    si->obj = child;
727    si->p.box.reference = reference;
728
729    elm_widget_sub_object_add(obj, child);
730    evas_object_event_callback_add
731       (child, EVAS_CALLBACK_CHANGED_SIZE_HINTS, _changed_size_hints, wd);
732    evas_object_event_callback_add
733       ((Evas_Object *)reference, EVAS_CALLBACK_DEL, _box_reference_del, si);
734
735    wd->subs = eina_list_append(wd->subs, si);
736    _request_sizing_eval(wd);
737 }
738
739 /**
740  * Insert child to layout box part at a given position.
741  *
742  * Once the object is inserted, its lifetime will be bound to the
743  * layout, whenever the layout dies the child will be deleted
744  * automatically. One should use elm_layout_box_remove() to make this
745  * layout forget about the object.
746  *
747  * @param obj the layout object
748  * @param part the box part to insert.
749  * @param child the child object to insert into box.
750  * @param pos the numeric position >=0 to insert the child.
751  *
752  * @ingroup Layout
753  */
754 EAPI void
755 elm_layout_box_insert_at(Evas_Object *obj, const char *part, Evas_Object *child, unsigned int pos)
756 {
757    ELM_CHECK_WIDTYPE(obj, widtype);
758    Widget_Data *wd = elm_widget_data_get(obj);
759    Subinfo *si;
760    if (!wd) return;
761
762    if (!edje_object_part_box_insert_at(wd->lay, part, child, pos))
763      WRN("child %p could not be inserted at %u to box part '%s'",
764          child, pos, part);
765
766    elm_widget_sub_object_add(obj, child);
767    evas_object_event_callback_add
768       (child, EVAS_CALLBACK_CHANGED_SIZE_HINTS, _changed_size_hints, wd);
769
770    si = ELM_NEW(Subinfo);
771    si->type = BOX_INSERT_AT;
772    si->part = eina_stringshare_add(part);
773    si->obj = child;
774    si->p.box.pos = pos;
775    wd->subs = eina_list_append(wd->subs, si);
776    _request_sizing_eval(wd);
777 }
778
779 static Evas_Object *
780 _sub_box_remove(Widget_Data *wd, Subinfo *si)
781 {
782    Evas_Object *child;
783
784    if (si->type == BOX_INSERT_BEFORE)
785      evas_object_event_callback_del_full
786         ((Evas_Object *)si->p.box.reference,
787          EVAS_CALLBACK_DEL, _box_reference_del, si);
788
789    child = si->obj; /* si will die in _sub_del due elm_widget_sub_object_del() */
790    edje_object_part_box_remove(wd->lay, si->part, child);
791    elm_widget_sub_object_del(wd->obj, child);
792    return child;
793 }
794
795 static Evas_Object *
796 _sub_table_remove(Widget_Data *wd, Subinfo *si)
797 {
798    Evas_Object *child;
799
800    child = si->obj; /* si will die in _sub_del due elm_widget_sub_object_del() */
801    edje_object_part_table_unpack(wd->lay, si->part, child);
802    elm_widget_sub_object_del(wd->obj, child);
803    return child;
804 }
805
806 static Eina_Bool
807 _sub_box_is(const Subinfo *si)
808 {
809    switch (si->type)
810      {
811       case BOX_APPEND:
812       case BOX_PREPEND:
813       case BOX_INSERT_BEFORE:
814       case BOX_INSERT_AT:
815          return EINA_TRUE;
816       default:
817          return EINA_FALSE;
818      }
819 }
820
821 /**
822  * Remove a child of the given part box.
823  *
824  * The object will be removed from the box part and its lifetime will
825  * not be handled by the layout anymore. This is equivalent to
826  * elm_layout_content_unset() for box.
827  *
828  * @param obj The layout object
829  * @param part The box part name to remove child.
830  * @param child The object to remove from box.
831  * @return The object that was being used, or NULL if not found.
832  *
833  * @ingroup Layout
834  */
835 EAPI Evas_Object *
836 elm_layout_box_remove(Evas_Object *obj, const char *part, Evas_Object *child)
837 {
838    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
839    Widget_Data *wd = elm_widget_data_get(obj);
840    const Eina_List *l;
841    Subinfo *si;
842
843    if (!wd) return NULL;
844
845    EINA_SAFETY_ON_NULL_RETURN_VAL(part, NULL);
846    EINA_SAFETY_ON_NULL_RETURN_VAL(child, NULL);
847    EINA_LIST_FOREACH(wd->subs, l, si)
848      {
849         if (!_sub_box_is(si)) continue;
850         if ((si->obj == child) && (!strcmp(si->part, part)))
851           return _sub_box_remove(wd, si);
852      }
853    return NULL;
854 }
855
856 /**
857  * Remove all child of the given part box.
858  *
859  * The objects will be removed from the box part and their lifetime will
860  * not be handled by the layout anymore. This is equivalent to
861  * elm_layout_content_unset() for all box children.
862  *
863  * @param obj The layout object
864  * @param part The box part name to remove child.
865  * @param clear If EINA_TRUE, then all objects will be deleted as
866  *        well, otherwise they will just be removed and will be
867  *        dangling on the canvas.
868  *
869  * @ingroup Layout
870  */
871 EAPI void
872 elm_layout_box_remove_all(Evas_Object *obj, const char *part, Eina_Bool clear)
873 {
874    ELM_CHECK_WIDTYPE(obj, widtype);
875    Widget_Data *wd = elm_widget_data_get(obj);
876    Subinfo *si;
877    Eina_List *lst;
878
879    if (!wd) return;
880    EINA_SAFETY_ON_NULL_RETURN(part);
881
882    lst = eina_list_clone(wd->subs);
883    EINA_LIST_FREE(lst, si)
884      {
885         if (!_sub_box_is(si)) continue;
886         if (!strcmp(si->part, part))
887           {
888              Evas_Object *child = _sub_box_remove(wd, si);
889              if ((clear) && (child)) evas_object_del(child);
890           }
891      }
892    /* eventually something may not be added with layout, del them as well */
893    edje_object_part_box_remove_all(wd->lay, part, clear);
894 }
895
896 /**
897  * Insert child to layout table part.
898  *
899  * Once the object is inserted, its lifetime will be bound to the
900  * layout, whenever the layout dies the child will be deleted
901  * automatically. One should use elm_layout_box_remove() to make this
902  * layout forget about the object.
903  *
904  * @param obj the layout object
905  * @param part the box part to pack child.
906  * @param child the child object to pack into table.
907  * @param reference another reference object to insert before in box.
908  *
909  * @ingroup Layout
910  */
911 EAPI void
912 elm_layout_table_pack(Evas_Object *obj, const char *part, Evas_Object *child, unsigned short col, unsigned short row, unsigned short colspan, unsigned short rowspan)
913 {
914    ELM_CHECK_WIDTYPE(obj, widtype);
915    Widget_Data *wd = elm_widget_data_get(obj);
916    Subinfo *si;
917    if (!wd) return;
918
919    if (!edje_object_part_table_pack
920        (wd->lay, part, child, col, row, colspan, rowspan))
921      WRN("child %p could not be packed into box part '%s' col=%uh, row=%hu, "
922          "colspan=%hu, rowspan=%hu", child, part, col, row, colspan, rowspan);
923
924    elm_widget_sub_object_add(obj, child);
925    evas_object_event_callback_add
926       (child, EVAS_CALLBACK_CHANGED_SIZE_HINTS, _changed_size_hints, wd);
927
928    si = ELM_NEW(Subinfo);
929    si->type = TABLE_PACK;
930    si->part = eina_stringshare_add(part);
931    si->obj = child;
932    si->p.table.col = col;
933    si->p.table.row = row;
934    si->p.table.colspan = colspan;
935    si->p.table.rowspan = rowspan;
936    wd->subs = eina_list_append(wd->subs, si);
937    _request_sizing_eval(wd);
938 }
939
940 /**
941  * Unpack (remove) a child of the given part table.
942  *
943  * The object will be unpacked from the table part and its lifetime
944  * will not be handled by the layout anymore. This is equivalent to
945  * elm_layout_content_unset() for table.
946  *
947  * @param obj The layout object
948  * @param part The table part name to remove child.
949  * @param child The object to remove from table.
950  * @return The object that was being used, or NULL if not found.
951  *
952  * @ingroup Layout
953  */
954 EAPI Evas_Object *
955 elm_layout_table_unpack(Evas_Object *obj, const char *part, Evas_Object *child)
956 {
957    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
958    Widget_Data *wd = elm_widget_data_get(obj);
959    const Eina_List *l;
960    Subinfo *si;
961
962    if (!wd) return NULL;
963
964    EINA_SAFETY_ON_NULL_RETURN_VAL(part, NULL);
965    EINA_SAFETY_ON_NULL_RETURN_VAL(child, NULL);
966    EINA_LIST_FOREACH(wd->subs, l, si)
967      {
968         if (si->type != TABLE_PACK) continue;
969         if ((si->obj == child) && (!strcmp(si->part, part)))
970           return _sub_table_remove(wd, si);
971      }
972    return NULL;
973 }
974
975 /**
976  * Remove all child of the given part table.
977  *
978  * The objects will be removed from the table part and their lifetime will
979  * not be handled by the layout anymore. This is equivalent to
980  * elm_layout_content_unset() for all table children.
981  *
982  * @param obj The layout object
983  * @param part The table part name to remove child.
984  * @param clear If EINA_TRUE, then all objects will be deleted as
985  *        well, otherwise they will just be removed and will be
986  *        dangling on the canvas.
987  *
988  * @ingroup Layout
989  */
990 EAPI void
991 elm_layout_table_clear(Evas_Object *obj, const char *part, Eina_Bool clear)
992 {
993    ELM_CHECK_WIDTYPE(obj, widtype);
994    Widget_Data *wd = elm_widget_data_get(obj);
995    Subinfo *si;
996    Eina_List *lst;
997
998    if (!wd) return;
999    EINA_SAFETY_ON_NULL_RETURN(part);
1000
1001    lst = eina_list_clone(wd->subs);
1002    EINA_LIST_FREE(lst, si)
1003      {
1004         if (si->type != TABLE_PACK) continue;
1005         if (!strcmp(si->part, part))
1006           {
1007              Evas_Object *child = _sub_table_remove(wd, si);
1008              if ((clear) && (child)) evas_object_del(child);
1009           }
1010      }
1011    /* eventually something may not be added with layout, del them as well */
1012    edje_object_part_table_clear(wd->lay, part, clear);
1013 }
1014
1015 /**
1016  * Get the edje layout
1017  *
1018  * @param obj The layout object
1019  *
1020  * This returns the edje object. It is not expected to be used to then swallow
1021  * objects via edje_object_part_swallow() for example. Use
1022  * elm_layout_content_set() instead so child object handling and sizing is
1023  * done properly. This is more intended for setting text, emitting signals,
1024  * hooking to signal callbacks etc.
1025  *
1026  * @return A Evas_Object with the edje layout settings loaded
1027  * with function elm_layout_file_set
1028  *
1029  * @ingroup Layout
1030  */
1031 EAPI Evas_Object *
1032 elm_layout_edje_get(const Evas_Object *obj)
1033 {
1034    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
1035    Widget_Data *wd = elm_widget_data_get(obj);
1036    if (!wd) return NULL;
1037    return wd->lay;
1038 }
1039
1040 /**
1041  * Get the edje data of the given layout
1042  *
1043  * @param obj The layout object
1044  * @param key The data key
1045  *
1046  * @return The edje data string
1047  *
1048  * This function fetches data specified at the object level.
1049  * This function return NULL if data is not found.
1050  *
1051  * In EDC this comes from a data block within the group block that @a
1052  * obj was loaded from. E.g.
1053  *
1054  * @code
1055  * collections {
1056  *   group {
1057  *     name: "a_group";
1058  *     data {
1059  *       item: "key1" "value1";
1060  *       item: "key2" "value2";
1061  *     }
1062  *   }
1063  * }
1064  * @endcode
1065  *
1066  * @ingroup Layout
1067  */
1068 EAPI const char *
1069 elm_layout_data_get(const Evas_Object *obj, const char *key)
1070 {
1071    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
1072    Widget_Data *wd = elm_widget_data_get(obj);
1073    return edje_object_data_get(wd->lay, key);
1074 }
1075
1076 /**
1077  * Eval sizing
1078  *
1079  * Manually forms a sizing re-evaluation when contents changed state so that
1080  * minimum size might have changed and needs re-evaluation. Also note that
1081  * a standard signal of "size,eval" "elm" emitted by the edje object will
1082  * cause this to happen too
1083  *
1084  * @param obj The layout object
1085  *
1086  * @ingroup Layout
1087  */
1088 EAPI void
1089 elm_layout_sizing_eval(Evas_Object *obj)
1090 {
1091    ELM_CHECK_WIDTYPE(obj, widtype);
1092    Widget_Data *wd = elm_widget_data_get(obj);
1093    EINA_SAFETY_ON_NULL_RETURN(wd);
1094    _request_sizing_eval(wd);
1095 }
1096
1097 /**
1098  * Sets a specific cursor for an edje part.
1099  *
1100  * @param obj The layout object.
1101  * @param part_name a part from loaded edje group.
1102  * @param cursor cursor name to use, see Elementary_Cursor.h
1103  *
1104  * @return EINA_TRUE on success or EINA_FALSE on failure, that may be
1105  *         part not exists or it has "mouse_events: 0".
1106  *
1107  * @ingroup Layout
1108  */
1109 EAPI Eina_Bool
1110 elm_layout_part_cursor_set(Evas_Object *obj, const char *part_name, const char *cursor)
1111 {
1112    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
1113    EINA_SAFETY_ON_NULL_RETURN_VAL(part_name, EINA_FALSE);
1114    Widget_Data *wd = elm_widget_data_get(obj);
1115    EINA_SAFETY_ON_NULL_RETURN_VAL(wd, EINA_FALSE);
1116    Evas_Object *part_obj;
1117    Part_Cursor *pc;
1118
1119    part_obj = (Evas_Object *)edje_object_part_object_get(wd->lay, part_name);
1120    if (!part_obj)
1121      {
1122         const char *group, *file;
1123         edje_object_file_get(wd->lay, &file, &group);
1124         WRN("no part '%s' in group '%s' of file '%s'. Cannot set cursor '%s'",
1125             part_name, group, file, cursor);
1126         return EINA_FALSE;
1127      }
1128    if (evas_object_pass_events_get(part_obj))
1129      {
1130         const char *group, *file;
1131         edje_object_file_get(wd->lay, &file, &group);
1132         WRN("part '%s' in group '%s' of file '%s' has mouse_events: 0. "
1133             "Cannot set cursor '%s'",
1134             part_name, group, file, cursor);
1135         return EINA_FALSE;
1136      }
1137
1138    pc = _parts_cursors_find(wd, part_name);
1139    if (pc) eina_stringshare_replace(&pc->cursor, cursor);
1140    else
1141      {
1142         pc = calloc(1, sizeof(*pc));
1143         pc->part = eina_stringshare_add(part_name);
1144         pc->cursor = eina_stringshare_add(cursor);
1145      }
1146
1147    pc->obj = part_obj;
1148    elm_object_sub_cursor_set(part_obj, obj, pc->cursor);
1149    return EINA_TRUE;
1150 }
1151
1152 /**
1153  * Get the cursor to be shown when mouse is over an edje part
1154  *
1155  * @param obj The layout object.
1156  * @param part_name a part from loaded edje group.
1157  * @return the cursor name.
1158  *
1159  * @ingroup Layout
1160  */
1161 EAPI const char *
1162 elm_layout_part_cursor_get(const Evas_Object *obj, const char *part_name)
1163 {
1164    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
1165    EINA_SAFETY_ON_NULL_RETURN_VAL(part_name, NULL);
1166    Widget_Data *wd = elm_widget_data_get(obj);
1167    EINA_SAFETY_ON_NULL_RETURN_VAL(wd, NULL);
1168    Part_Cursor *pc = _parts_cursors_find(wd, part_name);
1169    EINA_SAFETY_ON_NULL_RETURN_VAL(pc, NULL);
1170    EINA_SAFETY_ON_NULL_RETURN_VAL(pc->obj, NULL);
1171    return elm_object_cursor_get(pc->obj);
1172 }
1173
1174 /**
1175  * Unsets a cursor previously set with elm_layout_part_cursor_set().
1176  *
1177  * @param obj The layout object.
1178  * @param part_name a part from loaded edje group, that had a cursor set
1179  *        with elm_layout_part_cursor_set().
1180  *
1181  * @ingroup Layout
1182  */
1183 EAPI void
1184 elm_layout_part_cursor_unset(Evas_Object *obj, const char *part_name)
1185 {
1186    ELM_CHECK_WIDTYPE(obj, widtype);
1187    EINA_SAFETY_ON_NULL_RETURN(part_name);
1188    Widget_Data *wd = elm_widget_data_get(obj);
1189    EINA_SAFETY_ON_NULL_RETURN(wd);
1190    Eina_List *l;
1191    Part_Cursor *pc;
1192
1193    EINA_LIST_FOREACH(wd->parts_cursors, l, pc)
1194      {
1195         if (!strcmp(part_name, pc->part))
1196           {
1197              if (pc->obj) elm_object_cursor_unset(pc->obj);
1198              _part_cursor_free(pc);
1199              wd->parts_cursors = eina_list_remove_list(wd->parts_cursors, l);
1200              return;
1201           }
1202      }
1203 }
1204
1205 /**
1206  * Sets a specific cursor style for an edje part.
1207  *
1208  * @param obj The layout object.
1209  * @param part_name a part from loaded edje group.
1210  * @param style the theme style to use (default, transparent, ...)
1211  *
1212  * @return EINA_TRUE on success or EINA_FALSE on failure, that may be
1213  *         part not exists or it did not had a cursor set.
1214  *
1215  * @ingroup Layout
1216  */
1217 EAPI Eina_Bool
1218 elm_layout_part_cursor_style_set(Evas_Object *obj, const char *part_name, const char *style)
1219 {
1220    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
1221    EINA_SAFETY_ON_NULL_RETURN_VAL(part_name, EINA_FALSE);
1222    Widget_Data *wd = elm_widget_data_get(obj);
1223    EINA_SAFETY_ON_NULL_RETURN_VAL(wd, EINA_FALSE);
1224    Part_Cursor *pc = _parts_cursors_find(wd, part_name);
1225    EINA_SAFETY_ON_NULL_RETURN_VAL(pc, EINA_FALSE);
1226    EINA_SAFETY_ON_NULL_RETURN_VAL(pc->obj, EINA_FALSE);
1227
1228    eina_stringshare_replace(&pc->style, style);
1229    elm_object_cursor_style_set(pc->obj, pc->style);
1230    return EINA_TRUE;
1231 }
1232
1233 /**
1234  * Gets a specific cursor style for an edje part.
1235  *
1236  * @param obj The layout object.
1237  * @param part_name a part from loaded edje group.
1238  *
1239  * @return the theme style in use, defaults to "default". If the
1240  *         object does not have a cursor set, then NULL is returned.
1241  *
1242  * @ingroup Layout
1243  */
1244 EAPI const char *
1245 elm_layout_part_cursor_style_get(const Evas_Object *obj, const char *part_name)
1246 {
1247    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
1248    EINA_SAFETY_ON_NULL_RETURN_VAL(part_name, NULL);
1249    Widget_Data *wd = elm_widget_data_get(obj);
1250    EINA_SAFETY_ON_NULL_RETURN_VAL(wd, NULL);
1251    Part_Cursor *pc = _parts_cursors_find(wd, part_name);
1252    EINA_SAFETY_ON_NULL_RETURN_VAL(pc, NULL);
1253    EINA_SAFETY_ON_NULL_RETURN_VAL(pc->obj, NULL);
1254    return elm_object_cursor_style_get(pc->obj);
1255 }
1256
1257 /**
1258  * Sets if the cursor set should be searched on the theme or should use
1259  * the provided by the engine, only.
1260  *
1261  * @note before you set if should look on theme you should define a
1262  * cursor with elm_layout_part_cursor_set(). By default it will only
1263  * look for cursors provided by the engine.
1264  *
1265  * @param obj The layout object.
1266  * @param part_name a part from loaded edje group.
1267  * @param engine_only if cursors should be just provided by the engine
1268  *        or should also search on widget's theme as well
1269  *
1270  * @return EINA_TRUE on success or EINA_FALSE on failure, that may be
1271  *         part not exists or it did not had a cursor set.
1272  *
1273  * @ingroup Layout
1274  */
1275 EAPI Eina_Bool
1276 elm_layout_part_cursor_engine_only_set(Evas_Object *obj, const char *part_name, Eina_Bool engine_only)
1277 {
1278    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
1279    EINA_SAFETY_ON_NULL_RETURN_VAL(part_name, EINA_FALSE);
1280    Widget_Data *wd = elm_widget_data_get(obj);
1281    EINA_SAFETY_ON_NULL_RETURN_VAL(wd, EINA_FALSE);
1282    Part_Cursor *pc = _parts_cursors_find(wd, part_name);
1283    EINA_SAFETY_ON_NULL_RETURN_VAL(pc, EINA_FALSE);
1284    EINA_SAFETY_ON_NULL_RETURN_VAL(pc->obj, EINA_FALSE);
1285
1286    pc->engine_only = !!engine_only;
1287    elm_object_cursor_engine_only_set(pc->obj, pc->engine_only);
1288    return EINA_TRUE;
1289 }
1290
1291 /**
1292  * Gets a specific cursor engine_only for an edje part.
1293  *
1294  * @param obj The layout object.
1295  * @param part_name a part from loaded edje group.
1296  *
1297  * @return whenever the cursor is just provided by engine or also from theme.
1298  *
1299  * @ingroup Layout
1300  */
1301 EAPI Eina_Bool
1302 elm_layout_part_cursor_engine_only_get(const Evas_Object *obj, const char *part_name)
1303 {
1304    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
1305    EINA_SAFETY_ON_NULL_RETURN_VAL(part_name, EINA_FALSE);
1306    Widget_Data *wd = elm_widget_data_get(obj);
1307    EINA_SAFETY_ON_NULL_RETURN_VAL(wd, EINA_FALSE);
1308    Part_Cursor *pc = _parts_cursors_find(wd, part_name);
1309    EINA_SAFETY_ON_NULL_RETURN_VAL(pc, EINA_FALSE);
1310    EINA_SAFETY_ON_NULL_RETURN_VAL(pc->obj, EINA_FALSE);
1311    return elm_object_cursor_engine_only_get(pc->obj);
1312 }