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