svn update: 48945 (latest:48959)
[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  * @param obj The layout object
215  * @param swallow The swallow group name in the edje file
216  * @param content The content will be filled in this layout object
217  *
218  * @ingroup Layout
219  */
220 EAPI void
221 elm_layout_content_set(Evas_Object *obj, const char *swallow, Evas_Object *content)
222 {
223    ELM_CHECK_WIDTYPE(obj, widtype);
224    Widget_Data *wd = elm_widget_data_get(obj);
225    Subinfo *si;
226    const Eina_List *l;
227    if (!wd) return;
228    EINA_LIST_FOREACH(wd->subs, l, si)
229      {
230         if (!strcmp(swallow, si->swallow))
231           {
232              if (content == si->obj) return;
233              elm_widget_sub_object_del(obj, si->obj);
234              break;
235           }
236      }
237    if (content)
238      {
239         elm_widget_sub_object_add(obj, content);
240         evas_object_event_callback_add(content,
241                                        EVAS_CALLBACK_CHANGED_SIZE_HINTS,
242                                        _changed_size_hints, obj);
243         edje_object_part_swallow(wd->lay, swallow, content);
244         si = ELM_NEW(Subinfo);
245         si->swallow = eina_stringshare_add(swallow);
246         si->obj = content;
247         wd->subs = eina_list_append(wd->subs, si);
248         _request_sizing_eval(obj);
249      }
250 }
251
252 /**
253  * Get the edje layout
254  *
255  * @param obj The layout object
256  * 
257  * This returns the edje object. It is not expected to be used to then swallow
258  * objects via edje_object_part_swallow() for example. Use 
259  * elm_layout_content_set() instead so child object handling and sizing is
260  * done properly. This is more intended for setting text, emitting signals,
261  * hooking to singal callbacks etc.
262  *
263  * @return A Evas_Object with the edje layout settings loaded
264  * with function elm_layout_file_set
265  *
266  * @ingroup Layout
267  */
268 EAPI Evas_Object *
269 elm_layout_edje_get(const Evas_Object *obj)
270 {
271    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
272    Widget_Data *wd = elm_widget_data_get(obj);
273    if (!wd) return NULL;
274    return wd->lay;
275 }
276
277 /**
278  * Get the edje layout
279  * 
280  * Manually forms a sizing re-evaluation when contents changed state so that
281  * minimum size might have changed and needs re-evaluation. Also note that
282  * a standard signal of "size,eval" "elm" emitted by the edje object will
283  * cause this to happen too
284  *
285  * @param obj The layout object
286  *
287  * @ingroup Layout
288  */
289 EAPI void
290 elm_layout_sizing_eval(Evas_Object *obj)
291 {
292    ELM_CHECK_WIDTYPE(obj, widtype);
293    _request_sizing_eval(obj);
294 }