[SLP Merge] 2011/7/14 12:00PM
[framework/uifw/elementary.git] / src / lib / elm_panes.c
1 #include <Elementary.h>
2 #include "elm_priv.h"
3
4 /**
5  * @defgroup Panes Panes
6  * @ingroup Elementary
7  *
8  */
9
10 typedef struct _Widget_Data Widget_Data;
11
12 struct _Widget_Data
13 {
14    Evas_Object *panes;
15
16    struct
17      {
18         Evas_Object *left;
19         Evas_Object *right;
20      } contents;
21
22    struct
23      {
24         int x_diff;
25         int y_diff;
26         Eina_Bool move;
27      } move;
28
29    Eina_Bool clicked_double;
30    Eina_Bool horizontal;
31    Eina_Bool fixed;
32 };
33
34 static const char *widtype = NULL;
35 static void _del_hook(Evas_Object *obj);
36 static void _mirrored_set(Evas_Object *obj, Eina_Bool rtl);
37 static void _theme_hook(Evas_Object *obj);
38 static void _sizing_eval(Evas_Object *obj);
39 static void _changed_size_hints(void *data, Evas *e, Evas_Object *obj, void *event_info);
40
41 static const char SIG_CLICKED[] = "clicked";
42 static const char SIG_PRESS[] = "press";
43 static const char SIG_UNPRESS[] = "unpress";
44 static const char SIG_CLICKED_DOUBLE[] = "clicked,double";
45
46 static const Evas_Smart_Cb_Description _signals[] = {
47    {SIG_CLICKED, ""},
48    {SIG_PRESS, ""},
49    {SIG_UNPRESS, ""},
50    {SIG_CLICKED_DOUBLE, ""},
51    {NULL, NULL}
52 };
53
54 static void
55 _del_hook(Evas_Object *obj)
56 {
57    Widget_Data *wd = elm_widget_data_get(obj);
58    if (!wd) return;
59    free(wd);
60 }
61
62 static void
63 _mirrored_set(Evas_Object *obj, Eina_Bool rtl)
64 {
65    Widget_Data *wd = elm_widget_data_get(obj);
66    if (!wd) return;
67    edje_object_mirrored_set(wd->panes, rtl);
68 }
69
70 static void
71 _theme_hook(Evas_Object *obj)
72 {
73    Widget_Data *wd = elm_widget_data_get(obj);
74    const char *style = elm_widget_style_get(obj);
75    double size;
76
77    if (!wd) return;
78    _elm_widget_mirrored_reload(obj);
79    _mirrored_set(obj, elm_widget_mirrored_get(obj));
80    size = elm_panes_content_left_size_get(obj);
81
82    if (wd->horizontal)
83      _elm_theme_object_set(obj, wd->panes, "panes", "horizontal", style);
84    else
85      _elm_theme_object_set(obj, wd->panes, "panes", "vertical", style);
86
87    if (wd->contents.left)
88      edje_object_part_swallow(wd->panes, "elm.swallow.left", wd->contents.left);
89    if (wd->contents.right)
90      edje_object_part_swallow(wd->panes, "elm.swallow.right", wd->contents.right);
91    if(wd->contents.left && wd->contents.right)
92      edje_object_signal_emit(wd->panes, "elm.panes.pair", "elm");
93    if(wd->fixed)
94      edje_object_signal_emit(wd->panes, "elm.panes.fixed", "elm");
95
96    edje_object_scale_set(wd->panes, elm_widget_scale_get(obj) *
97                          _elm_config->scale);
98    _sizing_eval(obj);
99    elm_panes_content_left_size_set(obj, size);
100 }
101
102 static Eina_Bool
103 _elm_panes_focus_next_hook(const Evas_Object *obj, Elm_Focus_Direction dir, Evas_Object **next)
104 {
105    Widget_Data *wd = elm_widget_data_get(obj);
106
107    if (!wd)
108      return EINA_FALSE;
109
110    double w, h;
111    edje_object_part_drag_value_get(wd->panes, "elm.bar", &w, &h);
112    if (((wd->horizontal) && ( h == 0.0 )) || ((!wd->horizontal) && ( w == 0.0 )))
113      return elm_widget_focus_next_get(wd->contents.right, dir, next);
114
115    Evas_Object *chain[2];
116
117    /* Direction */
118    if (dir == ELM_FOCUS_PREVIOUS)
119      {
120         chain[0] = wd->contents.right;
121         chain[1] = wd->contents.left;
122      }
123    else if (dir == ELM_FOCUS_NEXT)
124      {
125         chain[0] = wd->contents.left;
126         chain[1] = wd->contents.right;
127      }
128    else
129      return EINA_FALSE;
130
131    unsigned char i = elm_widget_focus_get(chain[1]);
132
133    if (elm_widget_focus_next_get(chain[i], dir, next))
134      return EINA_TRUE;
135
136    i = !i;
137
138    Evas_Object *to_focus;
139    if (elm_widget_focus_next_get(chain[i], dir, &to_focus))
140      {
141         *next = to_focus;
142         return !!i;
143      }
144
145    return EINA_FALSE;
146 }
147
148 static void
149 _sizing_eval(Evas_Object *obj)
150 {
151    Widget_Data *wd = elm_widget_data_get(obj);
152    if (!wd) return;
153 }
154
155 static void
156 _changed_size_hints(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
157 {
158    _sizing_eval(data);
159 }
160
161 static void
162 _sub_del(void *data __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
163 {
164    Widget_Data *wd = elm_widget_data_get(obj);
165    Evas_Object *sub = event_info;
166
167    if (!wd) return;
168    if (sub == wd->contents.left)
169      {
170         evas_object_event_callback_del_full(sub, EVAS_CALLBACK_CHANGED_SIZE_HINTS,
171                                             _changed_size_hints, obj);
172         wd->contents.left = NULL;
173         _sizing_eval(obj);
174      }
175    else if (sub == wd->contents.right)
176      {
177         evas_object_event_callback_del_full(sub, EVAS_CALLBACK_CHANGED_SIZE_HINTS,
178                                             _changed_size_hints, obj);
179         wd->contents.right= NULL;
180         _sizing_eval(obj);
181      }
182 }
183
184 static void
185 _clicked(void *data, Evas_Object *obj __UNUSED__ , const char *emission __UNUSED__, const char *source __UNUSED__)
186 {
187    evas_object_smart_callback_call(data, SIG_CLICKED, NULL);
188 }
189
190 static void
191 _clicked_double(void *data, Evas_Object *obj __UNUSED__ , const char *emission __UNUSED__, const char *source __UNUSED__)
192 {
193    Widget_Data *wd = elm_widget_data_get(data);
194
195    wd->clicked_double = EINA_TRUE;
196 }
197
198 static void
199 _press(void *data, Evas_Object *obj __UNUSED__ , const char *emission __UNUSED__, const char *source __UNUSED__)
200 {
201    evas_object_smart_callback_call(data, SIG_PRESS, NULL);
202 }
203
204 static void
205 _unpress(void *data, Evas_Object *obj __UNUSED__ , const char *emission __UNUSED__, const char *source __UNUSED__)
206 {
207    Widget_Data *wd = elm_widget_data_get(data);
208    evas_object_smart_callback_call(data, SIG_UNPRESS, NULL);
209
210    if (wd->clicked_double)
211      {
212         evas_object_smart_callback_call(data, SIG_CLICKED_DOUBLE, NULL);
213         wd->clicked_double = EINA_FALSE;
214      }
215 }
216
217 /**
218  * Add a new panes to the parent
219  *
220  * @param[in] parent The parent object
221  * @return The new object or NULL if it cannot be created
222  *
223  * @ingroup Panes
224  */
225 EAPI Evas_Object *
226 elm_panes_add(Evas_Object *parent)
227 {
228    Evas_Object *obj;
229    Evas *e;
230    Widget_Data *wd;
231
232    ELM_WIDGET_STANDARD_SETUP(wd, Widget_Data, parent, e, obj, NULL);
233
234    ELM_SET_WIDTYPE(widtype, "panes");
235    elm_widget_type_set(obj, "panes");
236    elm_widget_can_focus_set(obj, EINA_FALSE);
237    elm_widget_sub_object_add(parent, obj);
238    elm_widget_data_set(obj, wd);
239    elm_widget_del_hook_set(obj, _del_hook);
240    elm_widget_theme_hook_set(obj, _theme_hook);
241    elm_widget_focus_next_hook_set(obj, _elm_panes_focus_next_hook);
242
243    wd->panes = edje_object_add(e);
244    _elm_theme_object_set(obj, wd->panes, "panes", "vertical", "default");
245    elm_widget_resize_object_set(obj, wd->panes);
246    evas_object_show(wd->panes);
247
248    elm_panes_content_left_size_set(obj, 0.5);
249
250    edje_object_signal_callback_add(wd->panes, "elm,action,click", "",
251                                    _clicked, obj);
252    edje_object_signal_callback_add(wd->panes, "elm,action,click,double", "",
253                                    _clicked_double, obj);
254    edje_object_signal_callback_add(wd->panes, "elm,action,press", "",
255                                    _press, obj);
256    edje_object_signal_callback_add(wd->panes, "elm,action,unpress", "",
257                                    _unpress, obj);
258
259    evas_object_smart_callback_add(obj, "sub-object-del", _sub_del, obj);
260    evas_object_event_callback_add(obj, EVAS_CALLBACK_CHANGED_SIZE_HINTS,
261                                   _changed_size_hints, obj);
262
263    evas_object_smart_callbacks_descriptions_set(obj, _signals);
264
265    _mirrored_set(obj, elm_widget_mirrored_get(obj));
266    _sizing_eval(obj);
267    return obj;
268 }
269
270
271 /**
272  * Set the left/top content of the panes widget
273  *
274  * Once the content object is set, a previously set one will be deleted.
275  * If you want to keep that old content object, use the
276  * elm_panes_content_left_unset() function.
277  *
278  * @param[in] obj The panes object
279  * @param[in] content The new left/top content object
280  *
281  * @ingroup Panes
282  */
283 EAPI void
284 elm_panes_content_left_set(Evas_Object *obj, Evas_Object *content)
285 {
286    ELM_CHECK_WIDTYPE(obj, widtype);
287    Widget_Data *wd = elm_widget_data_get(obj);
288    if (wd->contents.left)
289      {
290         evas_object_del(wd->contents.left);
291         wd->contents.left = NULL;
292      }
293    if (content)
294      {
295         wd->contents.left = content;
296         elm_widget_sub_object_add(obj, content);
297         edje_object_part_swallow(wd->panes, "elm.swallow.left", content);
298         if (wd->contents.right)
299           edje_object_signal_emit(wd->panes, "elm.panes.pair", "elm");
300      }
301    else
302       edje_object_signal_emit(wd->panes, "elm.panes.unpair", "elm");
303 }
304
305 /**
306  * Set the right/bottom content of the panes widget
307  *
308  * Once the content object is set, a previously set one will be deleted.
309  * If you want to keep that old content object, use the
310  * elm_panes_content_right_unset() function.
311  *
312  * @param[in] obj The panes object
313  * @param[in] content The new right/bottom content object
314  *
315  * @ingroup Panes
316  */
317 EAPI void
318 elm_panes_content_right_set(Evas_Object *obj, Evas_Object *content)
319 {
320    ELM_CHECK_WIDTYPE(obj, widtype);
321    Widget_Data *wd = elm_widget_data_get(obj);
322    if (wd->contents.right)
323      {
324         evas_object_del(wd->contents.right);
325         wd->contents.right = NULL;
326      }
327    if (content)
328      {
329         wd->contents.right = content;
330         elm_widget_sub_object_add(obj, content);
331         edje_object_part_swallow(wd->panes, "elm.swallow.right", content);
332         if (wd->contents.left)
333           edje_object_signal_emit(wd->panes, "elm.panes.pair", "elm");
334      }
335    else
336      edje_object_signal_emit(wd->panes, "elm.panes.unpair", "elm");
337 }
338
339 /**
340  * Get the left/top content used for the panes
341  *
342  * Return the left/top content object which is set for this widget.
343  *
344  * @param[in] obj The panes object
345  * @return The left/top content object that is being used
346  *
347  * @ingroup Panes
348  */
349 EAPI Evas_Object *
350 elm_panes_content_left_get(const Evas_Object *obj)
351 {
352    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
353    Widget_Data *wd = elm_widget_data_get(obj);
354    return wd->contents.left;
355 }
356
357 /**
358  * Get the right/bottom content used for the panes
359  *
360  * Return the right/bottom content object which is set for this widget.
361  *
362  * @param[in] obj The panes object
363  * @return The right/bottom content object that is being used
364  *
365  * @ingroup Panes
366  */
367 EAPI Evas_Object *
368 elm_panes_content_right_get(const Evas_Object *obj)
369 {
370    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
371    Widget_Data *wd = elm_widget_data_get(obj);
372    return wd->contents.right;
373 }
374
375 /**
376  * Unset the left/top content used for the panes
377  *
378  * Unparent and return the left content object which was set for this widget.
379  *
380  * @param[in] obj The panes object
381  * @return The left/top content object that was being used
382  *
383  * @ingroup Panes
384  */
385 EAPI Evas_Object *
386 elm_panes_content_left_unset(Evas_Object *obj)
387 {
388    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
389    Widget_Data *wd = elm_widget_data_get(obj);
390    if (!wd) return NULL;
391    if (!wd->contents.left) return NULL;
392    Evas_Object *content = wd->contents.left;
393    elm_widget_sub_object_del(obj, content);
394    edje_object_part_unswallow(wd->panes, content);
395    evas_object_hide(content);
396    wd->contents.left = NULL;
397    edje_object_signal_emit(wd->panes, "elm.panes.unpair", "elm");
398    return content;
399 }
400
401 /**
402  * Unset the right/bottom content used for the panes
403  *
404  * Unparent and return the right content object which was set for this widget.
405  *
406  * @param[in] obj The panes object
407   * @return The right/bottom content object that was being used
408  *
409  * @ingroup Panes
410  */
411 EAPI Evas_Object *
412 elm_panes_content_right_unset(Evas_Object *obj)
413 {
414    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
415    Widget_Data *wd = elm_widget_data_get(obj);
416    if (!wd) return NULL;
417    if (!wd->contents.right) return NULL;
418    Evas_Object *content = wd->contents.right;
419    elm_widget_sub_object_del(obj, content);
420    edje_object_part_unswallow(wd->panes, content);
421    evas_object_hide(content);
422    wd->contents.right = NULL;
423    edje_object_signal_emit(wd->panes, "elm.panes.unpair", "elm");
424    return content;
425 }
426
427 /**
428  * Get the relative normalized size of left/top content of the pane
429  *
430  * @param[in] obj The panes object
431  * @return The value of type double in the range [0.0,1.0]
432  *
433  * @ingroup Panes
434  */
435 EAPI double
436 elm_panes_content_left_size_get(const Evas_Object *obj)
437 {
438    ELM_CHECK_WIDTYPE(obj, widtype) 0.0;
439    Widget_Data *wd = elm_widget_data_get(obj);
440    double w, h;
441
442    edje_object_part_drag_value_get(wd->panes, "elm.bar", &w, &h);
443    if (wd->horizontal) return h;
444    else return w;
445 }
446
447 /**
448  * Set a size of the left/top content with a relative normalized double value
449  *
450  * @param[in] obj The panes object
451  * @param[in] size The value of type double in the range [0.0,1.0]
452  *
453  * @ingroup Panes
454  */
455 EAPI void
456 elm_panes_content_left_size_set(Evas_Object *obj, double size)
457 {
458    ELM_CHECK_WIDTYPE(obj, widtype);
459    Widget_Data *wd = elm_widget_data_get(obj);
460
461    if (size < 0.0) size = 0.0;
462    else if (size > 1.0) size = 1.0;
463    if (wd->horizontal)
464      edje_object_part_drag_value_set(wd->panes, "elm.bar", 0.0, size);
465    else
466      edje_object_part_drag_value_set(wd->panes, "elm.bar", size, 0.0);
467 }
468
469 /**
470  * Set the type of an existing panes object to horizontal/vertical
471  *
472  * By default the panes is of vertical type
473  *
474  * @param[in] obj The panes object
475  * @param[in] horizontal Boolean value. If true, then the type is set to horizontal else vertical
476  *
477  * @ingroup Panes
478  */
479 EAPI void
480 elm_panes_horizontal_set(Evas_Object *obj, Eina_Bool horizontal)
481 {
482    ELM_CHECK_WIDTYPE(obj, widtype);
483    Widget_Data *wd = elm_widget_data_get(obj);
484
485    wd->horizontal = horizontal;
486    _theme_hook(obj);
487    elm_panes_content_left_size_set(obj, 0.5);
488 }
489
490 /**
491  * Indicate if the type of pane object is horizontal or not
492  *
493  * @param[in] obj The panes object
494  * @return true if it is of horizontal type else false
495  *
496  * @ingroup Panes
497  */
498 EAPI Eina_Bool
499 elm_panes_horizontal_get(const Evas_Object *obj)
500 {
501    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
502    Widget_Data *wd = elm_widget_data_get(obj);
503    return wd->horizontal;
504 }
505
506 /**
507  * Set a handler of the pane object movable or non-movable
508  *
509  * @param[in] obj The panes object
510  * @param[in] fixed If set to true then the views size can't be changed using handler otherwise using handler they can be resized
511  *
512  * @ingroup Panes
513  */
514 EAPI void
515 elm_panes_fixed_set(Evas_Object *obj, Eina_Bool fixed)
516 {
517    ELM_CHECK_WIDTYPE(obj, widtype);
518    Widget_Data *wd = elm_widget_data_get(obj);
519    wd->fixed = fixed;
520    if (wd->fixed == EINA_TRUE)
521      edje_object_signal_emit(wd->panes, "elm.panes.fixed", "elm");
522    else
523      edje_object_signal_emit(wd->panes, "elm.panes.unfixed", "elm");
524 }
525
526 /**
527  * Indicate if the handler of the panes object can be moved with user interaction
528  *
529  * @param[in] obj The panes object
530  * @return false if the views can be resized using handler else true
531  *
532  * @ingroup Panes
533  */
534 EAPI Eina_Bool
535 elm_panes_fixed_get(const Evas_Object *obj)
536 {
537    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
538    Widget_Data *wd = elm_widget_data_get(obj);
539    return wd->fixed;
540 }