Merge "[controlbar] enable - elm_object_disabled_set"
[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  * 
7  * The aim is to provide a widget that can be used in elementary apps to 
8  * account for space taken up by the indicator, virtual keypad & softkey windows when running 
9  * the illume2 module of E17.
10  */
11
12 typedef struct _Widget_Data Widget_Data;
13 struct _Widget_Data 
14 {
15    Evas_Object *base;
16    Evas_Object *shelf, *panel, *virtualkeypad;
17    Evas_Object *content;
18    Evas_Object *scroller;
19    Ecore_Event_Handler *prop_hdl;
20 #ifdef HAVE_ELEMENTARY_X
21    Ecore_X_Virtual_Keyboard_State vkb_state;
22 #endif
23    struct {
24       Ecore_Animator *animator; // animaton timer
25       double start; // time started
26       Evas_Coord auto_x, auto_y; // desired delta
27       Evas_Coord x, y; // current delta
28    } delta;
29 };
30
31 /* Enum to identify conformant swallow parts */
32 typedef enum _Conformant_Part_Type Conformant_Part_Type;
33 enum _Conformant_Part_Type
34 {
35    ELM_CONFORM_INDICATOR_PART      = 1,
36    ELM_CONFORM_VIRTUAL_KEYPAD_PART = 2,
37    ELM_CONFORM_SOFTKEY_PART        = 4
38 };
39 #define SUB_TYPE_COUNT 2
40 static char *sub_type[SUB_TYPE_COUNT] = { "scroller", "genlist" };
41
42 /* local function prototypes */
43 static const char *widtype = NULL;
44 static void _del_hook(Evas_Object *obj);
45 static void _theme_hook(Evas_Object *obj);
46 static void
47 _swallow_conformant_parts(Evas_Object *obj);
48 static void
49 _conformant_part_size_set(Evas_Object *obj, Evas_Object *sobj, Evas_Coord sx,
50                           Evas_Coord sy, Evas_Coord sw, Evas_Coord sh);
51 static void
52 _conformant_part_sizing_eval(Evas_Object *obj, Conformant_Part_Type part_type);
53 static void _sizing_eval(Evas_Object *obj);
54 static Eina_Bool _prop_change(void *data, int type, void *event);
55
56 /* local functions */
57 static void 
58 _del_hook(Evas_Object *obj) 
59 {
60    Widget_Data *wd = elm_widget_data_get(obj);
61
62    if (!wd) return;
63    if (wd->prop_hdl) ecore_event_handler_del(wd->prop_hdl);
64    free(wd);
65 }
66
67 static void 
68 _theme_hook(Evas_Object *obj) 
69 {
70    Widget_Data *wd = elm_widget_data_get(obj);
71
72    if (!wd) return;
73    _elm_theme_object_set(obj, wd->base, "conformant", "base", elm_widget_style_get(obj));
74    _swallow_conformant_parts(obj);
75
76    if (wd->content)
77      edje_object_part_swallow(wd->base, "elm.swallow.content", wd->content);
78    edje_object_scale_set(wd->base, elm_widget_scale_get(obj) * _elm_config->scale);
79    _sizing_eval(obj);
80 }
81
82 static void 
83 _sizing_eval(Evas_Object *obj) 
84 {
85    Widget_Data *wd = elm_widget_data_get(obj);
86    Evas_Coord mw = -1, mh = -1;
87
88    if (!wd) return;
89    edje_object_size_min_calc(wd->base, &mw, &mh);
90    evas_object_size_hint_min_set(obj, mw, mh);
91    evas_object_size_hint_max_set(obj, -1, -1);
92 }
93
94 static void
95 _conformant_part_size_set(Evas_Object *obj, Evas_Object *sobj, Evas_Coord sx,
96                           Evas_Coord sy, Evas_Coord sw, Evas_Coord sh)
97 {
98    Evas_Coord cx, cy, cw, ch;
99    Evas_Coord part_height = 0, part_width = 0;
100
101    evas_object_geometry_get(obj, &cx, &cy, &cw, &ch);
102
103    /* Part overlapping with conformant */
104    if ((cx < (sx+sw)) && ((cx+cw) > sx) && (cy < (sy+sh)) && ((cy+ch) > sy))
105      {
106         part_height = MIN((cy+ch), (sy+sh)) - MAX(cy, sy);
107         part_width = MIN((cx+cw), (sx+sw)) - MAX(cx, sx);
108      }
109
110    evas_object_size_hint_min_set(sobj, part_width, part_height);
111    evas_object_size_hint_max_set(sobj, part_width, part_height);
112 }
113
114 static void
115 _conformant_part_sizing_eval(Evas_Object *obj, Conformant_Part_Type part_type)
116 {
117 #ifdef HAVE_ELEMENTARY_X
118    Ecore_X_Window zone, xwin;
119    int sx = -1, sy = -1, sw = -1, sh = -1;
120    Widget_Data *wd = elm_widget_data_get(obj);
121
122    if (!wd) return;
123
124    xwin = elm_win_xwindow_get(obj);
125    zone = ecore_x_e_illume_zone_get(xwin);
126
127    if (part_type & ELM_CONFORM_INDICATOR_PART)
128      {
129         ecore_x_e_illume_indicator_geometry_get(zone, &sx, &sy, &sw, &sh);
130         _conformant_part_size_set(obj, wd->shelf, sx, sy, sw, sh);
131      }
132    if (part_type & ELM_CONFORM_VIRTUAL_KEYPAD_PART)
133      {
134         ecore_x_e_illume_keyboard_geometry_get(zone, &sx, &sy, &sw, &sh);
135         _conformant_part_size_set(obj,wd->virtualkeypad, sx, sy, sw, sh);
136      }
137    if (part_type & ELM_CONFORM_SOFTKEY_PART)
138      {
139         ecore_x_e_illume_softkey_geometry_get(zone, &sx, &sy, &sw, &sh);
140         _conformant_part_size_set(obj, wd->panel, sx, sy, sw, sh);
141      }
142 #endif
143 }
144
145 static void
146 _swallow_conformant_parts(Evas_Object *obj)
147 {
148    Widget_Data *wd = elm_widget_data_get(obj);
149
150    wd->scroller = NULL;
151    if (!wd->shelf)
152      {
153         wd->shelf = evas_object_rectangle_add(evas_object_evas_get(obj));
154         elm_widget_sub_object_add(obj, wd->shelf);
155         evas_object_size_hint_min_set(wd->shelf, -1, 0);
156         evas_object_size_hint_max_set(wd->shelf, -1, 0);
157      }
158    else
159      _conformant_part_sizing_eval(obj, ELM_CONFORM_INDICATOR_PART);
160
161    evas_object_color_set(wd->shelf, 0, 0, 0, 0);
162    edje_object_part_swallow(wd->base, "elm.swallow.shelf", wd->shelf);
163
164    if (!wd->virtualkeypad)
165      {
166         wd->virtualkeypad = evas_object_rectangle_add(evas_object_evas_get(obj));
167         elm_widget_sub_object_add(obj, wd->virtualkeypad);
168         evas_object_size_hint_min_set(wd->virtualkeypad, -1, 0);
169         evas_object_size_hint_max_set(wd->virtualkeypad, -1, 0);
170      }
171    else
172      _conformant_part_sizing_eval(obj, ELM_CONFORM_VIRTUAL_KEYPAD_PART);
173
174    evas_object_color_set(wd->virtualkeypad, 0, 0, 0, 0);
175    edje_object_part_swallow(wd->base, "elm.swallow.virtualkeypad",
176                             wd->virtualkeypad);
177
178    if (!wd->panel)
179      {
180         wd->panel = evas_object_rectangle_add(evas_object_evas_get(obj));
181         elm_widget_sub_object_add(obj, wd->panel);
182         evas_object_size_hint_min_set(wd->panel, -1, 0);
183         evas_object_size_hint_max_set(wd->panel, -1, 0);
184      }
185    else
186      _conformant_part_sizing_eval(obj, ELM_CONFORM_SOFTKEY_PART);
187
188    evas_object_color_set(wd->panel, 0, 0, 0, 0);
189    edje_object_part_swallow(wd->base, "elm.swallow.panel", wd->panel);
190 }
191
192 static void
193 _changed_size_hints(void *data, Evas *e __UNUSED__, Evas_Object *obj __UNUSED__, void *event_info __UNUSED__)
194 {
195    Widget_Data *wd = elm_widget_data_get(data);
196
197    if (!wd) return;
198    _sizing_eval(data);
199 }
200
201 static void
202 _sub_del(void *data __UNUSED__, Evas_Object *obj, void *event_info)
203 {
204    Widget_Data *wd = elm_widget_data_get(obj);
205    Evas_Object *sub = event_info;
206
207    if (!wd) return;
208    if (sub == wd->content)
209      {
210         evas_object_event_callback_del_full(sub, EVAS_CALLBACK_CHANGED_SIZE_HINTS,
211                                             _changed_size_hints, obj);
212         wd->content = NULL;
213         _sizing_eval(obj);
214      }
215 }
216
217 /* unused now - but meant to be for making sure the focused widget is always
218  * visible when the vkbd comes and goes by moving the conformant obj (and thus
219  * its children) to  show the focused widget (and if focus changes follow)
220
221 static Evas_Object *
222 _focus_object_get(const Evas_Object *obj)
223 {
224    Evas_Object *win, *foc;
225    
226    win = elm_widget_top_get(obj);
227    if (!win) return NULL;
228    foc = elm_widget_top_get(win);
229 }
230
231 static void
232 _focus_object_region_get(const Evas_Object *obj, Evas_Coord *x, Evas_Coord *y, Evas_Coord *w, Evas_Coord *h)
233 {
234    evas_object_geometry_get(obj, x, y, w, h);
235 }
236
237 static void
238 _focus_change_del(void *data, Evas_Object *obj, void *event_info)
239 {
240    // called from toplevel when the focused window shanges
241 }
242
243 static void
244 _autoscroll_move(Evas_Object *obj)
245 {
246    // move conformant edje by delta to show focused widget
247 }
248
249 static void
250 _autoscroll_mode_enable(Evas_Object *obj)
251 {
252    // called when autoscroll mode should be on - content area smaller than
253    // its min size
254    // 1. get focused object
255    // 2. if not in visible conformant area calculate delta needed to
256    //    get it in
257    // 3. store delta and call _autoscroll_move() which either asanimates
258    //    or jumps right there
259 }
260
261 static void
262 _autoscroll_mode_disable(Evas_Object *obj)
263 {
264    // called when autoscroll mode should be off - set delta to 0 and
265    // call _autoscroll_move()
266 }
267 */
268
269 static void
270 _conformant_move_resize_event_cb(void *data, Evas *e, Evas_Object *obj,
271                                  void *event_info)
272 {
273    Conformant_Part_Type part_type;
274    Widget_Data *wd = elm_widget_data_get(obj);
275
276    if (!wd) return;
277    part_type =  (ELM_CONFORM_INDICATOR_PART |
278                  ELM_CONFORM_VIRTUAL_KEYPAD_PART |
279                  ELM_CONFORM_SOFTKEY_PART);
280    _conformant_part_sizing_eval(obj, part_type);
281 }
282
283 static void
284 _content_resize_event_cb(void *data, Evas *e, Evas_Object *obj,
285                          void *event_info)
286 {
287    Evas_Object *focus_obj;
288    Evas_Object *conformant = (Evas_Object *) data;
289    Widget_Data *wd = elm_widget_data_get(conformant);
290    if (!wd) return;
291    if (wd->vkb_state == ECORE_X_VIRTUAL_KEYBOARD_STATE_OFF) return;
292
293    focus_obj = elm_widget_focused_object_get(conformant);
294    if (focus_obj)
295    {
296       Evas_Coord x, y, w, h;
297
298       elm_widget_show_region_get(focus_obj, &x, &y, &w, &h);
299
300       if (h < _elm_config->finger_size)
301          h = _elm_config->finger_size;
302       else
303          h = 1 + h; //elm_widget_show_region_set expects some change, to redo the job.
304       elm_widget_show_region_set(focus_obj, x, y, w, h);
305    }
306 }
307
308 static void
309 _update_autoscroll_objs(void *data)
310 {
311    const char *type;
312    int i;
313    Evas_Object *sub, *top_scroller = NULL;
314    Evas_Object *conformant = (Evas_Object *) data;
315    Widget_Data *wd = elm_widget_data_get(data);
316
317    if (!wd) return;
318
319    sub = elm_widget_focused_object_get(conformant);
320    //Look up for Top most scroller in the Focus Object hierarchy inside Conformant.
321
322    while (sub)
323      {
324         type = elm_widget_type_get(sub);
325         if (!strcmp(type, "conformant")) break;
326         for (i = 0; i < SUB_TYPE_COUNT; i++)
327           if (!strcmp(type, sub_type[i]))
328                {
329                   top_scroller = sub;
330                   break;
331                }
332         sub = elm_object_parent_widget_get(sub);
333      }
334
335    //If the scroller got changed by app, replace it.
336    if (top_scroller != wd->scroller)
337      {
338         if (wd->scroller) evas_object_event_callback_del(wd->scroller,
339                                                    EVAS_CALLBACK_RESIZE,
340                                                    _content_resize_event_cb);
341         wd->scroller = top_scroller;
342         if (wd->scroller) evas_object_event_callback_add(wd->scroller,
343                                                    EVAS_CALLBACK_RESIZE,
344                                                    _content_resize_event_cb,
345                                                    data);
346      }
347 }
348
349 static Eina_Bool
350 _prop_change(void *data, int type __UNUSED__, void *event) 
351 {
352 #ifdef HAVE_ELEMENTARY_X
353    Ecore_X_Event_Window_Property *ev;
354    Widget_Data *wd = elm_widget_data_get(data);
355
356    if (!wd) return ECORE_CALLBACK_PASS_ON;
357    ev = event;
358    if (ev->atom == ECORE_X_ATOM_E_ILLUME_ZONE) 
359      {
360         Conformant_Part_Type part_type;
361
362         part_type =  (ELM_CONFORM_INDICATOR_PART |
363                       ELM_CONFORM_VIRTUAL_KEYPAD_PART |
364                       ELM_CONFORM_SOFTKEY_PART);
365         _conformant_part_sizing_eval(data, part_type);
366         evas_object_event_callback_add(data, EVAS_CALLBACK_RESIZE,
367                                        _conformant_move_resize_event_cb, data);
368         evas_object_event_callback_add(data, EVAS_CALLBACK_MOVE,
369                                        _conformant_move_resize_event_cb, data);
370      }
371    else if (ev->atom == ECORE_X_ATOM_E_ILLUME_INDICATOR_GEOMETRY) 
372      _conformant_part_sizing_eval(data, ELM_CONFORM_INDICATOR_PART);
373    else if (ev->atom == ECORE_X_ATOM_E_ILLUME_SOFTKEY_GEOMETRY) 
374      _conformant_part_sizing_eval(data, ELM_CONFORM_SOFTKEY_PART);
375    else if (ev->atom == ECORE_X_ATOM_E_ILLUME_KEYBOARD_GEOMETRY) 
376      _conformant_part_sizing_eval(data, ELM_CONFORM_VIRTUAL_KEYPAD_PART);
377    else if (ev->atom == ECORE_X_ATOM_E_VIRTUAL_KEYBOARD_STATE)
378      {
379         Ecore_X_Window zone;
380
381         zone = ecore_x_e_illume_zone_get(ev->win);
382         wd->vkb_state = ecore_x_e_virtual_keyboard_state_get(zone);
383         if (wd->vkb_state == ECORE_X_VIRTUAL_KEYBOARD_STATE_OFF)
384           {
385              evas_object_size_hint_min_set(wd->virtualkeypad, -1, 0);
386              evas_object_size_hint_max_set(wd->virtualkeypad, -1, 0);
387           }
388         else
389           _update_autoscroll_objs(data);
390      }
391 #endif
392
393    return ECORE_CALLBACK_PASS_ON;
394 }
395
396 /**
397  * Add a new Conformant object
398  * 
399  * @param parent The parent object
400  * @return The new conformant object or NULL if it cannot be created
401  * 
402  * @ingroup Conformant
403  */
404 EAPI Evas_Object *
405 elm_conformant_add(Evas_Object *parent) 
406 {
407    Evas_Object *obj;
408    Evas *evas;
409    Widget_Data *wd;
410
411    EINA_SAFETY_ON_NULL_RETURN_VAL(parent, NULL);
412
413    wd = ELM_NEW(Widget_Data);
414
415    evas = evas_object_evas_get(parent);
416    if (!evas) return NULL;
417
418    obj = elm_widget_add(evas);
419    ELM_SET_WIDTYPE(widtype, "conformant");
420    elm_widget_type_set(obj, "conformant");
421    elm_widget_sub_object_add(parent, obj);
422    elm_widget_data_set(obj, wd);
423    elm_widget_del_hook_set(obj, _del_hook);
424    elm_widget_theme_hook_set(obj, _theme_hook);
425    elm_widget_can_focus_set(obj, EINA_FALSE);
426
427    wd->base = edje_object_add(evas);
428    _elm_theme_object_set(obj, wd->base, "conformant", "base", "default");
429    elm_widget_resize_object_set(obj, wd->base);
430    _swallow_conformant_parts(obj);
431
432 #ifdef HAVE_ELEMENTARY_X
433    wd->prop_hdl = ecore_event_handler_add(ECORE_X_EVENT_WINDOW_PROPERTY,
434                                           _prop_change, obj);
435    wd->vkb_state = ECORE_X_VIRTUAL_KEYBOARD_STATE_OFF;
436 #endif
437
438    evas_object_smart_callback_add(obj, "sub-object-del", _sub_del, obj);
439
440    _sizing_eval(obj);
441    return obj;
442 }
443
444 /**
445  * Set the content of the conformant widget
446  *
447  * Once the content object is set, a previously set one will be deleted.
448  * If you want to keep that old content object, use the
449  * elm_conformat_content_unset() function.
450  *
451  * @param obj The conformant object
452  * @return The content that was being used
453  *
454  * @ingroup Conformant
455  */
456 EAPI void
457 elm_conformant_content_set(Evas_Object *obj, Evas_Object *content)
458 {
459    ELM_CHECK_WIDTYPE(obj, widtype);
460    Widget_Data *wd = elm_widget_data_get(obj);
461
462    if (!wd) return;
463    if (wd->content == content) return;
464    if (wd->content) evas_object_del(wd->content);
465    wd->content = content;
466    if (content)
467      {
468         elm_widget_sub_object_add(obj, content);
469         evas_object_event_callback_add(content, EVAS_CALLBACK_CHANGED_SIZE_HINTS,
470                                                       _changed_size_hints, obj);
471         edje_object_part_swallow(wd->base, "elm.swallow.content", content);
472      }
473    _sizing_eval(obj);
474 }
475
476 /**
477  * Get the content of the conformant widget
478  *
479  * Return the content object which is set for this widget;
480  *
481  * @param obj The conformant object
482  * @return The content that is being used
483  *
484  * @ingroup Conformant
485  */
486 EAPI Evas_Object *
487 elm_conformant_content_get(const Evas_Object *obj)
488 {
489    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
490    Widget_Data *wd = elm_widget_data_get(obj);
491
492    if (!wd) return NULL;
493    return wd->content;
494 }
495
496 /**
497  * Unset the content of the conformant widget
498  *
499  * Unparent and return the content object which was set for this widget;
500  *
501  * @param obj The conformant object
502  * @return The content that was being used
503  *
504  * @ingroup Conformant
505  */
506 EAPI Evas_Object *
507 elm_conformant_content_unset(Evas_Object *obj)
508 {
509    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
510    Widget_Data *wd = elm_widget_data_get(obj);
511    Evas_Object *content;
512
513    if (!wd) return NULL;
514    if (!wd->content) return NULL;
515    content = wd->content;
516    elm_widget_sub_object_del(obj, wd->content);
517    edje_object_part_unswallow(wd->base, wd->content);
518    wd->content = NULL;
519    return content;
520 }
521
522 /**
523  * Returns the Evas_Object that represents the content area.
524  *
525  * @param obj The conformant object.
526  * @return The content area of the widget.
527  *
528  * @ingroup Conformant
529  */
530 EAPI Evas_Object*
531 elm_conformant_content_area_get(Evas_Object *obj)
532 {
533    ELM_CHECK_WIDTYPE(obj, widtype) NULL;
534    Widget_Data *wd = elm_widget_data_get(obj);
535
536    if (!wd) return NULL;
537    /*Finger waggle warning*/
538    _elm_dangerous_call_check(__FUNCTION__);
539    return (Evas_Object*)edje_object_part_object_get(wd->base, "elm.swallow.content");
540 }
541