svn update: 51469 (latest:51480)
[framework/uifw/elementary.git] / src / lib / elm_widget.c
1 #include <Elementary.h>
2 #include "elm_priv.h"
3
4 static const char SMART_NAME[] = "elm_widget";
5
6 #define API_ENTRY \
7    Smart_Data *sd = evas_object_smart_data_get(obj); \
8    if ((!obj) || (!sd) || (!_elm_widget_is(obj)))
9 #define INTERNAL_ENTRY \
10    Smart_Data *sd = evas_object_smart_data_get(obj); \
11    if (!sd) return;
12
13 typedef struct _Smart_Data Smart_Data;
14
15 struct _Smart_Data
16 {
17    Evas_Object   *obj;
18    const char    *type;
19    Evas_Object   *parent_obj;
20    Evas_Coord     x, y, w, h;
21    Eina_List     *subobjs;
22    Evas_Object   *resize_obj;
23    Evas_Object   *hover_obj;
24    void         (*del_func) (Evas_Object *obj);
25    void         (*del_pre_func) (Evas_Object *obj);
26    void         (*focus_func) (Evas_Object *obj);
27    void         (*activate_func) (Evas_Object *obj);
28    void         (*disable_func) (Evas_Object *obj);
29    void         (*theme_func) (Evas_Object *obj);
30    void         (*signal_func) (Evas_Object *obj, const char *emission,
31                                 const char *source);
32    void         (*callback_add_func) (Evas_Object *obj, const char *emission,
33                                 const char *source, void (*func) (void *data,
34                                    Evas_Object *o, const char *emission,
35                                    const char *source), void *data);
36    void         *(*callback_del_func) (Evas_Object *obj, const char *emission,
37                                   const char *source, void (*func) (void *data,
38                                      Evas_Object *o, const char *emission,
39                                      const char *source));
40    void         (*changed_func) (Evas_Object *obj);
41    void         (*on_focus_func) (void *data, Evas_Object *obj);
42    void          *on_focus_data;
43    void         (*on_change_func) (void *data, Evas_Object *obj);
44    void          *on_change_data;
45    void         (*on_show_region_func) (void *data, Evas_Object *obj);
46    void          *on_show_region_data;
47    void          *data;
48    Evas_Coord     rx, ry, rw, rh;
49    int            scroll_hold;
50    int            scroll_freeze;
51    double         scale;
52    Elm_Theme     *theme;
53    const char    *style;
54    unsigned int   focus_order;
55    
56    int            child_drag_x_locked;
57    int            child_drag_y_locked;
58    Eina_Bool      drag_x_locked : 1;
59    Eina_Bool      drag_y_locked : 1;
60    
61    Eina_Bool      can_focus : 1;
62    Eina_Bool      child_can_focus : 1;
63    Eina_Bool      focused : 1;
64    Eina_Bool      disabled : 1;
65 };
66
67 /* local subsystem functions */
68 static void _smart_reconfigure(Smart_Data *sd);
69 static void _smart_add(Evas_Object *obj);
70 static void _smart_del(Evas_Object *obj);
71 static void _smart_move(Evas_Object *obj, Evas_Coord x, Evas_Coord y);
72 static void _smart_resize(Evas_Object *obj, Evas_Coord w, Evas_Coord h);
73 static void _smart_show(Evas_Object *obj);
74 static void _smart_hide(Evas_Object *obj);
75 static void _smart_color_set(Evas_Object *obj, int r, int g, int b, int a);
76 static void _smart_clip_set(Evas_Object *obj, Evas_Object * clip);
77 static void _smart_clip_unset(Evas_Object *obj);
78 static void _smart_calculate(Evas_Object *obj);
79 static void _smart_init(void);
80 static inline Eina_Bool _elm_widget_is(const Evas_Object *obj);
81
82 static void _if_focused_revert(Evas_Object *obj);
83
84 /* local subsystem globals */
85 static Evas_Smart *_e_smart = NULL;
86
87 static unsigned int focus_order = 0;
88
89 static void
90 _sub_obj_del(void *data, Evas *e __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__)
91 {
92    Smart_Data *sd = data;
93
94    if (obj == sd->resize_obj)
95      sd->resize_obj = NULL;
96    else if (obj == sd->hover_obj)
97      sd->hover_obj = NULL;
98    else
99      sd->subobjs = eina_list_remove(sd->subobjs, obj);
100    evas_object_smart_callback_call(sd->obj, "sub-object-del", obj);
101 }
102
103 static void
104 _sub_obj_mouse_down(void *data __UNUSED__, Evas *e __UNUSED__, Evas_Object *obj, void *event_info __UNUSED__)
105 {
106    Evas_Object *o = obj;
107    do 
108      {
109         if (_elm_widget_is(o)) break;
110         o = evas_object_smart_parent_get(o);
111      }
112    while (o);
113    if (!o) return;
114    if (!elm_widget_can_focus_get(o)) return;
115    elm_widget_focus_steal(o);
116 }
117
118 EAPI Evas_Object *
119 elm_widget_add(Evas *evas)
120 {
121    _smart_init();
122    return evas_object_smart_add(evas, _e_smart);
123 }
124
125 EAPI void
126 elm_widget_del_hook_set(Evas_Object *obj, void (*func) (Evas_Object *obj))
127 {
128    API_ENTRY return;
129    sd->del_func = func;
130 }
131
132 EAPI void
133 elm_widget_del_pre_hook_set(Evas_Object *obj, void (*func) (Evas_Object *obj))
134 {
135    API_ENTRY return;
136    sd->del_pre_func = func;
137 }
138
139 EAPI void
140 elm_widget_focus_hook_set(Evas_Object *obj, void (*func) (Evas_Object *obj))
141 {
142    API_ENTRY return;
143    sd->focus_func = func;
144 }
145
146 EAPI void
147 elm_widget_activate_hook_set(Evas_Object *obj, void (*func) (Evas_Object *obj))
148 {
149    API_ENTRY return;
150    sd->activate_func = func;
151 }
152
153 EAPI void
154 elm_widget_disable_hook_set(Evas_Object *obj, void (*func) (Evas_Object *obj))
155 {
156    API_ENTRY return;
157    sd->disable_func = func;
158 }
159
160 EAPI void
161 elm_widget_theme_hook_set(Evas_Object *obj, void (*func) (Evas_Object *obj))
162 {
163    API_ENTRY return;
164    sd->theme_func = func;
165 }
166
167 EAPI void
168 elm_widget_changed_hook_set(Evas_Object *obj, void (*func) (Evas_Object *obj))
169 {
170    API_ENTRY return;
171    sd->changed_func = func;
172 }
173
174 EAPI void
175 elm_widget_signal_emit_hook_set(Evas_Object *obj, void (*func) (Evas_Object *obj, const char *emission, const char *source))
176 {
177    API_ENTRY return;
178    sd->signal_func = func;
179 }
180
181 EAPI void
182 elm_widget_signal_callback_add_hook_set(Evas_Object *obj, void (*func) (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))
183 {
184    API_ENTRY return;
185    sd->callback_add_func = func;
186 }
187
188 EAPI void
189 elm_widget_signal_callback_del_hook_set(Evas_Object *obj, void *(*func) (Evas_Object *obj, const char *emission, const char *source, void (*func_cb) (void *data, Evas_Object *o, const char *emission, const char *source)))
190 {
191    API_ENTRY return;
192    sd->callback_del_func = func;
193 }
194
195 EAPI void
196 elm_widget_theme(Evas_Object *obj)
197 {
198    const Eina_List *l;
199    Evas_Object *child;
200
201    API_ENTRY return;
202    EINA_LIST_FOREACH(sd->subobjs, l, child)
203      elm_widget_theme(child);
204    if (sd->resize_obj) elm_widget_theme(sd->resize_obj);
205    if (sd->hover_obj) elm_widget_theme(sd->hover_obj);
206    if (sd->theme_func) sd->theme_func(obj);
207 }
208
209 EAPI void
210 elm_widget_on_focus_hook_set(Evas_Object *obj, void (*func) (void *data, Evas_Object *obj), void *data)
211 {
212    API_ENTRY return;
213    sd->on_focus_func = func;
214    sd->on_focus_data = data;
215 }
216
217 EAPI void
218 elm_widget_on_change_hook_set(Evas_Object *obj, void (*func) (void *data, Evas_Object *obj), void *data)
219 {
220    API_ENTRY return;
221    sd->on_change_func = func;
222    sd->on_change_data = data;
223 }
224
225 EAPI void
226 elm_widget_on_show_region_hook_set(Evas_Object *obj, void (*func) (void *data, Evas_Object *obj), void *data)
227 {
228    API_ENTRY return;
229    sd->on_show_region_func = func;
230    sd->on_show_region_data = data;
231 }
232
233 EAPI void
234 elm_widget_data_set(Evas_Object *obj, void *data)
235 {
236    API_ENTRY return;
237    sd->data = data;
238 }
239
240 EAPI void *
241 elm_widget_data_get(const Evas_Object *obj)
242 {
243    API_ENTRY return NULL;
244    return sd->data;
245 }
246
247 EAPI void
248 elm_widget_sub_object_add(Evas_Object *obj, Evas_Object *sobj)
249 {
250    API_ENTRY return;
251    double scale, pscale = elm_widget_scale_get(sobj);
252    Elm_Theme *th, *pth = elm_widget_theme_get(sobj);
253
254    sd->subobjs = eina_list_append(sd->subobjs, sobj);
255    if (!sd->child_can_focus)
256      {
257         if (elm_widget_can_focus_get(sobj)) sd->child_can_focus = 1;
258      }
259    if (_elm_widget_is(sobj))
260      {
261         Smart_Data *sd2 = evas_object_smart_data_get(sobj);
262         if (sd2)
263           {
264              if (sd2->parent_obj)
265                elm_widget_sub_object_del(sd2->parent_obj, sobj);
266              sd2->parent_obj = obj;
267           }
268      }
269    evas_object_data_set(sobj, "elm-parent", obj);
270    evas_object_event_callback_add(sobj, EVAS_CALLBACK_DEL, _sub_obj_del, sd);
271    evas_object_smart_callback_call(obj, "sub-object-add", sobj);
272    scale = elm_widget_scale_get(sobj);
273    th = elm_widget_theme_get(sobj);
274    if ((scale != pscale) || (th != pth)) elm_widget_theme(sobj);
275 }
276
277 EAPI void
278 elm_widget_sub_object_del(Evas_Object *obj, Evas_Object *sobj)
279 {
280    Evas_Object *sobj_parent;
281    API_ENTRY return;
282    if (!sobj) return;
283
284    sobj_parent = evas_object_data_del(sobj, "elm-parent");
285    if (sobj_parent != obj)
286      {
287         static int abort_on_warn = -1;
288         ERR("removing sub object %p from parent %p, "
289             "but elm-parent is different %p!",
290             sobj, obj, sobj_parent);
291         if (EINA_UNLIKELY(abort_on_warn == -1))
292           {
293              if (getenv("ELM_ERROR_ABORT")) abort_on_warn = 1;
294              else abort_on_warn = 0;
295           }
296         if (abort_on_warn == 1) abort();
297      }
298    sd->subobjs = eina_list_remove(sd->subobjs, sobj);
299    if (!sd->child_can_focus)
300      {
301         if (elm_widget_can_focus_get(sobj)) sd->child_can_focus = 0;
302      }
303    if (_elm_widget_is(sobj))
304      {
305         Smart_Data *sd2 = evas_object_smart_data_get(sobj);
306         if (sd2) sd2->parent_obj = NULL;
307      }
308    evas_object_event_callback_del_full(sobj, EVAS_CALLBACK_DEL, _sub_obj_del, sd);
309    evas_object_smart_callback_call(obj, "sub-object-del", sobj);
310 }
311
312 EAPI void
313 elm_widget_resize_object_set(Evas_Object *obj, Evas_Object *sobj)
314 {
315    API_ENTRY return;
316    if (sd->resize_obj)
317      {
318         evas_object_data_del(sd->resize_obj, "elm-parent");
319         if (_elm_widget_is(sd->resize_obj))
320           {
321              Smart_Data *sd2 = evas_object_smart_data_get(sd->resize_obj);
322              if (sd2) sd2->parent_obj = NULL;
323           }
324         evas_object_event_callback_del_full(sd->resize_obj, EVAS_CALLBACK_DEL,
325            _sub_obj_del, sd);
326         evas_object_event_callback_del_full(sd->resize_obj, EVAS_CALLBACK_MOUSE_DOWN,
327            _sub_obj_mouse_down, sd);
328         evas_object_smart_member_del(sd->resize_obj);
329      }
330    sd->resize_obj = sobj;
331    if (sd->resize_obj)
332      {
333         if (_elm_widget_is(sd->resize_obj))
334           {
335              Smart_Data *sd2 = evas_object_smart_data_get(sd->resize_obj);
336              if (sd2) sd2->parent_obj = obj;
337           }
338         evas_object_clip_set(sobj, evas_object_clip_get(obj));
339         evas_object_smart_member_add(sobj, obj);
340         evas_object_event_callback_add(sobj, EVAS_CALLBACK_DEL, _sub_obj_del, sd);
341         evas_object_event_callback_add(sobj, EVAS_CALLBACK_MOUSE_DOWN,
342                                        _sub_obj_mouse_down, sd);
343         _smart_reconfigure(sd);
344         evas_object_data_set(sobj, "elm-parent", obj);
345         evas_object_smart_callback_call(obj, "sub-object-add", sobj);
346      }
347 }
348
349 EAPI void
350 elm_widget_hover_object_set(Evas_Object *obj, Evas_Object *sobj)
351 {
352    API_ENTRY return;
353    if (sd->hover_obj)
354      {
355         evas_object_event_callback_del_full(sd->hover_obj, EVAS_CALLBACK_DEL,
356            _sub_obj_del, sd);
357      }
358    sd->hover_obj = sobj;
359    if (sd->hover_obj)
360      {
361         evas_object_event_callback_add(sobj, EVAS_CALLBACK_DEL, _sub_obj_del, sd);
362         _smart_reconfigure(sd);
363      }
364 }
365
366 EAPI void
367 elm_widget_can_focus_set(Evas_Object *obj, int can_focus)
368 {
369    API_ENTRY return;
370    sd->can_focus = can_focus;
371 }
372
373 EAPI int
374 elm_widget_can_focus_get(const Evas_Object *obj)
375 {
376    API_ENTRY return 0;
377    if (sd->can_focus) return 1;
378    if (sd->child_can_focus) return 1;
379    return 0;
380 }
381
382 EAPI int
383 elm_widget_focus_get(const Evas_Object *obj)
384 {
385    API_ENTRY return 0;
386    return sd->focused;
387 }
388
389 EAPI Evas_Object *
390 elm_widget_focused_object_get(const Evas_Object *obj)
391 {
392    const Evas_Object *subobj;
393    const Eina_List *l;
394    API_ENTRY return NULL;
395
396    if (!sd->focused) return NULL;
397    EINA_LIST_FOREACH(sd->subobjs, l, subobj)
398      {
399         Evas_Object *fobj = elm_widget_focused_object_get(subobj);
400         if (fobj) return fobj;
401      }
402    return (Evas_Object *)obj;
403 }
404
405 EAPI Evas_Object *
406 elm_widget_top_get(const Evas_Object *obj)
407 {
408 #if 1 // strict way  
409    API_ENTRY return NULL;
410    if (sd->parent_obj) return elm_widget_top_get(sd->parent_obj);
411    return (Evas_Object *)obj;
412 #else // loose way
413    Smart_Data *sd = evas_object_smart_data_get(obj);
414    Evas_Object *par;
415    
416    if (!obj) return NULL;
417    if ((sd) && _elm_widget_is(obj))
418      {
419         if ((sd->type) && (!strcmp(sd->type, "win"))) 
420           return (Evas_Object *)obj;
421         if (sd->parent_obj)
422           return elm_widget_top_get(sd->parent_obj);
423      }
424    par = evas_object_smart_parent_get(obj);
425    if (!par) return (Evas_Object *)obj;
426    return elm_widget_top_get(par);
427 #endif   
428 }
429
430 EAPI Eina_Bool
431 elm_widget_is(const Evas_Object *obj)
432 {
433    return _elm_widget_is(obj);
434 }
435
436 EAPI Evas_Object *
437 elm_widget_parent_widget_get(const Evas_Object *obj)
438 {
439    Evas_Object *parent;
440
441    if (_elm_widget_is(obj))
442      {
443         Smart_Data *sd = evas_object_smart_data_get(obj);
444         if (!sd) return NULL;
445         parent = sd->parent_obj;
446      }
447    else
448      {
449         parent = evas_object_data_get(obj, "elm-parent");
450         if (!parent)
451           parent = evas_object_smart_data_get(obj);
452      }
453
454    while (parent)
455      {
456         Evas_Object *elm_parent;
457         if (_elm_widget_is(parent)) break;
458         elm_parent = evas_object_data_get(parent, "elm-parent");
459         if (elm_parent)
460           parent = elm_parent;
461         else
462           parent = evas_object_smart_parent_get(parent);
463      }
464    return parent;
465 }
466
467 EAPI int
468 elm_widget_focus_jump(Evas_Object *obj, int forward)
469 {
470    API_ENTRY return 0;
471    if (!elm_widget_can_focus_get(obj)) return 0;
472
473    /* if it has a focus func its an end-point widget like a button */
474    if (sd->focus_func)
475      {
476         if (!sd->focused)
477           {
478              focus_order++;
479              sd->focus_order = focus_order;
480              sd->focused = 1;
481           }
482         else sd->focused = 0;
483         if (sd->on_focus_func) sd->on_focus_func(sd->on_focus_data, obj);
484         sd->focus_func(obj);
485         return sd->focused;
486      }
487    /* its some container */
488    else
489      {
490         int focus_next;
491         int noloop = 0;
492
493         focus_next = 0;
494         if (!sd->focused)
495           {
496              elm_widget_focus_set(obj, forward);
497              return 1;
498           }
499         else
500           {
501              if (forward)
502                {
503                   if (elm_widget_can_focus_get(sd->resize_obj))
504                     {
505                        if ((focus_next) &&
506                            (!elm_widget_disabled_get(sd->resize_obj)))
507                          {
508                             /* the previous focused item was unfocused - so focus
509                              * the next one (that can be focused) */
510                             if (elm_widget_focus_jump(sd->resize_obj, forward))
511                               return 1;
512                             else noloop = 1;
513                          }
514                        else
515                          {
516                             if (elm_widget_focus_get(sd->resize_obj))
517                               {
518                                  /* jump to the next focused item or focus this item */
519                                  if (elm_widget_focus_jump(sd->resize_obj, forward))
520                                    return 1;
521                                  /* it returned 0 - it got to the last item and is past it */
522                                  focus_next = 1;
523                               }
524                          }
525                     }
526                   if (!noloop)
527                     {
528                        const Eina_List *l;
529                        Evas_Object *child;
530                        EINA_LIST_FOREACH(sd->subobjs, l, child)
531                          {
532                             if (elm_widget_can_focus_get(child))
533                               {
534                                  if ((focus_next) &&
535                                      (!elm_widget_disabled_get(child)))
536                                    {
537                                       /* the previous focused item was unfocused - so focus
538                                        * the next one (that can be focused) */
539                                       if (elm_widget_focus_jump(child, forward))
540                                         return 1;
541                                       else break;
542                                    }
543                                  else
544                                    {
545                                       if (elm_widget_focus_get(child))
546                                         {
547                                            /* jump to the next focused item or focus this item */
548                                            if (elm_widget_focus_jump(child, forward))
549                                              return 1;
550                                            /* it returned 0 - it got to the last item and is past it */
551                                            focus_next = 1;
552                                         }
553                                    }
554                               }
555                          }
556                     }
557                }
558              else
559                {
560                   const Eina_List *l;
561                   Evas_Object *child;
562
563                   EINA_LIST_REVERSE_FOREACH(sd->subobjs, l, child)
564                     {
565                        if (elm_widget_can_focus_get(child))
566                          {
567                             if ((focus_next) &&
568                                 (!elm_widget_disabled_get(child)))
569                               {
570                                  /* the previous focused item was unfocused - so focus
571                                   * the next one (that can be focused) */
572                                  if (elm_widget_focus_jump(child, forward))
573                                    return 1;
574                                  else break;
575                               }
576                             else
577                               {
578                                  if (elm_widget_focus_get(child))
579                                    {
580                                       /* jump to the next focused item or focus this item */
581                                       if (elm_widget_focus_jump(child, forward))
582                                         return 1;
583                                       /* it returned 0 - it got to the last item and is past it */
584                                       focus_next = 1;
585                                    }
586                               }
587                          }
588                     }
589                   if (!l)
590                     {
591                        if (elm_widget_can_focus_get(sd->resize_obj))
592                          {
593                             if ((focus_next) &&
594                                 (!elm_widget_disabled_get(sd->resize_obj)))
595                               {
596                                  /* the previous focused item was unfocused - so focus
597                                   * the next one (that can be focused) */
598                                  if (elm_widget_focus_jump(sd->resize_obj, forward))
599                                    return 1;
600                               }
601                             else
602                               {
603                                  if (elm_widget_focus_get(sd->resize_obj))
604                                    {
605                                       /* jump to the next focused item or focus this item */
606                                       if (elm_widget_focus_jump(sd->resize_obj, forward))
607                                         return 1;
608                                       /* it returned 0 - it got to the last item and is past it */
609                                       focus_next = 1;
610                                    }
611                               }
612                          }
613                     }
614                }
615           }
616      }
617    /* no next item can be focused */
618    if (sd->focused)
619      {
620         sd->focused = 0;
621         if (sd->on_focus_func) sd->on_focus_func(sd->on_focus_data, obj);
622      }
623    return 0;
624 }
625
626 EAPI void
627 elm_widget_signal_emit(Evas_Object *obj, const char *emission, const char *source)
628 {
629    API_ENTRY return;
630    if (!sd->signal_func) return;
631    sd->signal_func(obj, emission, source);
632 }
633
634 EAPI void
635 elm_widget_signal_callback_add(Evas_Object *obj, const char *emission, const char *source, void (*func) (void *data, Evas_Object *o, const char *emission, const char *source), void *data)
636 {
637    API_ENTRY return;
638    if (!sd->callback_add_func) return;
639    sd->callback_add_func(obj, emission, source, func, data);
640 }
641
642 EAPI void *
643 elm_widget_signal_callback_del(Evas_Object *obj, const char *emission, const char *source, void (*func) (void *data, Evas_Object *o, const char *emission, const char *source))
644 {
645    API_ENTRY return NULL;
646    if (!sd->callback_del_func) return NULL;
647    return sd->callback_del_func(obj, emission, source, func);
648 }
649
650 EAPI void
651 elm_widget_focus_set(Evas_Object *obj, int first)
652 {
653    API_ENTRY return;
654    if (!sd->focused)
655      {
656         focus_order++;
657         sd->focus_order = focus_order;
658         sd->focused = 1;
659         if (sd->on_focus_func) sd->on_focus_func(sd->on_focus_data, obj);
660      }
661    if (sd->focus_func)
662      {
663         sd->focus_func(obj);
664         return;
665      }
666    else
667      {
668         if (first)
669           {
670              if ((elm_widget_can_focus_get(sd->resize_obj)) &&
671                  (!elm_widget_disabled_get(sd->resize_obj)))
672                {
673                   elm_widget_focus_set(sd->resize_obj, first);
674                }
675              else
676                {
677                   const Eina_List *l;
678                   Evas_Object *child;
679                   EINA_LIST_FOREACH(sd->subobjs, l, child)
680                     {
681                        if ((elm_widget_can_focus_get(child)) &&
682                            (!elm_widget_disabled_get(child)))
683                          {
684                             elm_widget_focus_set(child, first);
685                             break;
686                          }
687                     }
688                }
689           }
690         else
691           {
692              const Eina_List *l;
693              Evas_Object *child;
694              EINA_LIST_REVERSE_FOREACH(sd->subobjs, l, child)
695                {
696                   if ((elm_widget_can_focus_get(child)) &&
697                       (!elm_widget_disabled_get(child)))
698                     {
699                        elm_widget_focus_set(child, first);
700                        break;
701                     }
702                }
703              if (!l)
704                {
705                   if ((elm_widget_can_focus_get(sd->resize_obj)) &&
706                       (!elm_widget_disabled_get(sd->resize_obj)))
707                     {
708                        elm_widget_focus_set(sd->resize_obj, first);
709                     }
710                }
711           }
712      }
713 }
714
715 EAPI Evas_Object *
716 elm_widget_parent_get(const Evas_Object *obj)
717 {
718    API_ENTRY return NULL;
719    return sd->parent_obj;
720 }
721
722 EAPI void
723 elm_widget_focused_object_clear(Evas_Object *obj)
724 {
725    API_ENTRY return;
726    if (!sd->focused) return;
727    if (elm_widget_focus_get(sd->resize_obj))
728      elm_widget_focused_object_clear(sd->resize_obj);
729    else
730      {
731         const Eina_List *l;
732         Evas_Object *child;
733         EINA_LIST_FOREACH(sd->subobjs, l, child)
734           {
735              if (elm_widget_focus_get(child))
736                {
737                   elm_widget_focused_object_clear(child);
738                   break;
739                }
740           }
741      }
742    sd->focused = 0;
743    if (sd->on_focus_func) sd->on_focus_func(sd->on_focus_data, obj);
744    if (sd->focus_func) sd->focus_func(obj);
745 }
746
747 static void
748 _elm_widget_parent_focus(Evas_Object *obj)
749 {
750    API_ENTRY return;
751    Evas_Object *o = elm_widget_parent_get(obj);
752
753    if (sd->focused) return;
754    if (o) _elm_widget_parent_focus(o);
755    focus_order++;
756    sd->focus_order = focus_order;
757    sd->focused = 1;
758    if (sd->on_focus_func) sd->on_focus_func(sd->on_focus_data, obj);
759    if (sd->focus_func) sd->focus_func(obj);
760 }
761
762 EAPI void
763 elm_widget_focus_steal(Evas_Object *obj)
764 {
765    Evas_Object *parent, *o;
766    API_ENTRY return;
767
768    if (sd->focused) return;
769    if (sd->disabled) return;
770    parent = obj;
771    for (;;)
772      {
773         o = elm_widget_parent_get(parent);
774         if (!o) break;
775         sd = evas_object_smart_data_get(o);
776         if (sd->focused) break;
777         parent = o;
778      }
779    if (!elm_widget_parent_get(parent))
780      elm_widget_focused_object_clear(parent);
781    else
782      {
783         parent = elm_widget_parent_get(parent);
784         sd = evas_object_smart_data_get(parent);
785         if (elm_widget_focus_get(sd->resize_obj))
786           {
787              elm_widget_focused_object_clear(sd->resize_obj);
788           }
789         else
790           {
791              const Eina_List *l;
792              Evas_Object *child;
793              EINA_LIST_FOREACH(sd->subobjs, l, child)
794                {
795                   if (elm_widget_focus_get(child))
796                     {
797                        elm_widget_focused_object_clear(child);
798                        break;
799                     }
800                }
801           }
802      }
803    _elm_widget_parent_focus(obj);
804    return;
805 }
806
807 EAPI void
808 elm_widget_activate(Evas_Object *obj)
809 {
810    API_ENTRY return;
811    elm_widget_change(obj);
812    if (sd->activate_func) sd->activate_func(obj);
813 }
814
815 EAPI void
816 elm_widget_change(Evas_Object *obj)
817 {
818    API_ENTRY return;
819    elm_widget_change(elm_widget_parent_get(obj));
820    if (sd->on_change_func) sd->on_change_func(sd->on_change_data, obj);
821 }
822
823 EAPI void
824 elm_widget_disabled_set(Evas_Object *obj, int disabled)
825 {
826    API_ENTRY return;
827
828    if (sd->disabled == disabled) return;
829    sd->disabled = disabled;
830    if (sd->focused)
831      {
832         Evas_Object *o, *parent;
833
834         parent = obj;
835         for (;;)
836           {
837              o = elm_widget_parent_get(parent);
838              if (!o) break;
839              parent = o;
840           }
841         elm_widget_focus_jump(parent, 1);
842      }
843    if (sd->disable_func) sd->disable_func(obj);
844 }
845
846 EAPI int
847 elm_widget_disabled_get(const Evas_Object *obj)
848 {
849    API_ENTRY return 0;
850    return sd->disabled;
851 }
852
853 EAPI void
854 elm_widget_show_region_set(Evas_Object *obj, Evas_Coord x, Evas_Coord y, Evas_Coord w, Evas_Coord h)
855 {
856    API_ENTRY return;
857    if ((x == sd->rx) && (y == sd->ry) && (w == sd->rw) && (h == sd->rh)) return;
858    sd->rx = x;
859    sd->ry = y;
860    sd->rw = w;
861    sd->rh = h;
862    if (sd->on_show_region_func)
863      sd->on_show_region_func(sd->on_show_region_data, obj);
864 }
865
866 EAPI void
867 elm_widget_show_region_get(const Evas_Object *obj, Evas_Coord *x, Evas_Coord *y, Evas_Coord *w, Evas_Coord *h)
868 {
869    API_ENTRY return;
870    if (x) *x = sd->rx;
871    if (y) *y = sd->ry;
872    if (w) *w = sd->rw;
873    if (h) *h = sd->rh;
874 }
875
876 EAPI void
877 elm_widget_scroll_hold_push(Evas_Object *obj)
878 {
879    API_ENTRY return;
880    sd->scroll_hold++;
881    if (sd->scroll_hold == 1)
882      evas_object_smart_callback_call(obj, "scroll-hold-on", obj);
883    if (sd->parent_obj) elm_widget_scroll_hold_push(sd->parent_obj);
884    // FIXME: on delete/reparent hold pop
885 }
886
887 EAPI void
888 elm_widget_scroll_hold_pop(Evas_Object *obj)
889 {
890    API_ENTRY return;
891    sd->scroll_hold--;
892    if (sd->scroll_hold < 0) sd->scroll_hold = 0;
893    if (sd->scroll_hold == 0)
894      evas_object_smart_callback_call(obj, "scroll-hold-off", obj);
895    if (sd->parent_obj) elm_widget_scroll_hold_pop(sd->parent_obj);
896 }
897
898 EAPI int
899 elm_widget_scroll_hold_get(const Evas_Object *obj)
900 {
901    API_ENTRY return 0;
902    return sd->scroll_hold;
903 }
904
905 EAPI void
906 elm_widget_scroll_freeze_push(Evas_Object *obj)
907 {
908    API_ENTRY return;
909    sd->scroll_freeze++;
910    if (sd->scroll_freeze == 1)
911      evas_object_smart_callback_call(obj, "scroll-freeze-on", obj);
912    if (sd->parent_obj) elm_widget_scroll_freeze_push(sd->parent_obj);
913    // FIXME: on delete/reparent freeze pop
914 }
915
916 EAPI void
917 elm_widget_scroll_freeze_pop(Evas_Object *obj)
918 {
919    API_ENTRY return;
920    sd->scroll_freeze--;
921    if (sd->scroll_freeze < 0) sd->scroll_freeze = 0;
922    if (sd->scroll_freeze == 0)
923      evas_object_smart_callback_call(obj, "scroll-freeze-off", obj);
924    if (sd->parent_obj) elm_widget_scroll_freeze_pop(sd->parent_obj);
925 }
926
927 EAPI int
928 elm_widget_scroll_freeze_get(const Evas_Object *obj)
929 {
930    API_ENTRY return 0;
931    return sd->scroll_freeze;
932 }
933
934 EAPI void
935 elm_widget_scale_set(Evas_Object *obj, double scale)
936 {
937    API_ENTRY return;
938    if (scale <= 0.0) scale = 0.0;
939    if (sd->scale != scale)
940      {
941         sd->scale = scale;
942         elm_widget_theme(obj);
943      }
944 }
945
946 EAPI double
947 elm_widget_scale_get(const Evas_Object *obj)
948 {
949    API_ENTRY return 1.0;
950    // FIXME: save walking up the tree by storing/caching parent scale
951    if (sd->scale == 0.0)
952      {
953         if (sd->parent_obj)
954           return elm_widget_scale_get(sd->parent_obj);
955         else
956           return 1.0;
957      }
958    return sd->scale;
959 }
960
961 EAPI void
962 elm_widget_theme_set(Evas_Object *obj, Elm_Theme *th)
963 {
964    API_ENTRY return;
965    if (sd->theme != th)
966      {
967         if (sd->theme) elm_theme_free(sd->theme);
968         sd->theme = th;
969         if (th) th->ref++;
970         elm_widget_theme(obj);
971      }
972 }
973
974 EAPI Elm_Theme *
975 elm_widget_theme_get(const Evas_Object *obj)
976 {
977    API_ENTRY return NULL;
978    if (!sd->theme)
979      {
980         if (sd->parent_obj)
981           return elm_widget_theme_get(sd->parent_obj);
982         else
983           return NULL;
984      }
985    return sd->theme;
986 }
987
988 EAPI void
989 elm_widget_style_set(Evas_Object *obj, const char *style)
990 {
991    API_ENTRY return;
992
993    if (eina_stringshare_replace(&sd->style, style))
994      elm_widget_theme(obj);
995 }
996
997 EAPI const char *
998 elm_widget_style_get(const Evas_Object *obj)
999 {
1000    API_ENTRY return "";
1001    if (sd->style) return sd->style;
1002    return "default";
1003 }
1004
1005 EAPI void
1006 elm_widget_type_set(Evas_Object *obj, const char *type)
1007 {
1008    API_ENTRY return;
1009    eina_stringshare_replace(&sd->type, type);
1010 }
1011
1012 EAPI const char *
1013 elm_widget_type_get(const Evas_Object *obj)
1014 {
1015    API_ENTRY return "";
1016    if (sd->type) return sd->type;
1017    return "";
1018 }
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028 static void
1029 _propagate_x_drag_lock(Evas_Object *obj, int dir)
1030 {
1031    Smart_Data *sd = evas_object_smart_data_get(obj);
1032    if (sd->parent_obj)
1033      {
1034         Smart_Data *sd2 = evas_object_smart_data_get(sd->parent_obj);
1035         if (sd2)
1036           {
1037              sd2->child_drag_x_locked += dir;
1038              _propagate_x_drag_lock(sd->parent_obj, dir);
1039           }
1040      }
1041 }
1042
1043 static void
1044 _propagate_y_drag_lock(Evas_Object *obj, int dir)
1045 {
1046    Smart_Data *sd = evas_object_smart_data_get(obj);
1047    if (sd->parent_obj)
1048      {
1049         Smart_Data *sd2 = evas_object_smart_data_get(sd->parent_obj);
1050         if (sd2)
1051           {
1052              sd2->child_drag_y_locked += dir;
1053              _propagate_y_drag_lock(sd->parent_obj, dir);
1054           }
1055      }
1056 }
1057
1058 EAPI void
1059 elm_widget_drag_lock_x_set(Evas_Object *obj, Eina_Bool lock)
1060 {
1061    API_ENTRY return;
1062    if (sd->drag_x_locked == lock) return;
1063    sd->drag_x_locked = lock;
1064    if (sd->drag_x_locked) _propagate_x_drag_lock(obj, 1);
1065    else _propagate_x_drag_lock(obj, -1);
1066 }
1067
1068 EAPI void
1069 elm_widget_drag_lock_y_set(Evas_Object *obj, Eina_Bool lock)
1070 {
1071    API_ENTRY return;
1072    if (sd->drag_y_locked == lock) return;
1073    sd->drag_y_locked = lock;
1074    if (sd->drag_y_locked) _propagate_y_drag_lock(obj, 1);
1075    else _propagate_y_drag_lock(obj, -1);
1076 }
1077
1078 EAPI Eina_Bool
1079 elm_widget_drag_lock_x_get(const Evas_Object *obj)
1080 {
1081    API_ENTRY return EINA_FALSE;
1082    return sd->drag_x_locked;
1083 }
1084
1085 EAPI Eina_Bool
1086 elm_widget_drag_lock_y_get(const Evas_Object *obj)
1087 {
1088    API_ENTRY return EINA_FALSE;
1089    return sd->drag_y_locked;
1090 }
1091
1092 EAPI int
1093 elm_widget_drag_child_locked_x_get(const Evas_Object *obj)
1094 {
1095    API_ENTRY return 0;
1096    return sd->child_drag_x_locked;
1097 }
1098
1099 EAPI int
1100 elm_widget_drag_child_locked_y_get(const Evas_Object *obj)
1101 {
1102    API_ENTRY return 0;
1103    return sd->child_drag_y_locked;
1104 }
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116 /* local subsystem functions */
1117 static void
1118 _smart_reconfigure(Smart_Data *sd)
1119 {
1120    if (sd->resize_obj)
1121      {
1122         evas_object_move(sd->resize_obj, sd->x, sd->y);
1123         evas_object_resize(sd->resize_obj, sd->w, sd->h);
1124      }
1125    if (sd->hover_obj)
1126      {
1127         evas_object_move(sd->hover_obj, sd->x, sd->y);
1128         evas_object_resize(sd->hover_obj, sd->w, sd->h);
1129      }
1130 }
1131
1132 static void
1133 _smart_add(Evas_Object *obj)
1134 {
1135    Smart_Data *sd;
1136
1137    sd = calloc(1, sizeof(Smart_Data));
1138    if (!sd) return;
1139    sd->obj = obj;
1140    sd->x = 0;
1141    sd->y = 0;
1142    sd->w = 0;
1143    sd->h = 0;
1144    sd->can_focus = 1;
1145    evas_object_smart_data_set(obj, sd);
1146 }
1147
1148 static Evas_Object *
1149 _newest_focus_order_get(Evas_Object *obj, unsigned int *newest_focus_order)
1150 {
1151    const Eina_List *l;
1152    Evas_Object *child, *ret, *best;
1153    
1154    API_ENTRY return NULL;
1155    if (!evas_object_visible_get(obj)) return NULL;
1156    best = NULL;
1157    if (*newest_focus_order < sd->focus_order)
1158      {
1159         *newest_focus_order = sd->focus_order;
1160         best = obj;
1161      }
1162    EINA_LIST_FOREACH(sd->subobjs, l, child)
1163      {
1164         ret = _newest_focus_order_get(child, newest_focus_order);
1165         if (!ret) continue;
1166         best = ret;
1167      }
1168    return best;
1169 }
1170
1171 static void
1172 _if_focused_revert(Evas_Object *obj)
1173 {
1174    Evas_Object *top;
1175    Evas_Object *newest = NULL;
1176    unsigned int newest_focus_order = 0;
1177    
1178    INTERNAL_ENTRY;
1179    
1180    if (!sd->focused) return;
1181    if (!sd->parent_obj) return;
1182
1183    top = elm_widget_top_get(sd->parent_obj);
1184    if (top)
1185      {
1186         newest = _newest_focus_order_get(top, &newest_focus_order);
1187         if (newest)
1188           {
1189              elm_object_unfocus(newest);
1190              elm_object_focus(newest);
1191           }
1192      }
1193 }
1194
1195 static void
1196 _smart_del(Evas_Object *obj)
1197 {
1198    Evas_Object *sobj;
1199
1200    INTERNAL_ENTRY;
1201    if (sd->del_pre_func) sd->del_pre_func(obj);
1202    if (sd->resize_obj)
1203      {
1204         sobj = sd->resize_obj;
1205         sd->resize_obj = NULL;
1206         evas_object_event_callback_del_full(sobj, EVAS_CALLBACK_DEL, _sub_obj_del, sd);
1207         evas_object_smart_callback_call(sd->obj, "sub-object-del", sobj);
1208         evas_object_del(sobj);
1209      }
1210    if (sd->hover_obj)
1211      {
1212         sobj = sd->hover_obj;
1213         sd->hover_obj = NULL;
1214         evas_object_event_callback_del_full(sobj, EVAS_CALLBACK_DEL, _sub_obj_del, sd);
1215         evas_object_smart_callback_call(sd->obj, "sub-object-del", sobj);
1216         evas_object_del(sobj);
1217      }
1218    EINA_LIST_FREE(sd->subobjs, sobj)
1219      {
1220         evas_object_event_callback_del_full(sobj, EVAS_CALLBACK_DEL, _sub_obj_del, sd);
1221         evas_object_smart_callback_call(sd->obj, "sub-object-del", sobj);
1222         evas_object_del(sobj);
1223      }
1224    if (sd->del_func) sd->del_func(obj);
1225    if (sd->style) eina_stringshare_del(sd->style);
1226    if (sd->type) eina_stringshare_del(sd->type);
1227    if (sd->theme) elm_theme_free(sd->theme);
1228    _if_focused_revert(obj);
1229    free(sd);
1230 }
1231
1232 static void
1233 _smart_move(Evas_Object *obj, Evas_Coord x, Evas_Coord y)
1234 {
1235    INTERNAL_ENTRY;
1236    sd->x = x;
1237    sd->y = y;
1238    _smart_reconfigure(sd);
1239 }
1240
1241 static void
1242 _smart_resize(Evas_Object *obj, Evas_Coord w, Evas_Coord h)
1243 {
1244    INTERNAL_ENTRY;
1245    sd->w = w;
1246    sd->h = h;
1247    _smart_reconfigure(sd);
1248 }
1249
1250 static void
1251 _smart_show(Evas_Object *obj)
1252 {
1253    INTERNAL_ENTRY;
1254    evas_object_show(sd->resize_obj);
1255 }
1256
1257 static void
1258 _smart_hide(Evas_Object *obj)
1259 {
1260    INTERNAL_ENTRY;
1261    evas_object_hide(sd->resize_obj);
1262    _if_focused_revert(obj);
1263 }
1264
1265 static void
1266 _smart_color_set(Evas_Object *obj, int r, int g, int b, int a)
1267 {
1268    INTERNAL_ENTRY;
1269    evas_object_color_set(sd->resize_obj, r, g, b, a);
1270 }
1271
1272 static void
1273 _smart_clip_set(Evas_Object *obj, Evas_Object *clip)
1274 {
1275    INTERNAL_ENTRY;
1276    evas_object_clip_set(sd->resize_obj, clip);
1277 }
1278
1279 static void
1280 _smart_clip_unset(Evas_Object *obj)
1281 {
1282    INTERNAL_ENTRY;
1283    evas_object_clip_unset(sd->resize_obj);
1284 }
1285
1286 static void
1287 _smart_calculate(Evas_Object *obj)
1288 {
1289    INTERNAL_ENTRY;
1290    if (sd->changed_func) sd->changed_func(obj);
1291 }
1292
1293 /* never need to touch this */
1294
1295 static void
1296 _smart_init(void)
1297 {
1298    if (_e_smart) return;
1299      {
1300         static const Evas_Smart_Class sc =
1301           {
1302              SMART_NAME,
1303                EVAS_SMART_CLASS_VERSION,
1304                _smart_add,
1305                _smart_del,
1306                _smart_move,
1307                _smart_resize,
1308                _smart_show,
1309                _smart_hide,
1310                _smart_color_set,
1311                _smart_clip_set,
1312                _smart_clip_unset,
1313                _smart_calculate,
1314                NULL,
1315                NULL,
1316                NULL,
1317                NULL,
1318                NULL,
1319                NULL
1320           };
1321         _e_smart = evas_smart_class_new(&sc);
1322      }
1323 }
1324
1325 /* utilities */
1326
1327 Eina_List *
1328 _elm_stringlist_get(const char *str)
1329 {
1330    Eina_List *list = NULL;
1331    const char *s, *b;
1332    if (!str) return NULL;
1333    for (b = s = str; 1; s++)
1334      {
1335         if ((*s == ' ') || (*s == 0))
1336           {
1337              char *t = malloc(s - b + 1);
1338              if (t)
1339                {
1340                   strncpy(t, b, s - b);
1341                   t[s - b] = 0;
1342                   list = eina_list_append(list, eina_stringshare_add(t));
1343                   free(t);
1344                }
1345              b = s + 1;
1346           }
1347         if (*s == 0) break;
1348      }
1349    return list;
1350 }
1351
1352 void
1353 _elm_stringlist_free(Eina_List *list)
1354 {
1355    const char *s;
1356    EINA_LIST_FREE(list, s) eina_stringshare_del(s);
1357 }
1358
1359 Eina_Bool
1360 _elm_widget_type_check(const Evas_Object *obj, const char *type)
1361 {
1362    const char *provided, *expected = "(unknown)";
1363    static int abort_on_warn = -1;
1364    provided = elm_widget_type_get(obj);
1365    if (EINA_LIKELY(provided == type)) return EINA_TRUE;
1366    if (type) expected = type;
1367    if ((!provided) || (provided[0] == 0))
1368      {
1369         provided = evas_object_type_get(obj);
1370         if ((!provided) || (provided[0] == 0))
1371           provided = "(unknown)";
1372      }
1373    ERR("Passing Object: %p, of type: '%s' when expecting type: '%s'", obj, provided, expected);
1374    if (abort_on_warn == -1)
1375      {
1376         if (getenv("ELM_ERROR_ABORT")) abort_on_warn = 1;
1377         else abort_on_warn = 0;
1378      }
1379    if (abort_on_warn == 1) abort();
1380    return EINA_FALSE;
1381 }
1382
1383 static inline Eina_Bool
1384 _elm_widget_is(const Evas_Object *obj)
1385 {
1386    const char *type = evas_object_type_get(obj);
1387    return type == SMART_NAME;
1388 }