elementary/panes - Patch to handle deletion of current contents while setting new...
[framework/uifw/elementary.git] / src / lib / elm_panes.c
1 #include <Elementary.h>
2 #include "elm_priv.h"
3
4 /**
5  * TODO
6  * Update the minimun height of the bar in the theme.
7  * No minimun should be set in the vertical theme
8  * Add events (move, start ...)
9  */
10
11 typedef struct _Widget_Data Widget_Data;
12
13 struct _Widget_Data
14 {
15    Evas_Object *panes;
16
17    struct
18      {
19         Evas_Object *left;
20         Evas_Object *right;
21      } contents;
22
23    struct
24      {
25         int x_diff;
26         int y_diff;
27         Eina_Bool move;
28      } move;
29
30    Eina_Bool clicked_double;
31    Eina_Bool horizontal;
32    Eina_Bool fixed;
33 };
34
35 static const char *widtype = NULL;
36 static void _del_hook(Evas_Object *obj);
37 static void _mirrored_set(Evas_Object *obj, Eina_Bool rtl);
38 static void _theme_hook(Evas_Object *obj);
39 static void _sizing_eval(Evas_Object *obj);
40 static void _changed_size_hints(void *data, Evas *e, Evas_Object *obj, void *event_info);
41
42 static const char SIG_CLICKED[] = "clicked";
43 static const char SIG_PRESS[] = "press";
44 static const char SIG_UNPRESS[] = "unpress";
45 static const char SIG_CLICKED_DOUBLE[] = "clicked,double";
46
47 static const Evas_Smart_Cb_Description _signals[] = {
48    {SIG_CLICKED, ""},
49    {SIG_PRESS, ""},
50    {SIG_UNPRESS, ""},
51    {SIG_CLICKED_DOUBLE, ""},
52    {NULL, NULL}
53 };
54
55 static void
56 _del_hook(Evas_Object *obj)
57 {
58    Widget_Data *wd = elm_widget_data_get(obj);
59    if (!wd) return;
60    free(wd);
61 }
62
63 static void
64 _mirrored_set(Evas_Object *obj, Eina_Bool rtl)
65 {
66    Widget_Data *wd = elm_widget_data_get(obj);
67    if (!wd) return;
68    edje_object_mirrored_set(wd->panes, rtl);
69 }
70
71 static void
72 _theme_hook(Evas_Object *obj)
73 {
74    Widget_Data *wd = elm_widget_data_get(obj);
75    const char *style = elm_widget_style_get(obj);
76    double size;
77
78    if (!wd) return;
79    _elm_widget_mirrored_reload(obj);
80    _mirrored_set(obj, elm_widget_mirrored_get(obj));
81    size = elm_panes_content_left_size_get(obj);
82
83    if (wd->horizontal)
84      _elm_theme_object_set(obj, wd->panes, "panes", "horizontal", style);
85    else
86      _elm_theme_object_set(obj, wd->panes, "panes", "vertical", style);
87
88    if (wd->contents.left)
89      edje_object_part_swallow(wd->panes, "elm.swallow.left", wd->contents.left);
90    if (wd->contents.right)
91      edje_object_part_swallow(wd->panes, "elm.swallow.right", wd->contents.right);
92    if (wd->fixed)
93      edje_object_signal_emit(wd->panes, "elm.panes.fixed", "elm");
94
95    edje_object_scale_set(wd->panes, elm_widget_scale_get(obj) *
96                          _elm_config->scale);
97    _sizing_eval(obj);
98    elm_panes_content_left_size_set(obj, size);
99 }
100
101 static Eina_Bool
102 _elm_panes_focus_next_hook(const Evas_Object *obj, Elm_Focus_Direction dir, Evas_Object **next)
103 {
104    Widget_Data *wd = elm_widget_data_get(obj);
105    if (!wd) return EINA_FALSE;
106
107    double w, h;
108    edje_object_part_drag_value_get(wd->panes, "elm.bar", &w, &h);
109    if (((wd->horizontal) && ( h == 0.0 )) || ((!wd->horizontal) && ( w == 0.0 )))
110      return elm_widget_focus_next_get(wd->contents.right, dir, next);
111
112    Evas_Object *chain[2];
113
114    /* Direction */
115    if (dir == ELM_FOCUS_PREVIOUS)
116      {
117         chain[0] = wd->contents.right;
118         chain[1] = wd->contents.left;
119      }
120    else if (dir == ELM_FOCUS_NEXT)
121      {
122         chain[0] = wd->contents.left;
123         chain[1] = wd->contents.right;
124      }
125    else
126      return EINA_FALSE;
127
128    unsigned char i = elm_widget_focus_get(chain[1]);
129
130    if (elm_widget_focus_next_get(chain[i], dir, next))
131      return EINA_TRUE;
132
133    i = !i;
134
135    Evas_Object *to_focus;
136    if (elm_widget_focus_next_get(chain[i], dir, &to_focus))
137      {
138         *next = to_focus;
139         return !!i;
140      }
141
142    return EINA_FALSE;
143 }
144
145 static void
146 _sizing_eval(Evas_Object *obj)
147 {
148    Widget_Data *wd = elm_widget_data_get(obj);
149    if (!wd) return;
150 }
151
152 static void
153 _changed_size_hints(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
154 {
155    _sizing_eval(data);
156 }
157
158 static void
159 _sub_del(void *data __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
160 {
161    Widget_Data *wd = elm_widget_data_get(obj);
162    Evas_Object *sub = event_info;
163
164    if (!wd) return;
165    if (sub == wd->contents.left)
166      {
167         evas_object_event_callback_del_full(sub, EVAS_CALLBACK_CHANGED_SIZE_HINTS,
168                                             _changed_size_hints, obj);
169         edje_object_part_unswallow(wd->panes, sub);
170         wd->contents.left = NULL;
171         _sizing_eval(obj);
172      }
173    else if (sub == wd->contents.right)
174      {
175         evas_object_event_callback_del_full(sub, EVAS_CALLBACK_CHANGED_SIZE_HINTS,
176                                             _changed_size_hints, obj);
177         edje_object_part_unswallow(wd->panes, sub);
178         wd->contents.right= NULL;
179         _sizing_eval(obj);
180      }
181 }
182
183 static void
184 _clicked(void *data, Evas_Object *obj __UNUSED__ , const char *emission __UNUSED__, const char *source __UNUSED__)
185 {
186    evas_object_smart_callback_call(data, SIG_CLICKED, NULL);
187 }
188
189 static void
190 _clicked_double(void *data, Evas_Object *obj __UNUSED__ , const char *emission __UNUSED__, const char *source __UNUSED__)
191 {
192    Widget_Data *wd = elm_widget_data_get(data);
193
194    wd->clicked_double = EINA_TRUE;
195 }
196
197 static void
198 _press(void *data, Evas_Object *obj __UNUSED__ , const char *emission __UNUSED__, const char *source __UNUSED__)
199 {
200    evas_object_smart_callback_call(data, SIG_PRESS, NULL);
201 }
202
203 static void
204 _unpress(void *data, Evas_Object *obj __UNUSED__ , const char *emission __UNUSED__, const char *source __UNUSED__)
205 {
206    Widget_Data *wd = elm_widget_data_get(data);
207    evas_object_smart_callback_call(data, SIG_UNPRESS, NULL);
208
209    if (wd->clicked_double)
210      {
211         evas_object_smart_callback_call(data, SIG_CLICKED_DOUBLE, NULL);
212         wd->clicked_double = EINA_FALSE;
213      }
214 }
215
216 static void
217 _content_left_set(Evas_Object *obj, Evas_Object *content)
218 {
219    Widget_Data *wd = elm_widget_data_get(obj);
220    if (wd->contents.left == content) return;
221    if (wd->contents.left)
222      evas_object_del(wd->contents.left);
223    wd->contents.left = content;
224    if (content)
225      {
226         elm_widget_sub_object_add(obj, content);
227         edje_object_part_swallow(wd->panes, "elm.swallow.left", content);
228      }
229 }
230
231 static void
232 _content_right_set(Evas_Object *obj, Evas_Object *content)
233 {
234    Widget_Data *wd = elm_widget_data_get(obj);
235    if (wd->contents.right == content) return;
236    if (wd->contents.right)
237      evas_object_del(wd->contents.right);
238    wd->contents.right = content;
239    if (content)
240      {
241         elm_widget_sub_object_add(obj, content);
242         edje_object_part_swallow(wd->panes, "elm.swallow.right", content);
243      }
244 }
245
246 static Evas_Object *
247 _content_left_unset(Evas_Object *obj)
248 {
249    Widget_Data *wd = elm_widget_data_get(obj);
250    if (!wd->contents.left) return NULL;
251    Evas_Object *content = wd->contents.left;
252    elm_widget_sub_object_del(obj, content);
253    return content;
254 }
255
256 static Evas_Object *
257 _content_right_unset(Evas_Object *obj)
258 {
259    Widget_Data *wd = elm_widget_data_get(obj);
260    if (!wd->contents.right) return NULL;
261    Evas_Object *content = wd->contents.right;
262    elm_widget_sub_object_del(obj, content);
263    return content;
264 }
265
266 static void
267 _content_set_hook(Evas_Object *obj, const char *part, Evas_Object *content)
268 {
269    ELM_CHECK_WIDTYPE(obj, widtype);
270    Widget_Data *wd = elm_widget_data_get(obj);
271    if (!wd) return;
272    if (part && (!strncmp(part, "elm.swallow.", 12))) part += 12;
273    if (!part || !strcmp(part, "left"))
274      _content_left_set(obj, content);
275    else if(!strcmp(part, "right"))
276      _content_right_set(obj, content);
277 }
278
279 static Evas_Object *
280 _content_get_hook(const Evas_Object *obj, const char *part)
281 {
282    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
283    Widget_Data *wd = elm_widget_data_get(obj);
284    if (!wd) return NULL;
285    if (part && (!strncmp(part, "elm.swallow.", 12))) part += 12;
286    if (!part || !strcmp(part, "left"))
287      return wd->contents.left;
288    else if (!strcmp(part, "right"))
289      return wd->contents.right;
290    return NULL;
291 }
292
293 static Evas_Object *
294 _content_unset_hook(Evas_Object *obj, const char *part)
295 {
296    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
297    Widget_Data *wd = elm_widget_data_get(obj);
298    if (!wd) return NULL;
299    if (part && (!strncmp(part, "elm.swallow.", 12))) part += 12;
300    if (!part || !strcmp(part, "left"))
301      return _content_left_unset(obj);
302    else if (!strcmp(part, "right"))
303      return _content_right_unset(obj);
304    return NULL;
305 }
306
307 EAPI Evas_Object *
308 elm_panes_add(Evas_Object *parent)
309 {
310    Evas_Object *obj;
311    Evas *e;
312    Widget_Data *wd;
313
314    ELM_WIDGET_STANDARD_SETUP(wd, Widget_Data, parent, e, obj, NULL);
315
316    ELM_SET_WIDTYPE(widtype, "panes");
317    elm_widget_type_set(obj, "panes");
318    elm_widget_sub_object_add(parent, obj);
319    elm_widget_data_set(obj, wd);
320    elm_widget_del_hook_set(obj, _del_hook);
321    elm_widget_theme_hook_set(obj, _theme_hook);
322    elm_widget_focus_next_hook_set(obj, _elm_panes_focus_next_hook);
323    elm_widget_content_set_hook_set(obj, _content_set_hook);
324    elm_widget_content_get_hook_set(obj, _content_get_hook);
325    elm_widget_content_unset_hook_set(obj, _content_unset_hook);
326    elm_widget_can_focus_set(obj, EINA_FALSE);
327
328    wd->panes = edje_object_add(e);
329    _elm_theme_object_set(obj, wd->panes, "panes", "vertical", "default");
330    elm_widget_resize_object_set(obj, wd->panes);
331    evas_object_show(wd->panes);
332
333    elm_panes_content_left_size_set(obj, 0.5);
334
335    edje_object_signal_callback_add(wd->panes, "elm,action,click", "",
336                                    _clicked, obj);
337    edje_object_signal_callback_add(wd->panes, "elm,action,click,double", "",
338                                    _clicked_double, obj);
339    edje_object_signal_callback_add(wd->panes, "elm,action,press", "",
340                                    _press, obj);
341    edje_object_signal_callback_add(wd->panes, "elm,action,unpress", "",
342                                    _unpress, obj);
343
344    evas_object_smart_callback_add(obj, "sub-object-del", _sub_del, obj);
345    evas_object_event_callback_add(obj, EVAS_CALLBACK_CHANGED_SIZE_HINTS,
346                                   _changed_size_hints, obj);
347
348    evas_object_smart_callbacks_descriptions_set(obj, _signals);
349
350    _mirrored_set(obj, elm_widget_mirrored_get(obj));
351    _sizing_eval(obj);
352    return obj;
353 }
354
355 EINA_DEPRECATED EAPI void
356 elm_panes_content_left_set(Evas_Object *obj, Evas_Object *content)
357 {
358    _content_set_hook(obj, "left", content);
359 }
360
361 EINA_DEPRECATED EAPI void
362 elm_panes_content_right_set(Evas_Object *obj, Evas_Object *content)
363 {
364    _content_set_hook(obj, "right", content);
365 }
366
367 EINA_DEPRECATED EAPI Evas_Object *
368 elm_panes_content_left_get(const Evas_Object *obj)
369 {
370    return _content_get_hook(obj, "left");
371 }
372
373 EINA_DEPRECATED EAPI Evas_Object *
374 elm_panes_content_right_get(const Evas_Object *obj)
375 {
376    return _content_get_hook(obj, "right");
377 }
378
379 EINA_DEPRECATED EAPI Evas_Object *
380 elm_panes_content_left_unset(Evas_Object *obj)
381 {
382    return _content_unset_hook(obj, "left");
383 }
384
385 EINA_DEPRECATED EAPI Evas_Object *
386 elm_panes_content_right_unset(Evas_Object *obj)
387 {
388    return _content_unset_hook(obj, "right");
389 }
390
391 EAPI double
392 elm_panes_content_left_size_get(const Evas_Object *obj)
393 {
394    ELM_CHECK_WIDTYPE(obj, widtype) 0.0;
395    Widget_Data *wd = elm_widget_data_get(obj);
396    double w, h;
397
398    if (!wd) return 0;
399    edje_object_part_drag_value_get(wd->panes, "elm.bar", &w, &h);
400    if (wd->horizontal) return h;
401    else return w;
402 }
403
404 EAPI void
405 elm_panes_content_left_size_set(Evas_Object *obj, double size)
406 {
407    ELM_CHECK_WIDTYPE(obj, widtype);
408    Widget_Data *wd = elm_widget_data_get(obj);
409    if (!wd) return;
410    if (size < 0.0) size = 0.0;
411    else if (size > 1.0) size = 1.0;
412    if (wd->horizontal)
413      edje_object_part_drag_value_set(wd->panes, "elm.bar", 0.0, size);
414    else
415      edje_object_part_drag_value_set(wd->panes, "elm.bar", size, 0.0);
416 }
417
418 EAPI double
419 elm_panes_content_right_size_get(const Evas_Object *obj)
420 {
421    return (1.0 - elm_panes_content_left_size_get(obj));
422 }
423
424 EAPI void
425 elm_panes_content_right_size_set(Evas_Object *obj, double size)
426 {
427    elm_panes_content_left_size_set(obj, (1.0 - size));
428 }
429
430 EAPI void
431 elm_panes_horizontal_set(Evas_Object *obj, Eina_Bool horizontal)
432 {
433    ELM_CHECK_WIDTYPE(obj, widtype);
434    Widget_Data *wd = elm_widget_data_get(obj);
435    if (!wd) return;
436    wd->horizontal = horizontal;
437    _theme_hook(obj);
438    elm_panes_content_left_size_set(obj, 0.5);
439 }
440
441 EAPI Eina_Bool
442 elm_panes_horizontal_get(const Evas_Object *obj)
443 {
444    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
445    Widget_Data *wd = elm_widget_data_get(obj);
446    if (!wd) return EINA_FALSE;
447    return wd->horizontal;
448 }
449
450 EAPI void
451 elm_panes_fixed_set(Evas_Object *obj, Eina_Bool fixed)
452 {
453    ELM_CHECK_WIDTYPE(obj, widtype);
454    Widget_Data *wd = elm_widget_data_get(obj);
455    if (!wd) return;
456    wd->fixed = !!fixed;
457    if (wd->fixed == EINA_TRUE)
458      edje_object_signal_emit(wd->panes, "elm.panes.fixed", "elm");
459    else
460      edje_object_signal_emit(wd->panes, "elm.panes.unfixed", "elm");
461 }
462
463 EAPI Eina_Bool
464 elm_panes_fixed_get(const Evas_Object *obj)
465 {
466    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
467    Widget_Data *wd = elm_widget_data_get(obj);
468    if (!wd) return EINA_FALSE;
469    return wd->fixed;
470 }