svn update: 51469 (latest:51480)
[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
16 struct _Widget_Data
17 {
18    Evas_Object *lay;
19    Eina_List *subs;
20    Eina_Bool needs_size_calc:1;
21 };
22
23 struct _Subinfo
24 {
25    const char *swallow;
26    Evas_Object *obj;
27 };
28
29 static const char *widtype = NULL;
30 static void _del_hook(Evas_Object *obj);
31 static void _theme_hook(Evas_Object *obj);
32 static void _sizing_eval(Evas_Object *obj);
33 static void _changed_size_hints(void *data, Evas *e, Evas_Object *obj, void *event_info);
34 static void _sub_del(void *data, Evas_Object *obj, void *event_info);
35
36 static void
37 _del_hook(Evas_Object *obj)
38 {
39    Widget_Data *wd = elm_widget_data_get(obj);
40    Subinfo *si;
41    if (!wd) return;
42    EINA_LIST_FREE(wd->subs, si)
43      {
44         eina_stringshare_del(si->swallow);
45         free(si);
46      }
47    free(wd);
48 }
49
50 static void
51 _theme_hook(Evas_Object *obj)
52 {
53    Widget_Data *wd = elm_widget_data_get(obj);
54    if (!wd) return;
55    edje_object_scale_set(wd->lay, elm_widget_scale_get(obj) *
56                          _elm_config->scale);
57    _sizing_eval(obj);
58 }
59
60 static void
61 _changed_hook(Evas_Object *obj)
62 {
63    Widget_Data *wd = elm_widget_data_get(obj);
64    if (!wd) return;
65    if (wd->needs_size_calc)
66      {
67         _sizing_eval(obj);
68         wd->needs_size_calc = 0;
69      }
70 }
71
72 static void
73 _sizing_eval(Evas_Object *obj)
74 {
75    Widget_Data *wd = elm_widget_data_get(obj);
76    Evas_Coord minw = -1, minh = -1;
77    if (!wd) return;
78    edje_object_size_min_calc(wd->lay, &minw, &minh);
79    evas_object_size_hint_min_set(obj, minw, minh);
80    evas_object_size_hint_max_set(obj, -1, -1);
81 }
82
83 static void
84 _request_sizing_eval(Evas_Object *obj)
85 {
86    Widget_Data *wd = elm_widget_data_get(obj);
87    if (!wd) return;
88    if (wd->needs_size_calc) return;
89    wd->needs_size_calc = 1;
90    evas_object_smart_changed(obj);
91 }
92
93 static void
94 _changed_size_hints(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
95 {
96    _request_sizing_eval(data);
97 }
98
99 static void
100 _sub_del(void *data __UNUSED__, Evas_Object *obj, void *event_info)
101 {
102    Widget_Data *wd = elm_widget_data_get(obj);
103    Evas_Object *sub = event_info;
104    Eina_List *l;
105    Subinfo *si;
106    if (!wd) return;
107    EINA_LIST_FOREACH(wd->subs, l, si)
108      {
109         if (si->obj == sub)
110           {
111              evas_object_event_callback_del_full(sub,
112                                             EVAS_CALLBACK_CHANGED_SIZE_HINTS,
113                                             _changed_size_hints,
114                                             obj);
115              wd->subs = eina_list_remove_list(wd->subs, l);
116              eina_stringshare_del(si->swallow);
117              free(si);
118              break;
119           }
120      }
121 }
122
123 static void
124 _signal_size_eval(void *data, Evas_Object *obj __UNUSED__, const char *emission __UNUSED__, const char *source __UNUSED__)
125 {
126    _request_sizing_eval(data);
127 }
128
129 /**
130  * Add a new layout to the parent
131  *
132  * @param parent The parent object
133  * @return The new object or NULL if it cannot be created
134  *
135  * @ingroup Layout
136  */
137 EAPI Evas_Object *
138 elm_layout_add(Evas_Object *parent)
139 {
140    Evas_Object *obj;
141    Evas *e;
142    Widget_Data *wd;
143
144    wd = ELM_NEW(Widget_Data);
145    e = evas_object_evas_get(parent);
146    obj = elm_widget_add(e);
147    ELM_SET_WIDTYPE(widtype, "layout");
148    elm_widget_type_set(obj, "layout");
149    elm_widget_sub_object_add(parent, obj);
150    elm_widget_data_set(obj, wd);
151    elm_widget_del_hook_set(obj, _del_hook);
152    elm_widget_theme_hook_set(obj, _theme_hook);
153    elm_widget_changed_hook_set(obj, _changed_hook);
154
155    wd->lay = edje_object_add(e);
156    elm_widget_resize_object_set(obj, wd->lay);
157    edje_object_signal_callback_add(wd->lay, "size,eval", "elm",
158                                    _signal_size_eval, obj);
159    
160    evas_object_smart_callback_add(obj, "sub-object-del", _sub_del, obj);
161
162    _request_sizing_eval(obj);
163    return obj;
164 }
165
166 /**
167  * Set the file that will be used as layout
168  *
169  * @param obj The layout object
170  * @param file The path to file (edj) that will be used as layout
171  * @param group The group that the layout belongs in edje file
172  *
173  * @return (1 = sucess, 0 = error)
174  *
175  * @ingroup Layout
176  */
177 EAPI Eina_Bool
178 elm_layout_file_set(Evas_Object *obj, const char *file, const char *group)
179 {
180    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
181    Widget_Data *wd = elm_widget_data_get(obj);
182    if (!wd) return EINA_FALSE;
183    Eina_Bool ret = edje_object_file_set(wd->lay, file, group);
184    if (ret) _request_sizing_eval(obj);
185    return ret;
186 }
187
188 /**
189  * Set the edje group from the elementary theme that will be used as layout
190  *
191  * @param obj The layout object
192  * @param clas the clas of the group
193  * @param group the group
194  * @param style the style to used
195  *
196  * @return (1 = sucess, 0 = error)
197  *
198  * @ingroup Layout
199  */
200 EAPI Eina_Bool
201 elm_layout_theme_set(Evas_Object *obj, const char *clas, const char *group, const char *style)
202 {
203    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
204    Widget_Data *wd = elm_widget_data_get(obj);
205    if (!wd) return EINA_FALSE;
206    Eina_Bool ret = _elm_theme_object_set(obj, wd->lay, clas, group, style);
207    if (ret) _request_sizing_eval(obj);
208    return ret;
209 }
210
211 /**
212  * Set the layout content
213  *
214  * Once the content object is set, a previously set one will be deleted.
215  * If you want to keep that old content object, use the
216  * elm_layout_content_unset() function.
217  *
218  * @param obj The layout object
219  * @param swallow The swallow group name in the edje file
220  * @param content The content will be filled in this layout object
221  *
222  * @ingroup Layout
223  */
224 EAPI void
225 elm_layout_content_set(Evas_Object *obj, const char *swallow, Evas_Object *content)
226 {
227    ELM_CHECK_WIDTYPE(obj, widtype);
228    Widget_Data *wd = elm_widget_data_get(obj);
229    Subinfo *si;
230    const Eina_List *l;
231    if (!wd) return;
232    EINA_LIST_FOREACH(wd->subs, l, si)
233      {
234         if (!strcmp(swallow, si->swallow))
235           {
236              if (content == si->obj) return;
237              evas_object_del(si->obj);
238              break;
239           }
240      }
241    if (content)
242      {
243         elm_widget_sub_object_add(obj, content);
244         evas_object_event_callback_add(content,
245                                        EVAS_CALLBACK_CHANGED_SIZE_HINTS,
246                                        _changed_size_hints, obj);
247         edje_object_part_swallow(wd->lay, swallow, content);
248         si = ELM_NEW(Subinfo);
249         si->swallow = eina_stringshare_add(swallow);
250         si->obj = content;
251         wd->subs = eina_list_append(wd->subs, si);
252      }
253    _request_sizing_eval(obj);
254 }
255
256 /**
257  * Unset the layout content
258  *
259  * Unparent and return the content object which was set for this widget
260  *
261  * @param obj The layout object
262  * @param swallow The swallow group name in the edje file
263  * @return The content that was being used
264  *
265  * @ingroup Layout
266  */
267 EAPI Evas_Object *
268 elm_layout_content_unset(Evas_Object *obj, const char *swallow)
269 {
270    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
271    Widget_Data *wd = elm_widget_data_get(obj);
272    Subinfo *si;
273    const Eina_List *l;
274    if (!wd) return NULL;
275    EINA_LIST_FOREACH(wd->subs, l, si)
276      {
277         if (!strcmp(swallow, si->swallow))
278           {
279              Evas_Object *content;
280              if (!si->obj) return NULL;
281              content = si->obj; /* si will die in _sub_del due elm_widget_sub_object_del() */
282              elm_widget_sub_object_del(obj, content);
283              edje_object_part_unswallow(wd->lay, content);
284              return content;
285           }
286      }
287    return NULL;
288 }
289
290 /**
291  * Get the edje layout
292  *
293  * @param obj The layout object
294  * 
295  * This returns the edje object. It is not expected to be used to then swallow
296  * objects via edje_object_part_swallow() for example. Use 
297  * elm_layout_content_set() instead so child object handling and sizing is
298  * done properly. This is more intended for setting text, emitting signals,
299  * hooking to singal callbacks etc.
300  *
301  * @return A Evas_Object with the edje layout settings loaded
302  * with function elm_layout_file_set
303  *
304  * @ingroup Layout
305  */
306 EAPI Evas_Object *
307 elm_layout_edje_get(const Evas_Object *obj)
308 {
309    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
310    Widget_Data *wd = elm_widget_data_get(obj);
311    if (!wd) return NULL;
312    return wd->lay;
313 }
314
315 /**
316  * Get the edje layout
317  * 
318  * Manually forms a sizing re-evaluation when contents changed state so that
319  * minimum size might have changed and needs re-evaluation. Also note that
320  * a standard signal of "size,eval" "elm" emitted by the edje object will
321  * cause this to happen too
322  *
323  * @param obj The layout object
324  *
325  * @ingroup Layout
326  */
327 EAPI void
328 elm_layout_sizing_eval(Evas_Object *obj)
329 {
330    ELM_CHECK_WIDTYPE(obj, widtype);
331    _request_sizing_eval(obj);
332 }