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