Merge branch 'svn_merge'
[framework/uifw/elementary.git] / src / lib / elm_panel.c
1 #include <Elementary.h>
2 #include "elm_priv.h"
3
4 /**
5  * @defgroup Panel Panel
6  * @ingroup Elementary
7  *
8  * A panel is a type of animated container that contains subobjects.  It
9  * can be expanded or contracted.
10  *
11  * Orientations are as follows:
12  * ELM_PANEL_ORIENT_TOP
13  * ELM_PANEL_ORIENT_BOTTOM
14  * ELM_PANEL_ORIENT_LEFT
15  * ELM_PANEL_ORIENT_RIGHT
16  * NOTE: Only LEFT and RIGHT orientations are implemented.
17  *
18  * THIS WIDGET IS UNDER CONSTRUCTION!
19  */
20
21 typedef struct _Widget_Data Widget_Data;
22 struct _Widget_Data
23 {
24    Evas_Object *scr, *bx, *content;
25    Elm_Panel_Orient orient;
26    Eina_Bool hidden : 1;
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 _on_focus_hook(void *data, Evas_Object *obj);
33 static void _sizing_eval(Evas_Object *obj);
34 static void _resize(void *data, Evas *evas, Evas_Object *obj, void *event);
35 static void _layout(Evas_Object *o, Evas_Object_Box_Data *priv, void *data);
36 static void _toggle_panel(void *data, Evas_Object *obj, const char *emission, const char *source);
37
38 static void
39 _del_hook(Evas_Object *obj)
40 {
41    Widget_Data *wd = elm_widget_data_get(obj);
42    if (!wd) return;
43    free(wd);
44 }
45
46 static void
47 _mirrored_set(Evas_Object *obj, Eina_Bool rtl)
48 {
49    Widget_Data *wd = elm_widget_data_get(obj);
50    if (!wd) return;
51
52    if (wd->scr)
53      {
54         elm_widget_mirrored_set(wd->bx, rtl);
55         elm_panel_orient_set(obj, elm_panel_orient_get(obj));
56      }
57 }
58
59 static void
60 _theme_hook(Evas_Object *obj)
61 {
62    Widget_Data *wd = elm_widget_data_get(obj);
63    if (!wd) return;
64    _elm_widget_mirrored_reload(obj);
65    if (wd->scr)
66      {
67         Evas_Object *edj;
68         const char *str;
69
70         _mirrored_set(obj, elm_widget_mirrored_get(obj));
71         elm_smart_scroller_object_theme_set(obj, wd->scr, "panel", "base",
72                                             elm_widget_style_get(obj));
73         //   scale = (elm_widget_scale_get(obj) * _elm_config->scale);
74         //   edje_object_scale_set(wd->scr, scale);
75         edj = elm_smart_scroller_edje_object_get(wd->scr);
76         str = edje_object_data_get(edj, "focus_highlight");
77         if ((str) && (!strcmp(str, "on")))
78           elm_widget_highlight_in_theme_set(obj, EINA_TRUE);
79         else
80           elm_widget_highlight_in_theme_set(obj, EINA_FALSE);
81      }
82
83    _sizing_eval(obj);
84 }
85
86 static void
87 _on_focus_hook(void *data __UNUSED__, Evas_Object *obj)
88 {
89    Widget_Data *wd = elm_widget_data_get(obj);
90    if (!wd) return;
91    if (elm_widget_focus_get(obj))
92      evas_object_focus_set(obj, EINA_TRUE);
93    else
94      evas_object_focus_set(obj, EINA_FALSE);
95 }
96
97 static Eina_Bool
98 _elm_panel_focus_next_hook(const Evas_Object *obj, Elm_Focus_Direction dir, Evas_Object **next)
99 {
100    Widget_Data *wd = elm_widget_data_get(obj);
101    Evas_Object *cur;
102
103    if ((!wd) || (!wd->content))
104      return EINA_FALSE;
105
106    cur = wd->content;
107
108    /* Try Focus cycle in subitem */
109    if (!wd->hidden)
110      return elm_widget_focus_next_get(cur, dir, next);
111
112    /* Return */
113    *next = (Evas_Object *)obj;
114    return !elm_widget_focus_get(obj);
115 }
116
117 static void
118 _signal_emit_hook(Evas_Object *obj, const char *emission, const char *source)
119 {
120    Widget_Data *wd = elm_widget_data_get(obj);
121    if (!wd) return;
122    edje_object_signal_emit(elm_smart_scroller_edje_object_get(wd->scr),
123                            emission, source);
124 }
125
126 static void
127 _signal_callback_add_hook(Evas_Object *obj, const char *emission, const char *source, Edje_Signal_Cb func_cb, void *data)
128 {
129    Widget_Data *wd = elm_widget_data_get(obj);
130    if (!wd) return;
131    edje_object_signal_callback_add(elm_smart_scroller_edje_object_get(wd->scr),
132                                    emission, source, func_cb, data);
133 }
134
135 static void
136 _signal_callback_del_hook(Evas_Object *obj, const char *emission, const char *source, Edje_Signal_Cb func_cb, void *data)
137 {
138    Widget_Data *wd = elm_widget_data_get(obj);
139    edje_object_signal_callback_del_full(
140       elm_smart_scroller_edje_object_get(wd->scr), emission, source,
141       func_cb, data);
142 }
143
144 static void
145 _sizing_eval(Evas_Object *obj)
146 {
147    Widget_Data *wd = elm_widget_data_get(obj);
148    Evas_Coord mw = -1, mh = -1;
149    Evas_Coord vw = 0, vh = 0;
150    Evas_Coord w, h;
151    if (!wd) return;
152    evas_object_smart_calculate(wd->bx);
153    edje_object_size_min_calc(elm_smart_scroller_edje_object_get(wd->scr),
154                              &mw, &mh);
155    evas_object_geometry_get(obj, NULL, NULL, &w, &h);
156    if (w < mw) w = mw;
157    if (h < mh) h = mh;
158    evas_object_resize(wd->scr, w, h);
159
160    evas_object_size_hint_min_get(wd->bx, &mw, &mh);
161    if (w > mw) mw = w;
162    if (h > mh) mh = h;
163    evas_object_resize(wd->bx, mw, mh);
164
165    elm_smart_scroller_child_viewport_size_get(wd->scr, &vw, &vh);
166    mw = mw + (w - vw);
167    mh = mh + (h - vh);
168    evas_object_size_hint_min_set(obj, mw, mh);
169    evas_object_size_hint_max_set(obj, -1, -1);
170 }
171
172 static void
173 _resize(void *data, Evas *evas __UNUSED__, Evas_Object *obj __UNUSED__, void *event __UNUSED__)
174 {
175    Widget_Data *wd = elm_widget_data_get(data);
176    Evas_Coord mw, mh, vw, vh, w, h;
177    if (!wd) return;
178    elm_smart_scroller_child_viewport_size_get(wd->scr, &vw, &vh);
179    evas_object_size_hint_min_get(wd->bx, &mw, &mh);
180    evas_object_geometry_get(wd->bx, NULL, NULL, &w, &h);
181    if ((vw >= mw) || (vh >= mh))
182      {
183         if ((w != vw) || (h != vh)) evas_object_resize(wd->bx, vw, vh);
184      }
185 }
186
187 static void
188 _layout(Evas_Object *o, Evas_Object_Box_Data *priv, void *data)
189 {
190    Widget_Data *wd = data;
191    if (!wd) return;
192    _els_box_layout(o, priv, EINA_TRUE, EINA_FALSE, EINA_FALSE);
193 }
194
195 static void
196 _toggle_panel(void *data, Evas_Object *obj, const char *emission __UNUSED__, const char *source __UNUSED__)
197 {
198    Widget_Data *wd = elm_widget_data_get(data);
199    if (!wd) return;
200    if (wd->hidden)
201      {
202         edje_object_signal_emit(elm_smart_scroller_edje_object_get(wd->scr),
203                                 "elm,action,show", "elm");
204         wd->hidden = EINA_FALSE;
205         evas_object_repeat_events_set(obj, EINA_FALSE);
206      }
207    else
208      {
209         edje_object_signal_emit(elm_smart_scroller_edje_object_get(wd->scr),
210                                 "elm,action,hide", "elm");
211         wd->hidden = EINA_TRUE;
212         evas_object_repeat_events_set(obj, EINA_TRUE);
213         if (elm_widget_focus_get(wd->content))
214           {
215              elm_widget_focused_object_clear(obj);
216              elm_widget_focus_steal(obj);
217           }
218      }
219 }
220
221 static Eina_Bool
222 _event_hook(Evas_Object *obj, Evas_Object *src __UNUSED__, Evas_Callback_Type type, void *event_info)
223 {
224    if ((src != obj) || (type != EVAS_CALLBACK_KEY_DOWN)) return EINA_FALSE;
225
226    Evas_Event_Key_Down *ev = event_info;
227    Widget_Data *wd = elm_widget_data_get(obj);
228    if (!wd) return EINA_FALSE;
229
230    if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return EINA_FALSE;
231
232    if ((strcmp(ev->keyname, "Return")) &&
233        (strcmp(ev->keyname, "KP_Enter")) &&
234        (strcmp(ev->keyname, "space")))
235      return EINA_FALSE;
236
237    _toggle_panel(obj, NULL, "elm,action,panel,toggle", "*");
238
239    ev->event_flags |= EVAS_EVENT_FLAG_ON_HOLD;
240    return EINA_TRUE;
241 }
242
243 /**
244  * Adds a panel object
245  *
246  * @param parent The parent object
247  *
248  * @return The panel object, or NULL on failure
249  *
250  * @ingroup Panel
251  */
252 EAPI Evas_Object *
253 elm_panel_add(Evas_Object *parent)
254 {
255    Evas_Object *obj;
256    Evas *e;
257    Widget_Data *wd;
258
259    ELM_WIDGET_STANDARD_SETUP(wd, Widget_Data, parent, e, obj, NULL);
260
261    ELM_SET_WIDTYPE(widtype, "panel");
262    elm_widget_type_set(obj, "panel");
263    elm_widget_sub_object_add(parent, obj);
264    elm_widget_data_set(obj, wd);
265    elm_widget_del_hook_set(obj, _del_hook);
266    elm_widget_theme_hook_set(obj, _theme_hook);
267    elm_widget_on_focus_hook_set(obj, _on_focus_hook, NULL);
268    elm_widget_signal_emit_hook_set(obj, _signal_emit_hook);
269    elm_widget_signal_callback_add_hook_set(obj, _signal_callback_add_hook);
270    elm_widget_signal_callback_del_hook_set(obj, _signal_callback_del_hook);
271    elm_widget_focus_next_hook_set(obj, _elm_panel_focus_next_hook);
272    elm_widget_can_focus_set(obj, EINA_TRUE);
273    elm_widget_event_hook_set(obj, _event_hook);
274
275    wd->scr = elm_smart_scroller_add(e);
276    elm_smart_scroller_widget_set(wd->scr, obj);
277    _theme_hook(obj);
278    elm_smart_scroller_bounce_allow_set(wd->scr, EINA_FALSE, EINA_FALSE);
279    elm_widget_resize_object_set(obj, wd->scr);
280    elm_smart_scroller_policy_set(wd->scr, ELM_SMART_SCROLLER_POLICY_OFF,
281                                  ELM_SMART_SCROLLER_POLICY_OFF);
282
283    wd->hidden = EINA_FALSE;
284    wd->orient = ELM_PANEL_ORIENT_LEFT;
285
286    wd->bx = evas_object_box_add(e);
287    evas_object_size_hint_align_set(wd->bx, 0.5, 0.5);
288    evas_object_box_layout_set(wd->bx, _layout, wd, NULL);
289    elm_widget_sub_object_add(obj, wd->bx);
290    elm_smart_scroller_child_set(wd->scr, wd->bx);
291    evas_object_show(wd->bx);
292
293    edje_object_signal_callback_add(elm_smart_scroller_edje_object_get(wd->scr),
294                                    "elm,action,panel,toggle", "*",
295                                    _toggle_panel, obj);
296
297    evas_object_event_callback_add(wd->scr, EVAS_CALLBACK_RESIZE, _resize, obj);
298
299    _mirrored_set(obj, elm_widget_mirrored_get(obj));
300    _sizing_eval(obj);
301    return obj;
302 }
303
304 /**
305  * Sets the orientation of the panel
306  *
307  * @param parent The parent object
308  * @param orient The panel orientation.  Can be one of the following:
309  * ELM_PANEL_ORIENT_TOP
310  * ELM_PANEL_ORIENT_BOTTOM
311  * ELM_PANEL_ORIENT_LEFT
312  * ELM_PANEL_ORIENT_RIGHT
313  *
314  * NOTE: Only LEFT and RIGHT orientations are implemented.
315  *
316  * @ingroup Panel
317  */
318 EAPI void
319 elm_panel_orient_set(Evas_Object *obj, Elm_Panel_Orient orient)
320 {
321    ELM_CHECK_WIDTYPE(obj, widtype);
322    Widget_Data *wd = elm_widget_data_get(obj);
323    if (!wd) return;
324    wd->orient = orient;
325    switch (orient)
326      {
327       case ELM_PANEL_ORIENT_TOP:
328          elm_smart_scroller_object_theme_set(obj, wd->scr, "panel", "base", "top");
329          break;
330       case ELM_PANEL_ORIENT_BOTTOM:
331          elm_smart_scroller_object_theme_set(obj, wd->scr, "panel", "base", "bottom");
332          break;
333       case ELM_PANEL_ORIENT_LEFT:
334          if (!elm_widget_mirrored_get(obj))
335            elm_smart_scroller_object_theme_set(obj, wd->scr, "panel", "base", "left");
336          else
337            elm_smart_scroller_object_theme_set(obj, wd->scr, "panel", "base", "right");
338          break;
339       case ELM_PANEL_ORIENT_RIGHT:
340          if (!elm_widget_mirrored_get(obj))
341            elm_smart_scroller_object_theme_set(obj, wd->scr, "panel", "base", "right");
342          else
343            elm_smart_scroller_object_theme_set(obj, wd->scr, "panel", "base", "left");
344          break;
345      }
346
347    _sizing_eval(obj);
348 }
349
350 /**
351  * Get the orientation of the panel.
352  *
353  * @param obj The panel object
354  * @return The Elm_Panel_Orient, or ELM_PANEL_ORIENT_LEFT on failure.
355  *
356  * @ingroup Panel
357  */
358 EAPI Elm_Panel_Orient
359 elm_panel_orient_get(const Evas_Object *obj)
360 {
361    ELM_CHECK_WIDTYPE(obj, widtype) ELM_PANEL_ORIENT_LEFT;
362    Widget_Data *wd = elm_widget_data_get(obj);
363    if (!wd) return ELM_PANEL_ORIENT_LEFT;
364    return wd->orient;
365 }
366
367 /**
368  * Set the content of the panel.
369  *
370  * Once the content object is set, a previously set one will be deleted.
371  * If you want to keep that old content object, use the
372  * elm_panel_content_unset() function.
373  *
374  * @param obj The panel object
375  * @param content The panel content
376  *
377  * @ingroup Panel
378  */
379 EAPI void
380 elm_panel_content_set(Evas_Object *obj, Evas_Object *content)
381 {
382    ELM_CHECK_WIDTYPE(obj, widtype);
383    Widget_Data *wd = elm_widget_data_get(obj);
384    if (!wd) return;
385    if (wd->content == content) return;
386    if (wd->content)
387      evas_object_box_remove_all(wd->bx, EINA_TRUE);
388    wd->content = content;
389    if (content)
390      {
391         evas_object_box_append(wd->bx, wd->content);
392         evas_object_show(wd->content);
393      }
394    _sizing_eval(obj);
395 }
396
397 /**
398  * Get the content of the panel.
399  *
400  * Return the content object which is set for this widget.
401  *
402  * @param obj The panel object
403  * @return The content that is being used
404  *
405  * @ingroup Panel
406  */
407 EAPI Evas_Object *
408 elm_panel_content_get(const Evas_Object *obj)
409 {
410    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
411    Widget_Data *wd = elm_widget_data_get(obj);
412    if (!wd) return NULL;
413    return wd->content;
414 }
415
416 /**
417  * Unset the content of the panel.
418  *
419  * Unparent and return the content object which was set for this widget.
420  *
421  * @param obj The panel object
422  * @return The content that was being used
423  *
424  * @ingroup Panel
425  */
426 EAPI Evas_Object *
427 elm_panel_content_unset(Evas_Object *obj)
428 {
429    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
430    Widget_Data *wd = elm_widget_data_get(obj);
431    Evas_Object *content;
432    if (!wd) return NULL;
433    if (!wd->content) return NULL;
434    content = wd->content;
435    evas_object_box_remove_all(wd->bx, EINA_FALSE);
436    wd->content = NULL;
437    return content;
438 }
439
440 /**
441  * Set the state of the panel.
442  *
443  * @param obj The panel object
444  * @param hidden If true, the panel will run the edje animation to contract
445  *
446  * @ingroup Panel
447  */
448 EAPI void
449 elm_panel_hidden_set(Evas_Object *obj, Eina_Bool hidden)
450 {
451    ELM_CHECK_WIDTYPE(obj, widtype);
452    Widget_Data *wd = elm_widget_data_get(obj);
453    if (!wd) return;
454    if (wd->hidden == hidden) return;
455    _toggle_panel(obj, NULL, "elm,action,panel,toggle", "*");
456 }
457
458 /**
459  * Get the state of the panel.
460  *
461  * @param obj The panel object
462  * @param hidden If true, the panel is in the "hide" state
463  *
464  * @ingroup Panel
465  */
466 EAPI Eina_Bool
467 elm_panel_hidden_get(const Evas_Object *obj)
468 {
469    ELM_CHECK_WIDTYPE(obj, widtype) EINA_FALSE;
470    Widget_Data *wd = elm_widget_data_get(obj);
471    if (!wd) return EINA_FALSE;
472    return wd->hidden;
473 }
474
475 /**
476  * Toggle the state of the panel from code
477  *
478  * @param obj The panel object
479  *
480  * @ingroup Panel
481  */
482 EAPI void
483 elm_panel_toggle(Evas_Object *obj)
484 {
485    ELM_CHECK_WIDTYPE(obj, widtype);
486    Widget_Data *wd = elm_widget_data_get(obj);
487    if (!wd) return;
488    _toggle_panel(obj, NULL, "elm,action,panel,toggle", "*");
489 }