8696c13686b2d458721265267d1e7f79a989e140
[framework/uifw/elementary.git] / src / lib / elm_conform.c
1 #include <Elementary.h>
2 #include "elm_priv.h"
3
4 /**
5  * @defgroup Conformant Conformant
6  * @ingroup Elementary
7  * 
8  * The aim is to provide a widget that can be used in elementary apps to 
9  * account for space taken up by the indicator & softkey windows when running 
10  * the illume2 module of E17.
11  */
12
13 typedef struct _Widget_Data Widget_Data;
14 struct _Widget_Data
15 {
16    Evas_Object *base;
17    Evas_Object *shelf, *panel, *virtualkeypad;
18    Evas_Object *content;
19    Evas_Object *scroller;
20    int vkeypad_height;
21    Ecore_X_Virtual_Keyboard_State vkeypad_state;
22    Ecore_Event_Handler *prop_hdl;
23 };
24
25 /* local function prototypes */
26 static const char *widtype = NULL;
27 static void
28 _del_hook(Evas_Object *obj);
29 static void
30 _theme_hook(Evas_Object *obj);
31 static void
32 _swallow_conformant_parts(Evas_Object *obj);
33 static void
34 _sizing_eval(Evas_Object *obj);
35 static Eina_Bool
36 _prop_change(void *data, int type, void *event);
37
38 /* local functions */
39 static void
40 _del_hook(Evas_Object *obj)
41 {
42    Widget_Data *wd = elm_widget_data_get(obj);
43    if (!wd) return;
44    if (wd->prop_hdl) ecore_event_handler_del(wd->prop_hdl);
45    free(wd);
46 }
47
48 static void
49 _theme_hook(Evas_Object *obj)
50 {
51    Widget_Data *wd = elm_widget_data_get(obj);
52    if (!wd) return;
53    _elm_theme_object_set(obj, wd->base, "conformant", "base",
54                          elm_widget_style_get(obj));
55    _swallow_conformant_parts(obj);
56
57    if (wd->content) edje_object_part_swallow(wd->base, "elm.swallow.content",
58                                              wd->content);
59    edje_object_scale_set(wd->base, elm_widget_scale_get(obj)
60             * _elm_config->scale);
61    _sizing_eval(obj);
62 }
63
64 static void
65 _sizing_eval(Evas_Object *obj)
66 {
67    Widget_Data *wd = elm_widget_data_get(obj);
68    Evas_Coord mw = -1, mh = -1;
69    if (!wd) return;
70    edje_object_size_min_calc(wd->base, &mw, &mh);
71    evas_object_size_hint_min_set(obj, mw, mh);
72    evas_object_size_hint_max_set(obj, -1, -1);
73 }
74
75 static void
76 _swallow_conformant_parts(Evas_Object *obj)
77 {
78 #ifdef HAVE_ELEMENTARY_X
79    Widget_Data *wd = elm_widget_data_get(obj);
80
81    Ecore_X_Window zone, xwin;
82    int sh = -1;
83
84    xwin = elm_win_xwindow_get(obj);
85    zone = ecore_x_e_illume_zone_get(xwin);
86
87    ecore_x_e_illume_indicator_geometry_get(zone, NULL, NULL, NULL, &sh);
88    if (sh < 0) sh = 0;
89    if(!wd->shelf)
90    wd->shelf = evas_object_rectangle_add(evas_object_evas_get(obj));
91    evas_object_color_set(wd->shelf, 0, 0, 0, 0);
92    evas_object_size_hint_min_set(wd->shelf, -1, sh);
93    evas_object_size_hint_max_set(wd->shelf, -1, sh);
94    edje_object_part_swallow(wd->base, "elm.swallow.shelf", wd->shelf);
95
96    wd->scroller = NULL;
97    sh = -1;
98    ecore_x_e_illume_keyboard_geometry_get(zone, NULL, NULL, NULL, &sh);
99    if (sh < 0) sh = 0;
100    wd->vkeypad_height = sh;
101    if(!wd->virtualkeypad)
102    wd->virtualkeypad= evas_object_rectangle_add(evas_object_evas_get(obj));
103    evas_object_color_set(wd->virtualkeypad, 0, 0, 0, 0);
104    evas_object_size_hint_min_set(wd->virtualkeypad, -1, sh);
105    evas_object_size_hint_max_set(wd->virtualkeypad, -1, sh);
106    edje_object_part_swallow(wd->base, "elm.swallow.virtualkeypad", wd->virtualkeypad);
107
108    sh = -1;
109    ecore_x_e_illume_softkey_geometry_get(zone, NULL, NULL, NULL, &sh);
110    if (sh < 0) sh = 0;
111    if(!wd->panel)
112    wd->panel = evas_object_rectangle_add(evas_object_evas_get(obj));
113    evas_object_color_set(wd->panel, 0, 0, 0, 0);
114    evas_object_size_hint_min_set(wd->panel, -1, sh);
115    evas_object_size_hint_max_set(wd->panel, -1, sh);
116    edje_object_part_swallow(wd->base, "elm.swallow.panel", wd->panel);
117 #endif
118 }
119
120 static void
121 _changed_size_hints(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
122 {
123    Widget_Data *wd = elm_widget_data_get(data);
124    if (!wd) return;
125    _sizing_eval(data);
126 }
127
128 static void
129 _sub_del(void *data __UNUSED__, Evas_Object *obj, void *event_info)
130 {
131    Widget_Data *wd = elm_widget_data_get(obj);
132    Evas_Object *sub = event_info;
133    if (!wd) return;
134    if (sub == wd->content)
135    {
136       evas_object_event_callback_del_full(sub, EVAS_CALLBACK_CHANGED_SIZE_HINTS,
137                _changed_size_hints, obj);
138       wd->content = NULL;
139       _sizing_eval(obj);
140    }
141 }
142
143 static void
144 _content_resize_event_cb(void *data, Evas *e, Evas_Object *obj,
145                          void *event_info)
146 {
147    Evas_Object *focus_obj = elm_widget_focused_object_get(obj);
148    if (focus_obj)
149    {
150       Evas_Coord x, y, w, h;
151
152       elm_widget_show_region_get(focus_obj, &x, &y, &w, &h);
153
154       if (h < _elm_config->finger_size) h = _elm_config->finger_size;
155       else h=1+h;
156       elm_widget_show_region_set(focus_obj, x, y, w, h);
157    }
158 }
159
160 static void
161 _update_autoscroll_objs(void *data)
162 {
163    char *type;
164    Evas_Object *sub, *top_scroller = NULL;
165    Evas_Object *conformant = (Evas_Object *) data;
166    Widget_Data *wd = elm_widget_data_get(data);
167    if (!wd) return;
168    sub = elm_widget_focused_object_get(conformant);
169
170    //Look up for Top most scroller in the Focus Object hierarchy inside Conformant.
171    while (sub)
172    {
173       type = elm_widget_type_get(sub);
174       if (!strcmp(type, "conformant")) break;
175       if (!strcmp(type, "scroller") || !strcmp(type, "genlist"))
176          top_scroller = sub;
177
178       sub = elm_object_parent_widget_get(sub);
179    }
180
181    //If the scroller got changed by app, replace it.
182    if (top_scroller != wd->scroller)
183    {
184       if (wd->scroller) evas_object_event_callback_del(wd->scroller,
185                                                        EVAS_CALLBACK_RESIZE,
186                                                        _content_resize_event_cb);
187       wd->scroller = top_scroller;
188       if (wd->scroller) evas_object_event_callback_add(wd->scroller,
189                                                        EVAS_CALLBACK_RESIZE,
190                                                        _content_resize_event_cb,
191                                                        data);
192    }
193 }
194
195 static Eina_Bool
196 _prop_change(void *data, int type __UNUSED__, void *event)
197 {
198 #ifdef HAVE_ELEMENTARY_X
199    int indicator_height=0;
200    Ecore_X_Virtual_Keyboard_State virt_keypad_state = ECORE_X_VIRTUAL_KEYBOARD_STATE_UNKNOWN;
201    Ecore_X_Event_Window_Property *ev;
202    Widget_Data *wd = elm_widget_data_get(data);
203    if (!wd) return ECORE_CALLBACK_PASS_ON;
204    ev = event;
205    if (ev->atom == ECORE_X_ATOM_E_ILLUME_ZONE)
206    {
207       Ecore_X_Window zone;
208       int sh = -1;
209
210       zone = ecore_x_e_illume_zone_get(ev->win);
211       ecore_x_e_illume_indicator_geometry_get(zone, NULL, NULL, NULL, &sh);
212       if (sh < 0) sh = indicator_height;
213
214       evas_object_size_hint_min_set(wd->shelf, -1, sh);
215       evas_object_size_hint_max_set(wd->shelf, -1, sh);
216       sh = -1;
217       zone = ecore_x_e_illume_zone_get(ev->win);
218       ecore_x_e_illume_keyboard_geometry_get(zone, NULL, NULL, NULL, &sh);
219       if (sh < 0) sh = 0;
220       evas_object_size_hint_min_set(wd->virtualkeypad, -1, sh);
221       evas_object_size_hint_max_set(wd->virtualkeypad, -1, sh);
222       sh = -1;
223       ecore_x_e_illume_softkey_geometry_get(zone, NULL, NULL, NULL, &sh);
224       if (sh < 0) sh = 0;
225       evas_object_size_hint_min_set(wd->panel, -1, sh);
226       evas_object_size_hint_max_set(wd->panel, -1, sh);
227    }
228    else if (ev->atom == ECORE_X_ATOM_E_ILLUME_INDICATOR_GEOMETRY)
229    {
230       Ecore_X_Window zone;
231       int sh = -1;
232
233       zone = ecore_x_e_illume_zone_get(ev->win);
234       ecore_x_e_illume_indicator_geometry_get(zone, NULL, NULL, NULL, &sh);
235       if (sh < 0) sh = 0;
236       evas_object_size_hint_min_set(wd->shelf, -1, sh);
237       evas_object_size_hint_max_set(wd->shelf, -1, sh);
238    }
239    else if (ev->atom == ECORE_X_ATOM_E_ILLUME_SOFTKEY_GEOMETRY)
240    {
241       Ecore_X_Window zone;
242       int sh = -1;
243
244       zone = ecore_x_e_illume_zone_get(ev->win);
245       ecore_x_e_illume_softkey_geometry_get(zone, NULL, NULL, NULL, &sh);
246       if (sh < 0) sh = 0;
247       evas_object_size_hint_min_set(wd->panel, -1, sh);
248       evas_object_size_hint_max_set(wd->panel, -1, sh);
249    }
250    else if (ev->atom == ECORE_X_ATOM_E_ILLUME_KEYBOARD_GEOMETRY)
251    {
252       Ecore_X_Window zone;
253       int ky = -1, kh = -1;
254
255       zone = ecore_x_e_illume_zone_get(ev->win);
256       ecore_x_e_illume_keyboard_geometry_get(zone, NULL, &ky, NULL, &kh);
257       if (kh < 0) kh = 0;
258       if (kh == wd->vkeypad_height) return ECORE_CALLBACK_PASS_ON;
259       wd->vkeypad_height = kh;
260       evas_object_size_hint_min_set(wd->virtualkeypad, -1, kh);
261       evas_object_size_hint_max_set(wd->virtualkeypad, -1, kh);
262    }
263    else if (ev->atom == ECORE_X_ATOM_E_VIRTUAL_KEYBOARD_STATE)
264    {
265       Ecore_X_Window zone;
266       zone = ecore_x_e_illume_zone_get(ev->win);
267       virt_keypad_state = ecore_x_e_virtual_keyboard_state_get(zone);
268       if (virt_keypad_state == wd->vkeypad_state) return ECORE_CALLBACK_PASS_ON;
269       wd->vkeypad_state = virt_keypad_state;
270       if(wd->vkeypad_state == ECORE_X_VIRTUAL_KEYBOARD_STATE_ON)
271          _update_autoscroll_objs(data);
272    }
273 #endif
274
275    return ECORE_CALLBACK_PASS_ON;
276 }
277
278 /**
279  * Add a new Conformant object
280  * 
281  * @param parent The parent object
282  * @return The new conformant object or NULL if it cannot be created
283  * 
284  * @ingroup Conformant
285  */
286 EAPI Evas_Object *
287 elm_conformant_add(Evas_Object *parent)
288 {
289    Evas_Object *obj;
290    Evas *evas;
291    Widget_Data *wd;
292
293    wd = ELM_NEW(Widget_Data);
294
295    evas = evas_object_evas_get(parent);
296
297    obj = elm_widget_add(evas);
298    ELM_SET_WIDTYPE(widtype, "conformant");
299    elm_widget_type_set(obj, "conformant");
300    elm_widget_sub_object_add(parent, obj);
301    elm_widget_data_set(obj, wd);
302    elm_widget_del_hook_set(obj, _del_hook);
303    elm_widget_theme_hook_set(obj, _theme_hook);
304
305    wd->base = edje_object_add(evas);
306    _elm_theme_object_set(obj, wd->base, "conformant", "base", "default");
307    elm_widget_resize_object_set(obj, wd->base);
308    _swallow_conformant_parts(obj);
309
310 #ifdef HAVE_ELEMENTARY_X
311    wd->prop_hdl = ecore_event_handler_add(ECORE_X_EVENT_WINDOW_PROPERTY,
312             _prop_change, obj);
313    wd->vkeypad_state = ECORE_X_VIRTUAL_KEYBOARD_STATE_OFF;
314 #endif
315
316    evas_object_smart_callback_add(obj, "sub-object-del", _sub_del, obj);
317
318    _sizing_eval(obj);
319    return obj;
320 }
321
322 /**
323  * Set the content of the conformant widget
324  *
325  * Once the content object is set, a previously set one will be deleted.
326  * If you want to keep that old content object, use the
327  * elm_conformat_content_unset() function.
328  *
329  * @param obj The conformant object
330  * @return The content that was being used
331  *
332  * @ingroup Conformant
333  */
334 EAPI void
335 elm_conformant_content_set(Evas_Object *obj, Evas_Object *content)
336 {
337    ELM_CHECK_WIDTYPE(obj, widtype);
338    Widget_Data *wd = elm_widget_data_get(obj);
339    if (!wd) return;
340    if (wd->content == content) return;
341    if (wd->content) evas_object_del(wd->content);
342    wd->content = content;
343    if (content)
344    {
345       elm_widget_sub_object_add(obj, content);
346       evas_object_event_callback_add(content, EVAS_CALLBACK_CHANGED_SIZE_HINTS,
347                                      _changed_size_hints, obj);
348       edje_object_part_swallow(wd->base, "elm.swallow.content", content);
349    }
350    _sizing_eval(obj);
351 }
352
353 /**
354  * Unset the content of the conformant widget
355  *
356  * Unparent and return the content object which was set for this widget;
357  *
358  * @param obj The conformant object
359  * @return The content that was being used
360  *
361  * @ingroup Conformant
362  */
363 EAPI Evas_Object *
364 elm_conformant_content_unset(Evas_Object *obj)
365 {
366    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
367    Widget_Data *wd = elm_widget_data_get(obj);
368    Evas_Object *content;
369    if (!wd) return NULL;
370    if (!wd->content) return NULL;
371    content = wd->content;
372    elm_widget_sub_object_del(obj, wd->content);
373    edje_object_part_unswallow(wd->base, wd->content);
374    wd->content = NULL;
375    return content;
376 }