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