#ifdef HAVE_CONFIG_H
# include "elementary_config.h"
#endif

#define EFL_ACCESS_OBJECT_PROTECTED
#define EFL_ACCESS_COMPONENT_PROTECTED
#define ELM_WIDGET_PROTECTED
#define ELM_WIDGET_ITEM_PROTECTED
#define EFL_INPUT_EVENT_PROTECTED
#define EFL_UI_L10N_PROTECTED
#define EFL_UI_FOCUS_OBJECT_PROTECTED
#define EFL_UI_WIDGET_PART_BG_PROTECTED
#define EFL_PART_PROTECTED

#include <Elementary.h>

#include "elm_priv.h"
#include "elm_widget_container.h"
#include "elm_interface_scrollable.h"
#include "elm_pan_eo.h"
#include "elm_part_helper.h"
#include "elm_widget_combobox.h"

#define MY_CLASS EFL_UI_WIDGET_CLASS

#define MY_CLASS_NAME "Efl_Ui_Widget"
#define MY_CLASS_NAME_LEGACY "elm_widget"

#define API_ENTRY                                    \
  Elm_Widget_Smart_Data *sd = NULL;                  \
  if (!_elm_widget_is(obj) ||                        \
      (!(sd = efl_data_scope_get(obj, MY_CLASS))))

#define INTERNAL_ENTRY                               \
  ELM_WIDGET_DATA_GET(obj, sd);                      \
  if (!sd) return

#define ELM_WIDGET_FOCUS_GET(obj)                                          \
  (efl_isa(obj, EFL_UI_WIDGET_CLASS) &&                                    \
   ((_elm_access_auto_highlight_get()) ? (elm_widget_highlight_get(obj)) : \
                                         (efl_ui_focus_object_focus_get(obj))))

const char SIG_WIDGET_FOCUSED[] = "focused";
const char SIG_WIDGET_UNFOCUSED[] = "unfocused";
const char SIG_WIDGET_LANG_CHANGED[] = "language,changed";
const char SIG_WIDGET_ACCESS_CHANGED[] = "access,changed";

typedef struct _Elm_Event_Cb_Data         Elm_Event_Cb_Data;
typedef struct _Elm_Label_Data            Elm_Label_Data;
typedef struct _Elm_Translate_String_Data Elm_Translate_String_Data;

static Eina_Error _efl_ui_property_bind(Eo *widget, Eo *target, Efl_Ui_Widget_Data *pd,
                                        const char *part,
                                        const char *key, const char *property);

struct _Elm_Event_Cb_Data
{
   Elm_Event_Cb func;
   const void  *data;
};

struct _Elm_Label_Data
{
   const char *part;
   const char *text;
};

struct _Elm_Translate_String_Data
{
   EINA_INLIST;
   Eina_Stringshare *id;
   Eina_Stringshare *domain;
   Eina_Stringshare *string;
   Eina_Bool   preset : 1;
};

/* For keeping backward compatibility (EFL 1.18 or older versions).
 * Since EFL 1.19 which starts to use eolian_gen2, it does not convert
 * "." to "_" among the class name. */
static const char *legacy_type_table[][2] =
{
     { "Efl.Ui.Bg_Legacy", "Elm_Bg" },
     { "Efl.Ui.Button_Legacy", "Elm_Button" },
     { "Efl.Ui.Check_Legacy", "Elm_Check" },
     { "Efl.Ui.Clock_Legacy", "Elm_Datetime" },
     { "Efl.Ui.Flip_Legacy", "Elm_Flip" },
     { "Efl.Ui.Frame_Legacy", "Elm_Frame" },
     { "Efl.Ui.Image_Legacy", "Elm_Image" },
     { "Efl.Ui.Image_Zoomable_Legacy", "Elm_Photocam" },
     { "Efl.Ui.Layout_Legacy", "Elm_Layout" },
     { "Efl.Ui.Panes_Legacy", "Elm_Panes" },
     { "Efl.Ui.Progressbar_Legacy", "Elm_Progressbar" },
     { "Efl.Ui.Radio_Legacy", "Elm_Radio" },
     { "Efl.Ui.Video_Legacy", "Elm_Video" },
     { "Efl.Ui.Win_Legacy", "Elm_Win" },
     { "Efl.Ui.Win_Socket_Legacy", "Elm_Win" },
     { "Efl.Ui.Win_Inlined_Legacy", "Elm_Win" },
     { "Elm.Code_Widget_Legacy", "Elm_Code_Widget" },
     { "Elm.Ctxpopup", "Elm_Ctxpopup" },
     { "Elm.Entry", "Elm_Entry" },
     { "Elm.Colorselector", "Elm_Colorselector" },
     { "Elm.List", "Elm_List" },
     { "Elm.Photo", "Elm_Photo" },
     { "Elm.Actionslider", "Elm_Actionslider" },
     { "Elm.Box", "Elm_Box" },
     { "Elm.Table", "Elm_Table" },
     { "Elm.Thumb", "Elm_Thumb" },
     { "Elm.Menu", "Elm_Menu" },
     { "Elm.Icon", "Elm_Icon" },
     { "Elm.Prefs", "Elm_Prefs" },
     { "Elm.Map", "Elm_Map" },
     { "Elm.Glview", "Elm_Glview" },
     { "Elm.Web", "Elm_Web" },
     { "Elm.Toolbar", "Elm_Toolbar" },
     { "Elm.Grid", "Elm_Grid" },
     { "Elm.Diskselector", "Elm_Diskselector" },
     { "Elm.Notify", "Elm_Notify" },
     { "Elm.Mapbuf", "Elm_Mapbuf" },
     { "Elm.Separator", "Elm_Separator" },
     { "Elm.Calendar", "Elm_Calendar" },
     { "Elm.Inwin", "Elm_Inwin" },
     { "Elm.Gengrid", "Elm_Gengrid" },
     { "Elm.Scroller", "Elm_Scroller" },
     { "Elm.Player", "Elm_Player" },
     { "Elm.Segment_Control", "Elm_Segment_Control" },
     { "Elm.Fileselector", "Elm_Fileselector" },
     { "Elm.Fileselector_Button", "Elm_Fileselector_Button" },
     { "Elm.Fileselector_Entry", "Elm_Fileselector_Entry" },
     { "Elm.Flipselector", "Elm_Flipselector" },
     { "Elm.Hoversel", "Elm_Hoversel" },
     { "Elm.Naviframe", "Elm_Naviframe" },
     { "Elm.Popup", "Elm_Popup" },
     { "Elm.Bubble", "Elm_Bubble" },
     { "Elm.Clock", "Elm_Clock" },
     { "Elm.Conformant", "Elm_Conformant" },
     { "Elm.Dayselector", "Elm_Dayselector" },
     { "Elm.Genlist", "Elm_Genlist" },
     { "Elm.Hover", "Elm_Hover" },
     { "Elm.Index", "Elm_Index" },
     { "Elm.Label", "Elm_Label" },
     { "Elm.Panel", "Elm_Panel" },
     { "Elm.Slider", "Elm_Slider" },
     { "Elm.Slideshow", "Elm_Slideshow" },
     { "Elm.Spinner", "Elm_Spinner" },
     { "Elm.Plug", "Elm_Plug" },
     { "Elm.Web.None", "Elm_Web" },
     { "Elm.Multibuttonentry", "Elm_Multibuttonentry" },
     { NULL, NULL }
};

/* local subsystem globals */
static inline Eina_Bool
_elm_widget_is(const Evas_Object *obj)
{
   return efl_isa(obj, MY_CLASS);
}

static inline Eina_Bool
_is_focusable(Evas_Object *obj)
{
   API_ENTRY return EINA_FALSE;
   return sd->can_focus || (sd->logical.child_count > 0);
}

static inline Eina_Bool
_is_focused(Evas_Object *obj)
{
   API_ENTRY return EINA_FALSE;
   return sd->focused;
}

static inline Eina_Bool
_elm_scrollable_is(const Evas_Object *obj)
{
   INTERNAL_ENTRY EINA_FALSE;
   if (elm_widget_is_legacy(obj))
     return
        efl_isa(obj, ELM_INTERFACE_SCROLLABLE_MIXIN);
   else
     return
        efl_isa(obj, EFL_UI_SCROLLABLE_INTERFACE);
}

static void
_on_sub_obj_del(void *data, const Efl_Event *event);
static void _propagate_event(void *data, const Efl_Event *eo_event);
static void _elm_widget_shadow_update(Efl_Ui_Widget *obj);

EFL_CALLBACKS_ARRAY_DEFINE(efl_subitems_callbacks,
                          { EFL_EVENT_DEL, _on_sub_obj_del });
EFL_CALLBACKS_ARRAY_DEFINE(focus_callbacks,
                          { EFL_EVENT_KEY_DOWN, _propagate_event },
                          { EFL_EVENT_KEY_UP, _propagate_event },
                          { EFL_EVENT_POINTER_WHEEL, _propagate_event });

static inline void
_callbacks_add(Eo *widget, void *data)
{
    efl_event_callback_array_add(widget, efl_subitems_callbacks(), data);
}

static inline void
_callbacks_del(Eo *widget, void *data)
{
    efl_event_callback_array_del(widget, efl_subitems_callbacks(), data);
}

void
_elm_widget_item_highlight_in_theme(Evas_Object *obj, Elm_Object_Item *eo_it)
{
   const char *str;

   if (!eo_it) return;
   if (efl_isa(eo_it, ELM_WIDGET_ITEM_CLASS))
     {
        Elm_Widget_Item_Data *it = efl_data_scope_get(eo_it, ELM_WIDGET_ITEM_CLASS);

        if (efl_isa(it->view, EFL_UI_LAYOUT_BASE_CLASS))
          str = edje_object_data_get(elm_layout_edje_get(it->view), "focus_highlight");
        else
          str = edje_object_data_get(it->view, "focus_highlight");
     }
   else
      str = edje_object_data_get(((Elm_Widget_Item_Data *)eo_it)->view, "focus_highlight");
   if ((str) && (!strcmp(str, "on")))
     elm_widget_highlight_in_theme_set(obj, EINA_TRUE);
   else
     elm_widget_highlight_in_theme_set(obj, EINA_FALSE);
}

void
_elm_widget_focus_highlight_start(const Evas_Object *obj)
{
   Evas_Object *top = efl_provider_find(obj, EFL_UI_WIN_CLASS);

   EINA_SAFETY_ON_FALSE_RETURN(efl_isa(top, EFL_UI_WIN_CLASS));

   _elm_win_focus_highlight_start(top);
}

Evas_Object *
_efl_ui_widget_focus_highlight_object_get(const Evas_Object *obj)
{
   Evas_Object *top = efl_provider_find(obj, EFL_UI_WIN_CLASS);

   EINA_SAFETY_ON_FALSE_RETURN_VAL(efl_isa(top, EFL_UI_WIN_CLASS), NULL);

   return _elm_win_focus_highlight_object_get(top);
}

static Eina_Bool
_legacy_focus_eval(Eo *obj)
{
   Eina_List *lst;
   Efl_Ui_Widget *wid = obj, *top;
   Elm_Widget_Smart_Data *wid_pd;

   wid_pd = efl_data_scope_get(wid, MY_CLASS);
   do {

     top = wid;

     wid = elm_widget_parent_get(wid);
     if (!wid) break;
     wid_pd = efl_data_scope_get(wid, MY_CLASS);

     lst = wid_pd->legacy_focus.custom_chain;
     if (lst)
       {
          if (!eina_list_data_find(lst, top))
            {
               WRN("Widget %p disabled due to custom chain of %p", top, wid);
               return EINA_TRUE;
            }
       }

   } while (1);

   return !efl_isa(top, EFL_UI_WIN_CLASS);
}

static void _full_eval(Eo *obj, Elm_Widget_Smart_Data *pd);

static Efl_Ui_Focus_Object*
_focus_manager_eval(Eo *obj, Elm_Widget_Smart_Data *pd)
{
   Evas_Object *provider = NULL;
   Evas_Object *parent;
   Efl_Ui_Focus_Manager *new = NULL, *old = NULL;

   parent = elm_widget_parent_get(obj);
   if (efl_isa(parent, EFL_UI_FOCUS_MANAGER_INTERFACE))
     {
        new = parent;
     }
   else if (parent)
     {
        new = efl_ui_focus_object_focus_manager_get(parent);
        provider = parent;
     }

   if (new != pd->manager.manager)
     {
        old = pd->manager.manager;

        pd->manager.manager = new;
        pd->manager.provider = provider;
     }

   return old;
}

EOLIAN static Eina_Bool
_efl_ui_widget_focus_state_apply(Eo *obj, Elm_Widget_Smart_Data *pd EINA_UNUSED, Efl_Ui_Widget_Focus_State current_state, Efl_Ui_Widget_Focus_State *configured_state, Efl_Ui_Widget *redirect)
{
   Eina_Bool registered = EINA_TRUE;

   //shortcut for having the same configurations
   if (current_state.manager == configured_state->manager && !current_state.manager)
     return !!current_state.manager;

   if (configured_state->logical == current_state.logical &&
       configured_state->manager == current_state.manager &&
       configured_state->parent == current_state.parent)
     return !!current_state.manager;

   //this thing doesnt want to be registered, but it is ...
   if (!configured_state->manager && current_state.manager)
     {
        efl_ui_focus_manager_calc_unregister(current_state.manager, obj);
        return EINA_FALSE;
     }
   //by that point we have always a configured manager

   if (!current_state.manager) registered = EINA_FALSE;

   if ((//check if we have changed the manager
        (current_state.manager != configured_state->manager) ||
        //check if we are already registered but in a different state
        (current_state.logical != configured_state->logical))
       && registered)
     {
        //we need to unregister here
        efl_ui_focus_manager_calc_unregister(current_state.manager, obj);
        registered = EINA_FALSE;
     }

   //the parent may has changed
   if (current_state.parent != configured_state->parent && registered)
     {
        return efl_ui_focus_manager_calc_update_parent(current_state.manager, obj, configured_state->parent);
     }

   if (!registered)
     {
        if (configured_state->logical)
          return efl_ui_focus_manager_calc_register_logical(configured_state->manager, obj, configured_state->parent, redirect);
        else
          return efl_ui_focus_manager_calc_register(configured_state->manager, obj, configured_state->parent, redirect);
     }
   ERR("Uncaught focus state consider this as unregistered (%d) \n (%p,%p,%d) \n (%p,%p,%d) ", registered,
     configured_state->manager, configured_state->parent, configured_state->logical,
     current_state.manager, current_state.parent, current_state.logical
   );
   return EINA_FALSE;
}
static void
_eval_registration_candidate(Eo *obj, Elm_Widget_Smart_Data *pd, Eina_Bool *should, Eina_Bool *want_full)
{
   *should = *want_full = EINA_FALSE;

    //can focus can be overridden by the following properties
    if ((!pd->parent_obj) ||
        (!evas_object_visible_get(obj)) ||
        pd->disabled > 0 ||
        pd->tree_unfocusable > 0)
      return;

    if (!pd->shared_win_data || ((Efl_Ui_Shared_Win_Data*)pd->shared_win_data)->legacy_focus_api_used)
      {
         if (_legacy_focus_eval(obj))
           return;
      }

    if (pd->can_focus)
      {
         *should = *want_full = EINA_TRUE;
      }
    else if (pd->logical.child_count > 0)
      {
         *should = EINA_TRUE;
      }
}

static void
_focus_state_eval(Eo *obj, Elm_Widget_Smart_Data *pd, Eina_Bool should, Eina_Bool want_full)
{
   Efl_Ui_Widget_Focus_State configuration;

   //this would mean we are registering again the root, we dont want that
   if (pd->manager.manager == obj) return;

   //there are two reasons to be registered, the child count is bigger than 0, or the widget is flagged to be able to handle focus
   if (should)
     {
        configuration.parent = pd->logical.parent;
        configuration.manager = pd->manager.manager;
        configuration.logical = !want_full;
     }
   else
     {
        configuration.parent = NULL;
        configuration.manager = NULL;
        configuration.logical = EINA_FALSE;
     }

   if (!efl_ui_widget_focus_state_apply(obj, pd->focus, &configuration, NULL))
     {
        //things went wrong or this thing is unregistered. Purge the current configuration.
        pd->focus.manager = NULL;
        pd->focus.parent = NULL;
        pd->focus.logical = EINA_FALSE;
     }
   else
     {
        pd->focus.parent = configuration.parent;
        pd->focus.manager = configuration.manager;
        pd->focus.logical = configuration.logical;
     }

}

static Efl_Ui_Focus_Object*
_logical_parent_eval(Eo *obj EINA_UNUSED, Elm_Widget_Smart_Data *pd, Eina_Bool should, Eina_Bool *state_change_to_parent)
{
   Efl_Ui_Widget *parent;
   Efl_Ui_Focus_Parent_Provider *provider;

   if (!pd->shared_win_data || ((Efl_Ui_Shared_Win_Data*)pd->shared_win_data)->custom_parent_provider)
     {
        if (should)
          {
             provider = efl_provider_find(obj, EFL_UI_FOCUS_PARENT_PROVIDER_INTERFACE);
             if (provider)
               {
                  EINA_SAFETY_ON_NULL_RETURN_VAL(provider, NULL);
                  parent = efl_ui_focus_parent_provider_find_logical_parent(provider, obj);
               }
             else parent = NULL;
          }
        else parent = NULL;
     }
   else parent = efl_ui_widget_parent_get(obj);


   if (pd->logical.parent != parent)
     {
        Efl_Ui_Focus_Object *old = NULL;

        //update old logical parent;
        if (pd->logical.parent)
          {
             if (efl_isa(pd->logical.parent, EFL_UI_WIDGET_CLASS))
               {
                  ELM_WIDGET_DATA_GET_OR_RETURN(pd->logical.parent, logical_wd, NULL);
                  logical_wd->logical.child_count --;
                  if (logical_wd->logical.child_count == 0)
                    {
                      *state_change_to_parent = EINA_TRUE;
                    }
               }
             old = pd->logical.parent;
             efl_weak_unref(&pd->logical.parent);
             pd->logical.parent = NULL;
          }
        if (parent)
          {
             if (efl_isa(parent, EFL_UI_WIDGET_CLASS))
               {
                  ELM_WIDGET_DATA_GET_OR_RETURN(parent, parent_wd, NULL);
                  parent_wd->logical.child_count ++;
                  if (parent_wd->logical.child_count == 1)
                    {
                      *state_change_to_parent = EINA_TRUE;
                    }
               }
             pd->logical.parent = parent;
             efl_weak_ref(&pd->logical.parent);
          }
        return old;
     }
   return NULL;
}

static void
_full_eval(Eo *obj, Elm_Widget_Smart_Data *pd)
{
   Efl_Ui_Focus_Object *old_parent;
   Efl_Ui_Focus_Object *old_registered_parent, *old_registered_manager;
   Eina_Bool should, want_full, state_change_to_parent = EINA_FALSE;

   _eval_registration_candidate(obj, pd, &should, &want_full);

   old_parent = _logical_parent_eval(obj, pd, should, &state_change_to_parent);

   if (state_change_to_parent)
     {
        if (efl_isa(old_parent, EFL_UI_WIDGET_CLASS))
          {
             //emit signal and focus eval old and new
             ELM_WIDGET_DATA_GET(old_parent, old_pd);
             if (old_pd) _full_eval(old_parent, old_pd);
          }

        if (efl_isa(pd->logical.parent, EFL_UI_WIDGET_CLASS))
          {
             ELM_WIDGET_DATA_GET(pd->logical.parent, new_pd);
             if (new_pd) _full_eval(pd->logical.parent, new_pd);
          }
     }


   _focus_manager_eval(obj, pd);

   old_registered_parent = pd->focus.parent;
   old_registered_manager = pd->focus.manager;

   _focus_state_eval(obj, pd, should, want_full);

   if (old_registered_parent != pd->focus.parent)
     {
        efl_event_callback_call(obj,
             EFL_UI_FOCUS_OBJECT_EVENT_FOCUS_PARENT_CHANGED, old_registered_parent);
     }

   if (old_registered_manager != pd->focus.manager)
     {
        _elm_widget_full_eval_children(obj, pd);
        efl_event_callback_call(obj,
             EFL_UI_FOCUS_OBJECT_EVENT_FOCUS_MANAGER_CHANGED, old_registered_manager);
     }

}

void
_elm_widget_full_eval(Eo *obj)
{
   ELM_WIDGET_DATA_GET(obj, pd);
   if (pd) _full_eval(obj, pd);
}

/**
 * @internal
 *
 * Resets the mirrored mode from the system mirror mode for widgets that are in
 * automatic mirroring mode. This function does not call elm_widget_theme.
 *
 * @param obj The widget.
 * @param mirrored EINA_TRUE to set mirrored mode. EINA_FALSE to unset.
 */
static void
_elm_widget_mirrored_reload(Evas_Object *obj)
{
   API_ENTRY return;
   Eina_Bool mirrored = elm_config_mirrored_get();

   if (efl_ui_mirrored_automatic_get(obj) && (sd->is_mirrored != mirrored))
     {
        sd->is_mirrored = mirrored;
     }
}

static void
_parents_focus(Evas_Object *obj)
{
   for (; obj; obj = elm_widget_parent_get(obj))
     {
        INTERNAL_ENTRY;
        if (sd->focused) return;
        sd->focused = 1;
     }
}

static void
_parents_unfocus(Evas_Object *obj)
{
   for (; obj; obj = elm_widget_parent_get(obj))
     {
        INTERNAL_ENTRY;
        if (!sd->focused) return;
        sd->focused = 0;
     }
}

static void
_on_sub_obj_del(void *data, const Efl_Event *event)
{
   ELM_WIDGET_DATA_GET_OR_RETURN(data, sd);

   if (_elm_widget_is(event->object))
     {
        if (_is_focused(event->object)) _parents_unfocus(data);
     }
   if (event->object == sd->resize_obj)
     {
        /* already dels sub object */
        elm_widget_resize_object_set(data, NULL);
     }
   else if (event->object == sd->hover_obj)
     {
        sd->hover_obj = NULL;
     }
   else
     {
        if (!elm_widget_sub_object_del(data, event->object))
          ERR("failed to remove sub object %p from %p\n", event->object, data);
     }
}

static const Evas_Smart_Cb_Description _smart_callbacks[] =
{
   {SIG_WIDGET_FOCUSED, ""},
   {SIG_WIDGET_UNFOCUSED, ""},
   {SIG_WIDGET_LANG_CHANGED, ""},
   {SIG_WIDGET_ACCESS_CHANGED, ""},
   {NULL, NULL}
};

static void
_obj_mouse_down(void *data,
                Evas *e EINA_UNUSED,
                Evas_Object *obj EINA_UNUSED,
                void *event_info)
{
   Evas_Object *top;

   ELM_WIDGET_DATA_GET(data, sd);
   if (!sd) return;
   Evas_Event_Mouse_Down *ev = event_info;
   if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD) return;

   top = elm_widget_top_get(data);
   if (top && efl_isa(top, EFL_UI_WIN_CLASS)) _elm_win_focus_auto_hide(top);
   sd->still_in = EINA_TRUE;
}

static void
_obj_mouse_move(void *data,
                Evas *e EINA_UNUSED,
                Evas_Object *obj,
                void *event_info)
{
   ELM_WIDGET_DATA_GET(data, sd);
   if (!sd) return;
   Evas_Event_Mouse_Move *ev = event_info;
   if (!sd->still_in) return;

   if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD)
     sd->still_in = EINA_FALSE;
   else
     {
        Evas_Coord x, y, w, h;
        evas_object_geometry_get(obj, &x, &y, &w, &h);
        if (ELM_RECTS_POINT_OUT(x, y, w, h, ev->cur.canvas.x, ev->cur.canvas.y))
          sd->still_in = EINA_FALSE;
     }
}

static void
_obj_mouse_up(void *data,
              Evas *e EINA_UNUSED,
              Evas_Object *obj,
              void *event_info)
{
   ELM_WIDGET_DATA_GET(data, sd);
   if (!sd) return;
   Evas_Event_Mouse_Up *ev = event_info;

   if (sd->still_in && (ev->flags == EVAS_BUTTON_NONE) &&
       (sd->focus_move_policy == ELM_FOCUS_MOVE_POLICY_CLICK) &&
       !efl_invalidated_get(data))
     elm_widget_focus_mouse_up_handle(evas_object_widget_parent_find(obj));

   sd->still_in = EINA_FALSE;
}

static void
_obj_mouse_in(void *data,
              Evas *e EINA_UNUSED,
              Evas_Object *obj,
              void *event_info EINA_UNUSED)
{
   ELM_WIDGET_DATA_GET(data, sd);
   if (!sd) return;
   if (sd->focus_move_policy == ELM_FOCUS_MOVE_POLICY_IN &&
       !efl_invalidated_get(data))
     elm_widget_focus_mouse_up_handle(evas_object_widget_parent_find(obj));
}

EOLIAN static void
_efl_ui_widget_efl_canvas_group_group_add(Eo *obj, Elm_Widget_Smart_Data *priv)
{
   priv->mirrored_auto_mode = EINA_TRUE; /* will follow system locale
                                          * settings */
   priv->focus_move_policy_auto_mode = EINA_TRUE;
   priv->focus_region_show_mode = ELM_FOCUS_REGION_SHOW_WIDGET;
   elm_widget_can_focus_set(obj, EINA_TRUE);
   priv->is_mirrored = elm_config_mirrored_get();
   priv->focus_move_policy = _elm_config->focus_move_policy;

   evas_object_event_callback_add(obj, EVAS_CALLBACK_MOUSE_DOWN,
                                  _obj_mouse_down, obj);
   evas_object_event_callback_add(obj, EVAS_CALLBACK_MOUSE_MOVE,
                                  _obj_mouse_move, obj);
   evas_object_event_callback_add(obj, EVAS_CALLBACK_MOUSE_UP,
                                  _obj_mouse_up, obj);
   evas_object_event_callback_add(obj, EVAS_CALLBACK_MOUSE_IN,
                                  _obj_mouse_in, obj);
}

static Eina_Bool
_keep(void *data, void *gdata)
{
   if (data == gdata)
     return EINA_FALSE;
   return EINA_TRUE;
}

EOLIAN static void
_efl_ui_widget_efl_canvas_group_group_del(Eo *obj, Elm_Widget_Smart_Data *sd)
{
   Evas_Object *sobj;
   Elm_Translate_String_Data *ts;
   Elm_Event_Cb_Data *ecb;

   if (sd->hover_obj)
     {
        /* detach it from us */
        _callbacks_del(sd->hover_obj, obj);
        sd->hover_obj = NULL;
     }
   while(eina_array_count(sd->children))
     {
        sobj = eina_array_data_get(sd->children, 0);

        if (!elm_widget_sub_object_del(obj, sobj))
          {
             ERR("failed to remove sub object %p from %p\n", sobj, obj);
             eina_array_remove(sd->children, _keep, sobj);
          }
        // FIXME: is that a legacy or a new object ?
        evas_object_del(sobj);
        EINA_SAFETY_ON_TRUE_RETURN(eina_array_count(sd->children) && sobj == eina_array_data_get(sd->children, 0));
     }
   sd->tooltips = eina_list_free(sd->tooltips); /* should be empty anyway */
   sd->cursors = eina_list_free(sd->cursors); /* should be empty anyway */
   while (sd->translate_strings)
     {
        ts = EINA_INLIST_CONTAINER_GET(sd->translate_strings,
                                       Elm_Translate_String_Data);
        eina_stringshare_del(ts->id);
        eina_stringshare_del(ts->domain);
        eina_stringshare_del(ts->string);
        sd->translate_strings = eina_inlist_remove(sd->translate_strings,
                                                   sd->translate_strings);
        free(ts);
     }

   EINA_LIST_FREE(sd->event_cb, ecb)
      free(ecb);

   eina_stringshare_del(sd->klass);
   eina_stringshare_del(sd->group);
   eina_stringshare_del(sd->style);
   if (sd->theme) elm_theme_free(sd->theme);
   eina_stringshare_del(sd->access_info);
   eina_stringshare_del(sd->accessible_name);
   evas_object_smart_data_set(obj, NULL);
   efl_canvas_group_del(efl_super(obj, MY_CLASS));
}

static void
_smart_reconfigure(Eo *obj, Elm_Widget_Smart_Data *sd)
{
   Eina_Rect geom = efl_gfx_entity_geometry_get(obj);

   if (sd->resize_obj)
     {
        efl_gfx_entity_geometry_set(sd->resize_obj, geom);
     }
   if (sd->hover_obj)
     {
        efl_gfx_entity_geometry_set(sd->hover_obj, geom);
     }
   if (sd->bg)
     {
        efl_gfx_entity_geometry_set(sd->bg, geom);
     }
   if (sd->has_shadow)
     _elm_widget_shadow_update(obj);
}

EOLIAN static void
_efl_ui_widget_efl_gfx_entity_position_set(Eo *obj EINA_UNUSED, Elm_Widget_Smart_Data *sd, Eina_Position2D pos)
{
   if (_evas_object_intercept_call(obj, EVAS_OBJECT_INTERCEPT_CB_MOVE, 0, pos.x, pos.y))
     return;

   if (sd->resize_obj)
     efl_gfx_entity_position_set(sd->resize_obj, pos);
   if (sd->hover_obj)
     efl_gfx_entity_position_set(sd->hover_obj, pos);
   if (sd->bg)
     efl_gfx_entity_position_set(sd->bg, pos);

   if (sd->has_shadow)
     _elm_widget_shadow_update(obj);

   efl_gfx_entity_position_set(efl_super(obj, MY_CLASS), pos);
}

EOLIAN static void
_efl_ui_widget_efl_gfx_entity_size_set(Eo *obj EINA_UNUSED, Elm_Widget_Smart_Data *sd, Eina_Size2D sz)
{
   if (_evas_object_intercept_call(obj, EVAS_OBJECT_INTERCEPT_CB_RESIZE, 0, sz.w, sz.h))
     return;

   if (sd->resize_obj)
     efl_gfx_entity_size_set(sd->resize_obj, sz);
   if (sd->hover_obj)
     efl_gfx_entity_size_set(sd->hover_obj, sz);
   if (sd->bg)
     efl_gfx_entity_size_set(sd->bg, sz);

   if (sd->has_shadow)
     _elm_widget_shadow_update(obj);

   efl_gfx_entity_size_set(efl_super(obj, MY_CLASS), sz);
}

void
_elm_widget_full_eval_children(Eo *obj, Elm_Widget_Smart_Data *sd)
{
   Eo *child;

   _full_eval(obj, sd);

   for (unsigned int i = 0; i < eina_array_count(sd->children); ++i)
     {
        Elm_Widget_Smart_Data *sd_child;
        child = eina_array_data_get(sd->children, i);

        if (!efl_isa(child, EFL_UI_WIDGET_CLASS)) continue;

        sd_child = efl_data_scope_get(child, EFL_UI_WIDGET_CLASS);
        _elm_widget_full_eval_children(child, sd_child);
     }
}

EOLIAN static void
_efl_ui_widget_efl_gfx_entity_visible_set(Eo *obj, Elm_Widget_Smart_Data *pd, Eina_Bool vis)
{
   Eina_Iterator *it;
   Evas_Object *o;

   if (_evas_object_intercept_call(obj, EVAS_OBJECT_INTERCEPT_CB_VISIBLE, 0, vis))
     {
        _elm_widget_full_eval_children(obj, pd);
        return;
     }

   efl_gfx_entity_visible_set(efl_super(obj, MY_CLASS), vis);

   _elm_widget_full_eval_children(obj, pd);


   it = evas_object_smart_iterator_new(obj);
   EINA_ITERATOR_FOREACH(it, o)
     {
       if (evas_object_data_get(o, "_elm_leaveme")) continue;
       efl_gfx_entity_visible_set(o, vis);
     }
   eina_iterator_free(it);

   if (!_elm_config->atspi_mode || pd->on_destroy)
     return;

   if (vis)
     {
        efl_access_added(obj);
        if (_elm_widget_onscreen_is(obj))
          efl_access_state_changed_signal_emit(obj, EFL_ACCESS_STATE_TYPE_SHOWING, EINA_TRUE);
     }
   else
     {
        efl_access_state_changed_signal_emit(obj, EFL_ACCESS_STATE_TYPE_SHOWING, EINA_FALSE);
     }
}

EOLIAN static void
_efl_ui_widget_efl_gfx_color_color_set(Eo *obj, Elm_Widget_Smart_Data *pd, int r, int g, int b, int a)
{
   Eina_Iterator *it;
   Evas_Object *o;

   if (_evas_object_intercept_call(obj, EVAS_OBJECT_INTERCEPT_CB_COLOR_SET, 0, r, g, b, a))
     return;

   efl_gfx_color_set(efl_super(obj, MY_CLASS), r, g, b, a);

   it = evas_object_smart_iterator_new(obj);
   EINA_ITERATOR_FOREACH(it, o)
     {
       if (pd->bg == o) continue;
       if (evas_object_data_get(o, "_elm_leaveme")) continue;
       evas_object_color_set(o, r, g, b, a);
     }
   eina_iterator_free(it);
}

EOLIAN static void
_efl_ui_widget_efl_canvas_object_no_render_set(Eo *obj, Elm_Widget_Smart_Data *_pd EINA_UNUSED, Eina_Bool hide)
{
   Eina_Iterator *it;
   Evas_Object *o;

   hide = !!hide;
   if (efl_canvas_object_no_render_get(obj) == hide)
     return;

   it = evas_object_smart_iterator_new(obj);
   EINA_ITERATOR_FOREACH(it, o)
     {
       if (evas_object_data_get(o, "_elm_leaveme")) continue;
       efl_canvas_object_no_render_set(o, hide);
     }
   eina_iterator_free(it);

   // bypass implementation in Efl.Canvas.Group
   efl_canvas_object_no_render_set(efl_super(obj, EFL_CANVAS_GROUP_CLASS), hide);
}

EOLIAN static void
_efl_ui_widget_efl_canvas_object_is_frame_object_set(Eo *obj, Elm_Widget_Smart_Data *pd, Eina_Bool frame)
{
   Evas_Object *o;

   frame = !!frame;
   efl_canvas_object_is_frame_object_set(efl_super(obj, MY_CLASS), frame);
   for (unsigned int i = 0; i < eina_array_count(pd->children); ++i)
     {
        o = eina_array_data_get(pd->children, i);

       if (evas_object_data_get(o, "_elm_leaveme")) continue;
       efl_canvas_object_is_frame_object_set(o, frame);
     }
}

EOLIAN static void
_efl_ui_widget_efl_canvas_object_clipper_set(Eo *obj, Elm_Widget_Smart_Data *_pd EINA_UNUSED, Evas_Object *clip)
{
   Eina_Iterator *it;
   Evas_Object *o;

   if (_evas_object_intercept_call(obj, EVAS_OBJECT_INTERCEPT_CB_CLIP_SET, 0, clip))
     return;

   efl_canvas_object_clipper_set(efl_super(obj, MY_CLASS), clip);

   it = evas_object_smart_iterator_new(obj);
   EINA_ITERATOR_FOREACH(it, o)
     {
       if (evas_object_data_get(o, "_elm_leaveme")) continue;
       evas_object_clip_set(o, clip);
     }
   eina_iterator_free(it);
}

EOLIAN static void
_efl_ui_widget_efl_canvas_group_group_calculate(Eo *obj EINA_UNUSED, Elm_Widget_Smart_Data *_pd EINA_UNUSED)
{
   /* a NO-OP, on the base */
   efl_canvas_group_need_recalculate_set(obj, EINA_FALSE);
}

EOLIAN static void
_efl_ui_widget_efl_canvas_group_group_member_add(Eo *obj, Elm_Widget_Smart_Data *pd, Evas_Object *child)
{
   int r, g, b, a;
   efl_canvas_group_member_add(efl_super(obj, MY_CLASS), child);

   if (evas_object_data_get(child, "_elm_leaveme")) return;

   if (pd->bg != child)
     {
        evas_object_color_get(obj, &r, &g, &b, &a);
        evas_object_color_set(child, r, g, b, a);
     }

   efl_canvas_object_no_render_set(child, efl_canvas_object_no_render_get(obj));
   evas_object_clip_set(child, evas_object_clip_get(obj));

   if (evas_object_visible_get(obj))
     evas_object_show(child);
   else
     evas_object_hide(child);
}

EOLIAN static void
_efl_ui_widget_efl_canvas_group_group_member_remove(Eo *obj EINA_UNUSED, Elm_Widget_Smart_Data *_pd EINA_UNUSED, Evas_Object *child)
{
   if (!evas_object_data_get(child, "_elm_leaveme"))
      evas_object_clip_unset(child);
   efl_canvas_group_member_remove(efl_super(obj, MY_CLASS), child);
}

// internal funcs
static void
_propagate_x_drag_lock(Evas_Object *obj,
                       int dir)
{
   INTERNAL_ENTRY;
   if (sd->parent_obj)
     {
        ELM_WIDGET_DATA_GET(sd->parent_obj, sd2);
        if (sd2)
          {
             sd2->child_drag_x_locked += dir;
             _propagate_x_drag_lock(sd->parent_obj, dir);
          }
     }
}

static void
_propagate_y_drag_lock(Evas_Object *obj,
                       int dir)
{
   INTERNAL_ENTRY;
   if (sd->parent_obj)
     {
        ELM_WIDGET_DATA_GET(sd->parent_obj, sd2);
        if (sd2)
          {
             sd2->child_drag_y_locked += dir;
             _propagate_y_drag_lock(sd->parent_obj, dir);
          }
     }
}

static Eina_Bool
_propagate_event_legacy(Eo *parent, const Efl_Event *event, Eo *obj, Elm_Event_Cb_Data *ecd)
{
   Evas_Callback_Type type;
   Evas_Event_Flags *event_flags, prev_flags;
   union {
      Evas_Event_Key_Down    *down;
      Evas_Event_Key_Up      *up;
      Evas_Event_Mouse_Wheel *wheel;
      void                   *any;
   } event_info;

   if (event->desc == EFL_EVENT_KEY_DOWN)
     {
        event_info.down = efl_input_legacy_info_get(event->info);
        EINA_SAFETY_ON_NULL_RETURN_VAL(event_info.down, EINA_FALSE);
        type = EVAS_CALLBACK_KEY_DOWN;
        event_flags = &event_info.down->event_flags;
     }
   else if (event->desc == EFL_EVENT_KEY_UP)
     {
        event_info.up = efl_input_legacy_info_get(event->info);
        EINA_SAFETY_ON_NULL_RETURN_VAL(event_info.up, EINA_FALSE);
        type = EVAS_CALLBACK_KEY_UP;
        event_flags = &event_info.up->event_flags;
     }
   else if (event->desc == EFL_EVENT_POINTER_WHEEL)
     {
        event_info.wheel = efl_input_legacy_info_get(event->info);
        EINA_SAFETY_ON_NULL_RETURN_VAL(event_info.wheel, EINA_FALSE);
        type = EVAS_CALLBACK_MOUSE_WHEEL;
        event_flags = &event_info.wheel->event_flags;
     }
   else
     return EINA_FALSE;

   prev_flags = *event_flags;
   if (ecd->func((void *)ecd->data, parent, obj, type, event_info.any) ||
       ((*event_flags) & EVAS_EVENT_FLAG_ON_HOLD))
     {
        if (prev_flags != *event_flags)
          efl_input_event_flags_set(event->info, (Efl_Input_Flags)*event_flags);
        return EINA_TRUE;
     }

   return EINA_FALSE;
}

/**
 * @internal
 *
 * If elm_widget_focus_region_get() returns an empty rect (w or h <= 0),
 * this function will ignore region show action.
 */
EAPI void
elm_widget_focus_region_show(Eo *obj)
{
   Evas_Coord ox, oy;
   Eina_Rect r;
   Evas_Object *o;

   o = elm_widget_parent_get(obj);
   if (!o) return;

   r = elm_widget_focus_region_get(obj);
   if (eina_rectangle_is_empty(&r.rect)) return;

   evas_object_geometry_get(obj, &ox, &oy, NULL, NULL);

   while (o)
     {
        Evas_Coord px, py;
        evas_object_geometry_get(o, &px, &py, NULL, NULL);

        if (_elm_scrollable_is(o) && !elm_widget_disabled_get(o))
          {
             Evas_Coord sx, sy;
             Evas_Coord vx, vy;

             elm_interface_scrollable_content_region_get(o, &sx, &sy, NULL, NULL);
             elm_interface_scrollable_content_viewport_geometry_get(o, &vx, &vy, NULL, NULL);

             // Get the object's on_focus_region position relative to the pan in the scroller.
             Evas_Coord rx, ry;
             rx = ox + r.x - vx + sx;
             ry = oy + r.y - vy + sy;

             switch (_elm_config->focus_autoscroll_mode)
               {
                case ELM_FOCUS_AUTOSCROLL_MODE_SHOW:
                   elm_interface_scrollable_content_region_show(o, rx, ry, r.w, r.h);
                   break;
                case ELM_FOCUS_AUTOSCROLL_MODE_BRING_IN:
                   elm_interface_scrollable_region_bring_in(o, rx, ry, r.w, r.h);
                   break;
                default:
                   break;
               }

             r = elm_widget_focus_region_get(o);
             evas_object_geometry_get(o, &ox, &oy, NULL, NULL);
          }
        else
          {
             r.x += ox - px;
             r.y += oy - py;
             ox = px;
             oy = py;
          }
        o = elm_widget_parent_get(o);
     }
}


EAPI Eina_Bool
elm_widget_api_check(int ver)
{
   if (ver != ELM_INTERNAL_API_VERSION)
     {
        CRI("Elementary widget api versions do not match");
        return EINA_FALSE;
     }
   return EINA_TRUE;
}

EAPI Eina_Bool
elm_widget_access(Evas_Object *obj,
                  Eina_Bool is_access)
{
   Evas_Object *child;
   Eina_Bool ret = EINA_TRUE;

   API_ENTRY return EINA_FALSE;
   for (unsigned int i = 0; i < eina_array_count(sd->children); ++i)
     {
        child = eina_array_data_get(sd->children, i);

        if (elm_widget_is(child))
          ret &= elm_widget_access(child, is_access);
     }

   efl_ui_widget_on_access_update(obj, is_access);
   efl_event_callback_legacy_call(obj, EFL_UI_WIDGET_EVENT_ACCESS_CHANGED, NULL);

   return ret;
}

EOLIAN static void
_efl_ui_widget_on_access_update(Eo *obj EINA_UNUSED, Elm_Widget_Smart_Data *_pd EINA_UNUSED, Eina_Bool is_access EINA_UNUSED)
{
}

static void
_elm_widget_theme_helper(Eina_Error err, Eina_Bool *err_default, Eina_Bool *err_generic)
{
   if (err == EFL_UI_THEME_APPLY_ERROR_DEFAULT)
     *err_default = EINA_TRUE;
   else if (err == EFL_UI_THEME_APPLY_ERROR_GENERIC)
     *err_generic = EINA_TRUE;
}

EAPI Eina_Error
elm_widget_theme(Evas_Object *obj)
{
   const Eina_List *l;
   Evas_Object *child;
   Elm_Tooltip *tt;
   Elm_Cursor *cur;
   Eina_Bool err_default = EINA_FALSE;
   Eina_Bool err_generic = EINA_FALSE;

   API_ENTRY return EFL_UI_THEME_APPLY_ERROR_GENERIC;
   for (unsigned int i = 0; i < eina_array_count(sd->children); ++i)
     {
        child = eina_array_data_get(sd->children, i);
        if (_elm_widget_is(child))
          _elm_widget_theme_helper(elm_widget_theme(child), &err_default, &err_generic);
     }

   if (sd->hover_obj)
     _elm_widget_theme_helper(elm_widget_theme(sd->hover_obj), &err_default, &err_generic);

   EINA_LIST_FOREACH(sd->tooltips, l, tt)
     elm_tooltip_theme(tt);
   EINA_LIST_FOREACH(sd->cursors, l, cur)
     elm_cursor_theme(cur);

   _elm_widget_theme_helper(efl_ui_widget_theme_apply(obj), &err_default, &err_generic);
   if (err_generic) return EFL_UI_THEME_APPLY_ERROR_GENERIC;
   if (err_default) return EFL_UI_THEME_APPLY_ERROR_DEFAULT;
   return EFL_UI_THEME_APPLY_ERROR_NONE;
}

EAPI void
elm_widget_theme_specific(Evas_Object *obj,
                          Elm_Theme *th,
                          Eina_Bool force)
{
   const Eina_List *l;
   Evas_Object *child;
   Elm_Tooltip *tt;
   Elm_Cursor *cur;
   Elm_Theme *th2, *thdef;

   API_ENTRY return;

   thdef = elm_theme_default_get();
   if (!th) th = thdef;
   if (!force)
     {
        th2 = sd->theme;
        if (!th2) th2 = thdef;
        while (th2)
          {
             if (th2 == th)
               {
                  force = EINA_TRUE;
                  break;
               }
             if (th2 == thdef) break;
             th2 = th2->ref_theme;
             if (!th2) th2 = thdef;
          }
     }
   if (!force) return;
   for (unsigned int i = 0; i < eina_array_count(sd->children); ++i)
     {
        child = eina_array_data_get(sd->children, i);
        if (elm_widget_is(child))
          elm_widget_theme_specific(child, th, force);
     }
   if (sd->hover_obj) elm_widget_theme(sd->hover_obj);
   EINA_LIST_FOREACH(sd->tooltips, l, tt)
     elm_tooltip_theme(tt);
   EINA_LIST_FOREACH(sd->cursors, l, cur)
     elm_cursor_theme(cur);
   efl_ui_widget_theme_apply(obj);
}

EOLIAN static Eina_Error
_efl_ui_widget_theme_apply(Eo *obj, Elm_Widget_Smart_Data *_pd EINA_UNUSED)
{
   _elm_widget_mirrored_reload(obj);

   return EFL_UI_THEME_APPLY_ERROR_NONE;
}

/**
 * @internal
 *
 * Returns the widget's mirrored mode.
 *
 * @param obj The widget.
 * @return mirrored mode of the object.
 *
 **/
EOLIAN static Eina_Bool
_efl_ui_widget_efl_ui_i18n_mirrored_get(const Eo *obj EINA_UNUSED, Elm_Widget_Smart_Data *sd)
{
   return sd->is_mirrored;
}

/**
 * @internal
 *
 * Sets the widget's mirrored mode.
 *
 * @param obj The widget.
 * @param mirrored EINA_TRUE to set mirrored mode. EINA_FALSE to unset.
 */
EOLIAN static void
_efl_ui_widget_efl_ui_i18n_mirrored_set(Eo *obj, Elm_Widget_Smart_Data *sd, Eina_Bool mirrored)
{
   mirrored = !!mirrored;

   if (sd->is_mirrored == mirrored) return;

   sd->is_mirrored = mirrored;
   elm_widget_theme(obj);
}

/**
 * Returns the widget's mirrored mode setting.
 *
 * @param obj The widget.
 * @return mirrored mode setting of the object.
 *
 **/
EOLIAN static Eina_Bool
_efl_ui_widget_efl_ui_i18n_mirrored_automatic_get(const Eo *obj EINA_UNUSED, Elm_Widget_Smart_Data *sd)
{
   return sd->mirrored_auto_mode;
}

/**
 * @internal
 *
 * Sets the widget's mirrored mode setting.
 * When widget in automatic mode, it follows the system mirrored mode set by
 * elm_mirrored_set().
 * @param obj The widget.
 * @param automatic EINA_TRUE for auto mirrored mode. EINA_FALSE for manual.
 */
EOLIAN static void
_efl_ui_widget_efl_ui_i18n_mirrored_automatic_set(Eo *obj, Elm_Widget_Smart_Data *sd, Eina_Bool automatic)
{
   if (sd->mirrored_auto_mode != automatic)
     {
        sd->mirrored_auto_mode = automatic;

        if (automatic)
          {
             efl_ui_mirrored_set(obj, elm_config_mirrored_get());
          }
     }
}

/*
 * @internal
 *
 * Add myself as a sub object of parent object
 *
 * @see elm_widget_sub_object_add()
 */
EAPI Eina_Bool
elm_widget_sub_object_parent_add(Evas_Object *sobj)
{
   Eo *parent = NULL;

   parent = efl_parent_get(sobj);
   if (!efl_isa(parent, EFL_UI_WIDGET_CLASS))
     {
        ERR("You passed a wrong parent parameter (%p %s). "
            "Elementary widget's parent should be an elementary widget.", parent, evas_object_type_get(parent));
        return EINA_FALSE;
     }

   return elm_widget_sub_object_add(parent, sobj);
}

static int
_disabled_counter_get(Eo *widget)
{
   ELM_WIDGET_DATA_GET_OR_RETURN(widget, pd, -1);

   return pd->disabled;
}

static void
_mirror_disabled_state(Eo *obj, Elm_Widget_Smart_Data *pd, int disabled_delta)
{
   int prev_disabled = pd->disabled;

   pd->disabled = (pd->parent_obj ? _disabled_counter_get(pd->parent_obj) : 0) + disabled_delta;

   //The current disabled state is the same as the parent
   //when the parent is assigned or changed, no further action is required.
   if (((prev_disabled > 0 && pd->disabled > 0)) ||
       ((prev_disabled <= 0 && pd->disabled <= 0)))
     return;

   //we should not call disabled_set when things are invalidated
   //otherwise we will unleashe an amount of errors in efl_ui_layout
   if (efl_invalidated_get(obj)) return;

   if (pd->disabled > 0)
     {
        pd->disabled --;
        efl_ui_widget_disabled_set(obj, EINA_TRUE);
     }
   else
     {
        pd->disabled ++;
        efl_ui_widget_disabled_set(obj, EINA_FALSE);
     }
}

EOLIAN static void
_efl_ui_widget_widget_parent_set(Eo *obj, Elm_Widget_Smart_Data *pd, Efl_Ui_Widget *parent)
{
   Efl_Ui_Widget *old_parent;
   //check if we are in the subobject list of parents
   if (parent)
     {
        ELM_WIDGET_DATA_GET_OR_RETURN(parent, ppd);
        EINA_SAFETY_ON_FALSE_RETURN(eina_array_find(ppd->children, obj, NULL));
        if (ppd->parent_obj == parent)
          {
             CRI("ATTEMPTING TO SET CHILD OF PARENT AS PARENT OF ITS OWN PARENT. THIS IS A BUG.");
             return;
          }
     }

   /* NOTE: In the following two lines, 'obj' is correct. Do not change it.
    * Due to elementary's scale policy, scale and prev_scale can be different in
    * some cases. This happens when obj's previous parent and new parent have
    * different scale value.
    * For example, if obj's previous parent's scale is 5 and new parent's scale
    * is 2 while obj's scale is 0. Then 'prev_pscale' is 5 and 'scale' is 2. So
    * we need to reset obj's scale to 5.
    * Note that each widget's scale is 1.0 by default.
    */
   double scale, prev_scale = efl_gfx_entity_scale_get(obj);
   Elm_Theme *th, *prev_th = elm_widget_theme_get(obj);
   int disabled_delta = pd->disabled - (pd->parent_obj ? _disabled_counter_get(pd->parent_obj) : 0);

   old_parent = pd->parent_obj;
   pd->parent_obj = parent;

   // now lets sync up all states
   if (pd->parent_obj)
     {
        Eina_Bool mirrored, pmirrored = efl_ui_mirrored_get(pd->parent_obj);
        scale = efl_gfx_entity_scale_get(obj);
        th = elm_widget_theme_get(obj);
        mirrored = efl_ui_mirrored_get(obj);

        if (!pd->on_create)
          {
             if (!EINA_DBL_EQ(scale, prev_scale) || (th != prev_th) ||
                 (pmirrored != mirrored))
               elm_widget_theme(obj);
          }
        if (_is_focused(obj)) _parents_focus(parent);
        elm_widget_display_mode_set(obj, evas_object_size_hint_display_mode_get(parent));
        _elm_widget_top_win_focused_set(obj, _elm_widget_top_win_focused_get(parent));
     }
   _mirror_disabled_state(obj, pd, disabled_delta);
   _full_eval(obj, pd);

   if (!efl_alive_get(obj)) return;
   if (old_parent && _elm_config->atspi_mode)
     {
        Efl_Access_Object *aparent;
        aparent = efl_provider_find(efl_parent_get(obj), EFL_ACCESS_OBJECT_MIXIN);
        if (aparent)
           efl_access_children_changed_del_signal_emit(aparent, obj);
     }

   if (pd->parent_obj && _elm_config->atspi_mode && efl_finalized_get(parent))
     {
        Efl_Access_Object *aparent;
        aparent = efl_provider_find(efl_parent_get(obj), EFL_ACCESS_OBJECT_MIXIN);
        if (aparent)
           efl_access_children_changed_added_signal_emit(aparent, obj);
     }
}

static void
_widget_add_sub(Eo *obj, Elm_Widget_Smart_Data *sd, Evas_Object *sobj)
{
   if (!sd->children) sd->children = eina_array_new(1);
   eina_array_push(sd->children, sobj);
   evas_object_data_set(sobj, "elm-parent", obj);
   _callbacks_add(sobj, obj);
}

static void
_widget_del_sub(Eo *obj, Elm_Widget_Smart_Data *sd, Evas_Object *sobj)
{
   eina_array_remove(sd->children, _keep, sobj);
   evas_object_data_del(sobj, "elm-parent");
   _callbacks_del(sobj, obj);
}

EOLIAN static Eina_Bool
_efl_ui_widget_widget_sub_object_add(Eo *obj, Elm_Widget_Smart_Data *sd, Evas_Object *sobj)
{
   Efl_Ui_Widget *parent;
   Eina_Bool is_widget;

   EINA_SAFETY_ON_FALSE_RETURN_VAL(efl_isa(sobj, EFL_GFX_ENTITY_INTERFACE), EINA_FALSE);
   EINA_SAFETY_ON_TRUE_RETURN_VAL(obj == sobj, EINA_FALSE);

   is_widget = elm_widget_is(sobj);
   //first make sure that we unregister the sobj from the parent
   if (is_widget)
     parent = efl_ui_widget_parent_get(sobj);
   else
     parent = evas_object_data_get(sobj, "elm-parent");
   if (parent == obj) return EINA_TRUE;
   if (parent)
     {
        if (sd->parent_obj == sobj)
          {
             CRI("ATTEMPTING TO SET CHILD OF PARENT AS PARENT OF ITS OWN PARENT. THIS IS A BUG.");
             return EINA_FALSE;
          }

        if (!efl_ui_widget_sub_object_del(parent, sobj))
          return EINA_FALSE;
     }

   //sobj does not have a parent here
   //first add it to our own children list
   _widget_add_sub(obj, sd, sobj);

   //and if it is a widget, please set the correct parent on the widget itself
   //the parent set method will take care of the property syncing etc.
   if (is_widget)
     efl_ui_widget_parent_set(sobj, obj);

   return EINA_TRUE;
}

EOLIAN static Eina_Bool
_efl_ui_widget_widget_sub_object_del(Eo *obj, Elm_Widget_Smart_Data *sd, Evas_Object *sobj)
{
   Evas_Object *sobj_parent = NULL;
   Eina_Bool is_widget;

   if (!sobj) return EINA_FALSE;

   EINA_SAFETY_ON_TRUE_RETURN_VAL(obj == sobj, EINA_FALSE);

   is_widget = _elm_widget_is(sobj);

   if (!is_widget) sobj_parent = evas_object_data_del(sobj, "elm-parent");

   if (sobj_parent && sobj_parent != obj)
     {
        static int abort_on_warn = -1;

        ERR("removing sub object %p (%s) from parent %p (%s), "
            "but elm-parent is different %p (%s)!",
            sobj, elm_widget_type_get(sobj), obj, elm_widget_type_get(obj),
            sobj_parent, elm_widget_type_get(sobj_parent));

        if (EINA_UNLIKELY(abort_on_warn == -1))
          {
             if (getenv("ELM_ERROR_ABORT")) abort_on_warn = 1;
             else abort_on_warn = 0;
          }
        if (abort_on_warn == 1) abort();

        return EINA_FALSE;
     }

   if (is_widget)
     {
	if (efl_ui_widget_parent_get(sobj) != obj)
          return EINA_FALSE;
        if (_is_focused(sobj))
          {
             elm_widget_tree_unfocusable_set(sobj, EINA_TRUE);
             elm_widget_tree_unfocusable_set(sobj, EINA_FALSE);
          }

        efl_ui_widget_parent_set(sobj, NULL);
     }

   if (sd->resize_obj == sobj) sd->resize_obj = NULL;

   _widget_del_sub(obj, sd, sobj);

   return EINA_TRUE;
}

/* protected function - for widget developers only */
EOLIAN static void
_efl_ui_widget_resize_object_set(Eo *obj, Elm_Widget_Smart_Data *sd, Eo *sobj)
{
   Evas_Object *parent;

   if (sd->resize_obj == sobj) return;
   EINA_SAFETY_ON_TRUE_RETURN(sobj && !efl_isa(sobj, EFL_CANVAS_OBJECT_CLASS));

   // orphan previous resize obj
   if (sd->resize_obj)
     {
        evas_object_clip_unset(sd->resize_obj);
        evas_object_smart_member_del(sd->resize_obj);

        if (_elm_widget_is(sd->resize_obj))
          {
             if (_is_focused(sd->resize_obj)) _parents_unfocus(obj);
          }
        elm_widget_sub_object_del(obj, sd->resize_obj);
     }

   sd->resize_obj = sobj;
   if (!sobj) return;

   // orphan new resize obj
   parent = evas_object_data_get(sobj, "elm-parent");
   if (parent && parent != obj)
     {
        ELM_WIDGET_DATA_GET(parent, sdp);

        /* should be there, just being paranoid */
        if (sdp)
          {
             if (sdp->resize_obj == sobj)
               elm_widget_resize_object_set(parent, NULL);
             else
               elm_widget_sub_object_del(parent, sobj);
          }
     }

   elm_widget_sub_object_add(obj, sobj);
   evas_object_smart_member_add(sobj, obj);
   _smart_reconfigure(obj, sd);
}

/*
 * @internal
 *
 * WARNING: the programmer is responsible, in the scenario of
 * exchanging a hover object, of cleaning the old hover "target"
 * before
 */
EAPI void
elm_widget_hover_object_set(Eo *obj, Evas_Object *sobj)
{
   Elm_Widget_Smart_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS);
   if (!sd) return;

   if (sd->hover_obj)
     {
        _callbacks_del(sd->hover_obj, obj);
     }
   sd->hover_obj = sobj;
   if (sd->hover_obj)
     {
        _callbacks_add(sobj, obj);
        _smart_reconfigure(obj, sd);
     }
}

EOLIAN static void
_efl_ui_widget_focus_allow_set(Eo *obj, Elm_Widget_Smart_Data *sd, Eina_Bool can_focus)
{
   can_focus = !!can_focus;

   if (sd->can_focus == can_focus) return;
   sd->can_focus = can_focus;
   if (sd->can_focus)
     {
        efl_event_callback_array_add(obj, focus_callbacks(), NULL);
     }
   else
     {
        efl_event_callback_array_del(obj, focus_callbacks(), NULL);
     }
     if (efl_finalized_get(obj))
       _full_eval(obj, sd);
}

EOLIAN static Eina_Bool
_efl_ui_widget_focus_allow_get(const Eo *obj EINA_UNUSED, Elm_Widget_Smart_Data *sd)
{
   return sd->can_focus;
}

EAPI Eina_Bool
elm_widget_child_can_focus_get(const Eo *obj)
{
   Elm_Widget_Smart_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS);
   if (!sd) return EINA_FALSE;

   return sd->logical.child_count > 0;
}


static int
_tree_unfocusable_counter_get(Eo *widget)
{
   ELM_WIDGET_DATA_GET_OR_RETURN(widget, pd, -1);

   return pd->tree_unfocusable;
}

/**
 * Evalulate tree number.
 *
 * This is here to support properties which are propagating through the widget tree. If this property is set to true,
 * every widget in the subtree, will also be automatically true.
 * When one of the widgets in the subtree then seperatly will be set to true, the unsetting on the original widget will not unset the flag automatically in this tree.
 *
 * The basic idea here is:
 * - The numeric number beeing bigger than 0, means that the property is true
 * - The difference between the number of the parent, and the number of the object, represents the boolean flag
 *   (0 means that the flag is equal to the one of the parent, 1 means that if the parent is false, this child is true).
 */
static int
_calculate_tree_number(int self_counter, int parent_counter, Eina_Bool flag)
{
   int distance = self_counter - parent_counter;

   if (flag)
     self_counter ++;
   else
     self_counter --;

   distance = self_counter - parent_counter;

   if ((distance < 0) || (distance > 1))
     {
        distance = MAX(MIN(flag, 1), 0);
        self_counter = parent_counter + distance;
     }

   return self_counter;
}

static void
_propagate_bool_property(Elm_Widget_Smart_Data *pd, Eina_Bool flag, void (*property_setting)(Eo *obj, Eina_Bool flag))
{
   Efl_Ui_Widget *subs;
   for (unsigned int i = 0; i < eina_array_count(pd->children); ++i)
     {
        subs = eina_array_data_get(pd->children, i);
        if (efl_isa(subs, EFL_UI_WIDGET_CLASS))
          property_setting(subs, flag);
     }
}

/**
 * @internal
 *
 * This API makes the widget object and its children to be unfocusable.
 *
 * This API can be helpful for an object to be deleted.
 * When an object will be deleted soon, it and its children may not
 * want to get focus (by focus reverting or by other focus controls).
 * Then, just use this API before deleting.
 *
 * @param obj The widget root of sub-tree
 * @param tree_unfocusable If true, set the object sub-tree as unfocusable
 *
 * @ingroup Widget
 */
EAPI void
elm_widget_tree_unfocusable_set(Eo *obj, Eina_Bool tree_unfocusable)
{
   Elm_Widget_Smart_Data *pd = efl_data_scope_safe_get(obj, MY_CLASS);
   EINA_SAFETY_ON_NULL_RETURN(pd);
   int old_tree_unfocusable;

   old_tree_unfocusable = pd->tree_unfocusable;

   pd->tree_unfocusable = _calculate_tree_number(pd->tree_unfocusable,
                                                (pd->parent_obj ? _tree_unfocusable_counter_get(pd->parent_obj) : 0),
                                                tree_unfocusable);

   if (old_tree_unfocusable != pd->tree_unfocusable)
     {
        _full_eval(obj, pd);
        _propagate_bool_property(pd, tree_unfocusable, elm_widget_tree_unfocusable_set);
     }
}

/**
 * @internal
 *
 * This returns true, if the object sub-tree is unfocusable.
 *
 * @param obj The widget root of sub-tree
 * @return EINA_TRUE if the object sub-tree is unfocusable
 *
 * @ingroup Widget
 */
EAPI Eina_Bool
elm_widget_tree_unfocusable_get(const Eo *obj)
{
   Elm_Widget_Smart_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS);
   EINA_SAFETY_ON_NULL_RETURN_VAL(sd, EINA_FALSE);

   return !!sd->tree_unfocusable;
}

/**
 * @internal
 *
 * Get the list of focusable child objects.
 *
 * This function returns list of child objects which can get focus.
 *
 * @param obj The parent widget
 * @return list of focusable child objects.
 *
 * @ingroup Widget
 */
EAPI Eina_List*
elm_widget_can_focus_child_list_get(const Eo *obj)
{
   Elm_Widget_Smart_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS);
   Eina_List *child_list = NULL;
   Evas_Object *child;

   if (!sd) return NULL;
   for (unsigned int i = 0; i < eina_array_count(sd->children); ++i)
     {
        child = eina_array_data_get(sd->children, i);
        if (!_elm_widget_is(child)) continue;
        if ((elm_widget_can_focus_get(child)) &&
            (evas_object_visible_get(child)) &&
            (!elm_widget_disabled_get(child)))
          child_list = eina_list_append(child_list, child);
        else
          {
             Eina_List *can_focus_list;
             can_focus_list = elm_widget_can_focus_child_list_get(child);
             if (can_focus_list)
               child_list = eina_list_merge(child_list, can_focus_list);
          }
     }

   return child_list;
}

/** @internal */
EAPI void
elm_widget_highlight_ignore_set(Eo *obj, Eina_Bool ignore)
{
   Elm_Widget_Smart_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS);
   if (!sd) return;

   sd->highlight_ignore = !!ignore;
}

/** @internal */
EAPI Eina_Bool
elm_widget_highlight_ignore_get(const Eo *obj)
{
   Elm_Widget_Smart_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS);
   if (!sd) return EINA_FALSE;

   return sd->highlight_ignore;
}

/** @internal */
EAPI void
elm_widget_highlight_in_theme_set(Eo *obj, Eina_Bool highlight)
{
   Elm_Widget_Smart_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS);
   if (!sd) return;

   sd->highlight_in_theme = !!highlight;
   /* FIXME: if focused, it should switch from one mode to the other */
}

void
_elm_widget_highlight_in_theme_update(Eo *obj)
{
   Evas_Object *top = elm_widget_top_get(obj);

   if (top && efl_isa(top, EFL_UI_WIN_CLASS))
     {
        _elm_win_focus_highlight_in_theme_update(
           top, elm_widget_highlight_in_theme_get(obj));
     }
}

/** @internal */
EAPI Eina_Bool
elm_widget_highlight_in_theme_get(const Eo *obj)
{
   Elm_Widget_Smart_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS);
   if (!sd) return EINA_FALSE;

   return sd->highlight_in_theme;
}

/** @internal */
EAPI void
elm_widget_access_highlight_in_theme_set(Eo *obj, Eina_Bool highlight)
{
   Elm_Widget_Smart_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS);
   if (!sd) return;

   sd->access_highlight_in_theme = !!highlight;
}

/** @internal */
EAPI Eina_Bool
elm_widget_access_highlight_in_theme_get(const Eo *obj)
{
   Elm_Widget_Smart_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS);
   if (!sd) return EINA_FALSE;

   return sd->access_highlight_in_theme;
}

/** @internal */
EAPI Eina_Bool
elm_widget_highlight_get(const Eo *obj)
{
   Elm_Widget_Smart_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS);
   if (!sd) return EINA_FALSE;

   return sd->highlighted;
}

EAPI Eina_Bool
elm_widget_is(const Evas_Object *obj)
{
   return _elm_widget_is(obj);
}

EAPI void
elm_widget_access_info_set(Efl_Ui_Widget *obj, const char *txt)
{
   efl_ui_widget_access_info_set(obj, txt);
}

EAPI const char *
elm_widget_access_info_get(const Efl_Ui_Widget *obj)
{
   return efl_ui_widget_access_info_get(obj);
}

EAPI Eo *
elm_widget_top_get(const Eo *obj)
{
   Efl_Ui_Widget *parent = elm_widget_parent_get(obj);
   if (parent)
     {
        if (!efl_isa(parent, EFL_UI_WIDGET_CLASS)) return NULL;
        return elm_widget_top_get(parent);
     }
   /* XXX const */
   return (Evas_Object *)obj;
}

EAPI Evas_Object *
elm_widget_parent_widget_get(const Evas_Object *obj)
{
   Evas_Object *parent;

   if (_elm_widget_is(obj))
     {
        ELM_WIDGET_DATA_GET(obj, sd);
        if (!sd) return NULL;
        parent = sd->parent_obj;
     }
   else
     {
        parent = evas_object_data_get(obj, "elm-parent");
        if (!parent) parent = evas_object_smart_parent_get(obj);
     }

   while (parent)
     {
        Evas_Object *elm_parent;
        if (_elm_widget_is(parent)) break;
        elm_parent = evas_object_data_get(parent, "elm-parent");
        if (elm_parent) parent = elm_parent;
        else parent = evas_object_smart_parent_get(parent);
     }
   return parent;
}

EAPI void
elm_widget_event_callback_add(Eo *obj, Elm_Event_Cb func, const void *data)
{
   API_ENTRY return;
   EINA_SAFETY_ON_NULL_RETURN(func);

   Elm_Event_Cb_Data *ecb = ELM_NEW(Elm_Event_Cb_Data);
   if (!ecb)
     {
        ERR("Failed to allocate memory");
        return;
     }
   ecb->func = func;
   ecb->data = data;
   sd->event_cb = eina_list_append(sd->event_cb, ecb);
}

EAPI void *
elm_widget_event_callback_del(Eo *obj, Elm_Event_Cb func, const void *data)
{
   API_ENTRY return NULL;
   EINA_SAFETY_ON_NULL_RETURN_VAL(func, NULL);
   Eina_List *l;
   Elm_Event_Cb_Data *ecd;

   EINA_LIST_FOREACH(sd->event_cb, l, ecd)
     if ((ecd->func == func) && (ecd->data == data))
       {
          free(ecd);
          sd->event_cb = eina_list_remove_list(sd->event_cb, l);
          return (void *)data;
       }

   return NULL;
}

static void
_propagate_event(void *data EINA_UNUSED, const Efl_Event *eo_event)
{
   Evas_Object *obj = eo_event->object;
   Evas_Object *parent = obj;
   Elm_Event_Cb_Data *ecd;
   Eina_List *l, *l_prev;

   if ((evas_focus_get(evas_object_evas_get(obj)) != elm_widget_top_get(obj)) &&
       efl_isa(obj, EFL_UI_WIN_CLASS))
     return;

   while (parent && !efl_input_processed_get(eo_event->info))
     {
        Elm_Widget_Smart_Data *sd = efl_data_scope_safe_get(parent, MY_CLASS);
        if (!sd) return;

        if (elm_widget_disabled_get(obj))
          {
             parent = sd->parent_obj;
             continue;
          }

        if (efl_ui_widget_input_event_handler(parent, eo_event, obj))
          return;

        EINA_LIST_FOREACH_SAFE(sd->event_cb, l, l_prev, ecd)
          {
             if (_propagate_event_legacy(parent, eo_event, obj, ecd))
               return;
          }

        parent = sd->parent_obj;
     }
}

/** @internal */
EAPI void
elm_widget_parent_highlight_set(Eo *obj, Eina_Bool highlighted)
{
   Elm_Widget_Smart_Data *sd =efl_data_scope_safe_get(obj, MY_CLASS);
   if (!sd) return;

   highlighted = !!highlighted;

   Evas_Object *o = elm_widget_parent_get(obj);

   if (o) elm_widget_parent_highlight_set(o, highlighted);

   sd->highlighted = highlighted;
}

EOLIAN static Evas_Object*
_efl_ui_widget_widget_parent_get(const Eo *obj EINA_UNUSED, Elm_Widget_Smart_Data *sd)
{
   return sd->parent_obj;
}

void
_elm_widget_focus_auto_show(Evas_Object *obj)
{
   Evas_Object *top = elm_widget_top_get(obj);
   if (top && efl_isa(top, EFL_UI_WIN_CLASS)) _elm_win_focus_auto_show(top);
}

void
_elm_widget_top_win_focused_set(Evas_Object *obj,
                                Eina_Bool top_win_focused)
{
   Evas_Object *child;
   API_ENTRY return;

   if (sd->top_win_focused == top_win_focused) return;
   for (unsigned int i = 0; i < eina_array_count(sd->children); ++i)
     {
        child = eina_array_data_get(sd->children, i);
        if (elm_widget_is(child))
          _elm_widget_top_win_focused_set(child, top_win_focused);
     }
   sd->top_win_focused = top_win_focused;

   if (sd->focused && !sd->top_win_focused)
     efl_ui_focus_object_on_focus_update(obj);
}

Eina_Bool
_elm_widget_top_win_focused_get(const Evas_Object *obj)
{
   API_ENTRY return EINA_FALSE;
   return sd->top_win_focused;
}

EOLIAN static void
_efl_ui_widget_disabled_set(Eo *obj EINA_UNUSED, Elm_Widget_Smart_Data *pd, Eina_Bool disabled)
{
   int old_state;

   old_state = pd->disabled;

   pd->disabled = _calculate_tree_number(pd->disabled, (pd->parent_obj ? _disabled_counter_get(pd->parent_obj) : 0), disabled);
   if (old_state != pd->disabled)
     {
        if (efl_finalized_get(obj))
          _full_eval(obj, pd);
        _propagate_bool_property(pd, disabled, efl_ui_widget_disabled_set);
     }
}

EOLIAN static Eina_Bool
_efl_ui_widget_disabled_get(const Eo *obj EINA_UNUSED, Elm_Widget_Smart_Data *pd)
{
   return pd->disabled > 0;
}

/**
 * @internal
 *
 * Get the focus region of the given widget.
 *
 * @return The region to show. If it's not a valid rectangle it will not show.
 *
 * The focus region is the area of a widget that should brought into the
 * visible area when the widget is focused. Mostly used to show the part of
 * an entry where the cursor is, for example. The area returned is relative
 * to the object @p obj.
 *
 * @param obj The widget object
 * @return The region to show, in relative coordinates. If it's not a valid
 *         rectangle (i.e. w or h <= 0) it will be ignored.
 *
 * @ingroup Widget
 */
EOLIAN static Eina_Rect
_efl_ui_widget_interest_region_get(const Eo *obj, Elm_Widget_Smart_Data *_pd EINA_UNUSED)
{
   Eina_Rect r = {};
   r.size = efl_gfx_entity_size_get(obj);
   return r;
}

EOLIAN static void
_efl_ui_widget_scroll_hold_push(Eo *obj, Elm_Widget_Smart_Data *sd)
{
   sd->scroll_hold++;
   if (sd->scroll_hold == 1)
     {
        if (_elm_scrollable_is(obj))
          {
             if (elm_widget_is_legacy(obj))
               elm_interface_scrollable_hold_set(obj, EINA_TRUE);
             else
               efl_ui_scrollable_scroll_hold_set(obj, EINA_TRUE);
          }
        else
          {
             Evas_Object *child;

             for (unsigned int i = 0; i < eina_array_count(sd->children); ++i)
               {
                  child = eina_array_data_get(sd->children, i);
                  if (elm_widget_is(child) && _elm_scrollable_is(child))
                    {
                       if (elm_widget_is_legacy(child))
                         elm_interface_scrollable_hold_set(child, EINA_TRUE);
                       else
                         efl_ui_scrollable_scroll_hold_set(child, EINA_TRUE);
                    }
               }
          }
     }
   if (sd->parent_obj) efl_ui_widget_scroll_hold_push(sd->parent_obj);
   // FIXME: on delete/reparent hold pop
}

EOLIAN static void
_efl_ui_widget_scroll_hold_pop(Eo *obj, Elm_Widget_Smart_Data *sd)
{
   sd->scroll_hold--;
   if (!sd->scroll_hold)
     {
        if (_elm_scrollable_is(obj))
          {
             if (elm_widget_is_legacy(obj))
               elm_interface_scrollable_hold_set(obj, EINA_FALSE);
             else
               efl_ui_scrollable_scroll_hold_set(obj, EINA_FALSE);
          }
        else
          {
             Evas_Object *child;

             for (unsigned int i = 0; i < eina_array_count(sd->children); ++i)
               {
                  child = eina_array_data_get(sd->children, i);
                  if (elm_widget_is(child) && _elm_scrollable_is(child))
                    {
                       if (elm_widget_is_legacy(child))
                         elm_interface_scrollable_hold_set(child, EINA_FALSE);
                       else
                         efl_ui_scrollable_scroll_hold_set(child, EINA_FALSE);
                     }
               }
          }
     }
   if (sd->parent_obj) efl_ui_widget_scroll_hold_pop(sd->parent_obj);
   if (sd->scroll_hold < 0) sd->scroll_hold = 0;
}

EAPI int
elm_widget_scroll_hold_get(const Eo *obj)
{
   Elm_Widget_Smart_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS);

   if (!sd) return 0;
   return sd->scroll_hold;
}

EOLIAN static void
_efl_ui_widget_scroll_freeze_push(Eo *obj, Elm_Widget_Smart_Data *sd)
{
   sd->scroll_freeze++;
   if (sd->scroll_freeze == 1)
     {
        if (_elm_scrollable_is(obj))
          {
             if (elm_widget_is_legacy(obj))
               elm_interface_scrollable_freeze_set(obj, EINA_TRUE);
             else
               efl_ui_scrollable_scroll_freeze_set(obj, EINA_TRUE);
          }
        else
          {
             Evas_Object *child;

             for (unsigned int i = 0; i < eina_array_count(sd->children); ++i)
               {
                  child = eina_array_data_get(sd->children, i);
                  if (elm_widget_is(child) && _elm_scrollable_is(child))
                    {
                       if (elm_widget_is_legacy(child))
                         elm_interface_scrollable_freeze_set(child, EINA_TRUE);
                       else
                         efl_ui_scrollable_scroll_freeze_set(child, EINA_TRUE);
                    }
               }
          }
     }
   if (sd->parent_obj) efl_ui_widget_scroll_freeze_push(sd->parent_obj);
   // FIXME: on delete/reparent freeze pop
}

EOLIAN static void
_efl_ui_widget_scroll_freeze_pop(Eo *obj, Elm_Widget_Smart_Data *sd)
{
   sd->scroll_freeze--;
   if (!sd->scroll_freeze)
     {
        if (_elm_scrollable_is(obj))
          {
             if (elm_widget_is_legacy(obj))
               elm_interface_scrollable_freeze_set(obj, EINA_FALSE);
             else
               efl_ui_scrollable_scroll_freeze_set(obj, EINA_FALSE);
          }
        else
          {
             Evas_Object *child;

             for (unsigned int i = 0; i < eina_array_count(sd->children); ++i)
               {
                  child = eina_array_data_get(sd->children, i);

                  if (elm_widget_is(child) && _elm_scrollable_is(child))
                    {
                       if (elm_widget_is_legacy(child))
                         elm_interface_scrollable_freeze_set(child, EINA_FALSE);
                       else
                         efl_ui_scrollable_scroll_freeze_set(child, EINA_FALSE);
                    }
               }
          }
     }
   if (sd->parent_obj) efl_ui_widget_scroll_freeze_pop(sd->parent_obj);
   if (sd->scroll_freeze < 0) sd->scroll_freeze = 0;
}

EAPI int
elm_widget_scroll_freeze_get(const Eo *obj)
{
   Elm_Widget_Smart_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS);

   if (!sd) return 0;
   return sd->scroll_freeze;
}

EOLIAN static void
_efl_ui_widget_efl_gfx_entity_scale_set(Eo *obj, Elm_Widget_Smart_Data *sd, double scale)
{
   if (scale < 0.0) scale = 0.0;
   if (!EINA_DBL_EQ(sd->scale, scale))
     {
        sd->scale = scale;
        elm_widget_theme(obj);
     }
}

EOLIAN static double
_efl_ui_widget_efl_gfx_entity_scale_get(const Eo *obj EINA_UNUSED, Elm_Widget_Smart_Data *sd)
{
   // FIXME: save walking up the tree by storing/caching parent scale
   if (EINA_DBL_EQ(sd->scale, 0.0))
     {
        if (sd->parent_obj)
          {
             return efl_gfx_entity_scale_get(sd->parent_obj);
          }
        else
          {
             return 1.0;
          }
     }
   return sd->scale;
}

EAPI void
elm_widget_theme_set(Evas_Object *obj, Elm_Theme *th)
{
   Elm_Widget_Smart_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS);
   if (!sd) return;

   Eina_Bool apply = EINA_FALSE;
   if (sd->theme != th)
     {
        if (elm_widget_theme_get(obj) != th) apply = EINA_TRUE;
        if (sd->theme) elm_theme_free(sd->theme);
        sd->theme = th;
        if (th) efl_ref(th->eo_theme);
        if (apply) elm_widget_theme(obj);
     }
}

EAPI void
elm_widget_part_text_set(Eo *obj, const char *part, const char *label)
{
   /* legacy support: combobox was special (internal entry is text object). */
   if (efl_isa(obj, ELM_COMBOBOX_CLASS))
     _elm_combobox_part_text_set(obj, part, label);
   else if (efl_isa(obj, EFL_UI_LAYOUT_BASE_CLASS))
     elm_layout_text_set(obj, part, label);
}

EAPI const char*
elm_widget_part_text_get(const Eo *obj, const char *part)
{
   /* legacy support: combobox was special (internal entry is text object). */
   if (efl_isa(obj, ELM_COMBOBOX_CLASS))
     return _elm_combobox_part_text_get(obj, part);
   else if (efl_isa(obj, EFL_UI_LAYOUT_BASE_CLASS))
     return elm_layout_text_get(obj, part);

   return NULL;
}

static Elm_Translate_String_Data *
_translate_string_data_get(Eina_Inlist *translate_strings, const char *part)
{
   Elm_Translate_String_Data *ts;
   Eina_Stringshare *str;

   if (!translate_strings) return NULL;

   str = eina_stringshare_add(part);
   EINA_INLIST_FOREACH(translate_strings, ts)
     {
        if (ts->id == str) break;
     }

   eina_stringshare_del(str);

   return ts;
}

static Elm_Translate_String_Data *
_part_text_translatable_set(Eina_Inlist **translate_strings, const char *part, Eina_Bool translatable, Eina_Bool preset)
{
   Eina_Inlist *t;
   Elm_Translate_String_Data *ts;
   t = *translate_strings;
   ts = _translate_string_data_get(t, part);

   if (translatable)
     {
        if (!ts)
          {
             ts = ELM_NEW(Elm_Translate_String_Data);
             if (!ts) return NULL;

             ts->id = eina_stringshare_add(part);
             t = eina_inlist_append(t, (Eina_Inlist*) ts);
          }
        if (preset) ts->preset = EINA_TRUE;
     }
   //Delete this exist one if this part has been not preset.
   //see elm_widget_part_text_translatable_set()
   else if (ts && ((preset) || (!ts->preset)))
     {
        t = eina_inlist_remove(t, EINA_INLIST_GET(ts));
        eina_stringshare_del(ts->id);
        eina_stringshare_del(ts->domain);
        eina_stringshare_del(ts->string);
        ELM_SAFE_FREE(ts, free);
     }

   *translate_strings = t;

   return ts;
}

/* internal */
void
elm_widget_part_translatable_text_set(Eo *obj, const char *part, const char *label, const char *domain)
{
   Elm_Translate_String_Data *ts;
   Elm_Widget_Smart_Data *sd;

   sd = efl_data_scope_safe_get(obj, MY_CLASS);
   if (!sd) return;

   if (!label)
     {
        _part_text_translatable_set(&sd->translate_strings, part, EINA_FALSE,
                                    EINA_FALSE);
     }
   else
     {
        ts = _part_text_translatable_set(&sd->translate_strings, part,
                                         EINA_TRUE, EINA_FALSE);
        if (!ts) return;
        if (!ts->string) ts->string = eina_stringshare_add(label);
        else eina_stringshare_replace(&ts->string, label);
        if (!ts->domain) ts->domain = eina_stringshare_add(domain);
        else eina_stringshare_replace(&ts->domain, domain);
#ifdef HAVE_GETTEXT
        if (label[0]) label = dgettext(domain, label);
#endif
     }

   sd->on_translate = EINA_TRUE;
   elm_widget_part_text_set(obj, part, label);
   sd->on_translate = EINA_FALSE;
}

/* legacy only */
EAPI void
elm_widget_domain_part_text_translatable_set(Eo *obj, const char *part, const char *domain, Eina_Bool translatable)
{
   Elm_Translate_String_Data *ts;
   Elm_Widget_Smart_Data *sd;
   const char *text = NULL;

   sd = efl_data_scope_safe_get(obj, MY_CLASS);
   if (!sd) return;

   ts = _part_text_translatable_set(&sd->translate_strings, part,
                                    translatable, EINA_TRUE);
   if (!ts) return;
   if (!ts->domain) ts->domain = eina_stringshare_add(domain);
   else eina_stringshare_replace(&ts->domain, domain);

   text = elm_widget_part_text_get(obj, part);
   if (!text || !text[0]) return;

   if (!ts->string) ts->string = eina_stringshare_add(text);

//Try to translate text since we don't know the text is already translated.
#ifdef HAVE_GETTEXT
   text = dgettext(domain, text);
#endif
   sd->on_translate = EINA_TRUE;
   elm_widget_part_text_set(obj, part, text);
   sd->on_translate = EINA_FALSE;
}

/* internal */
const char *
elm_widget_part_translatable_text_get(const Eo *obj, const char *part, const char **domain)
{
   Elm_Widget_Smart_Data *sd;
   Elm_Translate_String_Data *ts;

   if (domain) *domain = NULL;

   sd = efl_data_scope_safe_get(obj, MY_CLASS);
   if (!sd) return NULL;

   ts = _translate_string_data_get(sd->translate_strings, part);
   if (!ts) return NULL;

   if (domain) *domain = ts->domain;
   return ts->string;
}

EOLIAN static void
_efl_ui_widget_efl_ui_l10n_translation_update(Eo *obj EINA_UNUSED, Elm_Widget_Smart_Data *sd)
{
   Evas_Object *child;

   for (unsigned int i = 0; i < eina_array_count(sd->children); ++i)
     {
        child = eina_array_data_get(sd->children, i);
        if (elm_widget_is(child))
          efl_ui_l10n_translation_update(child);

     }

   if (sd->hover_obj) efl_ui_l10n_translation_update(sd->hover_obj);

#ifdef HAVE_GETTEXT
   Elm_Translate_String_Data *ts;
   EINA_INLIST_FOREACH(sd->translate_strings, ts)
     {
        if (!ts->string) continue;
        const char *s = dgettext(ts->domain, ts->string);
        sd->on_translate = EINA_TRUE;
        elm_widget_part_text_set(obj, ts->id, s);
        sd->on_translate = EINA_FALSE;
     }
#endif
   efl_event_callback_legacy_call(obj, EFL_UI_WIDGET_EVENT_LANGUAGE_CHANGED, NULL);
}

EOLIAN static void
_efl_ui_widget_access_info_set(Eo *obj EINA_UNUSED, Elm_Widget_Smart_Data *sd, const char *txt)
{
   eina_stringshare_replace(&sd->access_info, txt);
}

EOLIAN static const char*
_efl_ui_widget_access_info_get(const Eo *obj EINA_UNUSED, Elm_Widget_Smart_Data *sd)
{
   return sd->access_info;
}


EAPI void
elm_widget_scroll_hold_push(Efl_Ui_Widget *obj)
{
   efl_ui_widget_scroll_hold_push(obj);
}

EAPI void
elm_widget_scroll_hold_pop(Efl_Ui_Widget *obj)
{
   efl_ui_widget_scroll_hold_pop(obj);
}

EAPI void
elm_widget_scroll_freeze_push(Efl_Ui_Widget *obj)
{
   efl_ui_widget_scroll_freeze_push(obj);
}

EAPI void
elm_widget_scroll_freeze_pop(Efl_Ui_Widget *obj)
{
   efl_ui_widget_scroll_freeze_pop(obj);
}


EAPI Elm_Theme *
elm_widget_theme_get(const Evas_Object *obj)
{
   Elm_Widget_Smart_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS);
   if (!sd) return NULL;

   if (!sd->theme)
     {
        if (sd->parent_obj)
           return elm_widget_theme_get(sd->parent_obj);
        else return NULL;
     }
   return sd->theme;
}

EOLIAN static Eina_Error
_efl_ui_widget_style_set(Eo *obj, Elm_Widget_Smart_Data *sd, const char *style)
{
   if (!elm_widget_is_legacy(obj) && efl_finalized_get(obj))
     {
        ERR("Efl.Ui.Widget.style can only be set before finalize!");
        return EFL_UI_THEME_APPLY_ERROR_GENERIC;
     }

   if (eina_stringshare_replace(&sd->style, style))
      return elm_widget_theme(obj);

   return EFL_UI_THEME_APPLY_ERROR_NONE;
}

EOLIAN static const char*
_efl_ui_widget_style_get(const Eo *obj EINA_UNUSED, Elm_Widget_Smart_Data *sd)
{
   const char *ret;
   ret = "default";
   if (sd->style) ret = sd->style;

   return ret;
}

EAPI void
elm_widget_tooltip_add(Eo *obj, Elm_Tooltip *tt)
{
   Elm_Widget_Smart_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS);
   if (!sd) return;

   sd->tooltips = eina_list_append(sd->tooltips, tt);
}

EAPI void
elm_widget_tooltip_del(Eo *obj, Elm_Tooltip *tt)
{
   Elm_Widget_Smart_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS);
   if (!sd) return;

   sd->tooltips = eina_list_remove(sd->tooltips, tt);
}

EAPI void
elm_widget_cursor_add(Eo *obj, Elm_Cursor *cur)
{
   Elm_Widget_Smart_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS);
   if (!sd) return;

   sd->cursors = eina_list_append(sd->cursors, cur);
}

EAPI void
elm_widget_cursor_del(Eo *obj, Elm_Cursor *cur)
{
   Elm_Widget_Smart_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS);
   if (!sd) return;

   sd->cursors = eina_list_remove(sd->cursors, cur);
}

EAPI void
elm_widget_scroll_lock_set(Eo *obj, Efl_Ui_Layout_Orientation block)
{
   Elm_Widget_Smart_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS);
   Eina_Bool lx, ly;

   if (!sd) return;
   lx = !!(block & EFL_UI_LAYOUT_ORIENTATION_HORIZONTAL);
   ly = !!(block & EFL_UI_LAYOUT_ORIENTATION_VERTICAL);
   if (sd->scroll_x_locked != lx)
     {
        sd->scroll_x_locked = lx;
        _propagate_x_drag_lock(obj, lx ? 1 : -1);
     }
   if (sd->scroll_y_locked != ly)
     {
        sd->scroll_y_locked = ly;
        _propagate_y_drag_lock(obj, ly ? 1 : -1);
     }
}

EAPI Efl_Ui_Layout_Orientation
elm_widget_scroll_lock_get(const Eo *obj)
{
   Elm_Widget_Smart_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS);
   Efl_Ui_Layout_Orientation block = EFL_UI_LAYOUT_ORIENTATION_DEFAULT;

   if (!sd) return block;
   if (sd->scroll_x_locked) block |= EFL_UI_LAYOUT_ORIENTATION_HORIZONTAL;
   if (sd->scroll_y_locked) block |= EFL_UI_LAYOUT_ORIENTATION_VERTICAL;

   return block;
}

EAPI int
elm_widget_scroll_child_locked_x_get(const Eo *obj)
{
   Elm_Widget_Smart_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS);
   if (!sd) return EINA_FALSE;
   return sd->child_drag_x_locked;
}

EAPI int
elm_widget_scroll_child_locked_y_get(const Eo *obj)
{
   Elm_Widget_Smart_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS);
   if (!sd) return EINA_FALSE;
   return sd->child_drag_y_locked;
}

EAPI Eina_Error
elm_widget_theme_object_set(Evas_Object *obj, Evas_Object *edj, const char *wname, const char *welement, const char *wstyle)
{
   Elm_Widget_Smart_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS);
   if (!sd) return EFL_UI_THEME_APPLY_ERROR_GENERIC;

   if (eina_streq(welement, "base"))
     welement = NULL;
   if (eina_streq(wstyle, "default"))
     wstyle = NULL;
   return _elm_theme_object_set(obj, edj, wname, welement, wstyle);
}

static void
_convert(Efl_Dbg_Info *info, Eina_Iterator *ptr_list)
{
   void *p;
   int i = 0;

   EINA_ITERATOR_FOREACH(ptr_list, p)
     {
        char name[100];

        snprintf(name, sizeof(name), "Candidate %d", i);

        EFL_DBG_INFO_APPEND(info, name, EINA_VALUE_TYPE_UINT64, p);
        i++;
     }

   eina_iterator_free(ptr_list);
}

EOLIAN static void
_efl_ui_widget_efl_object_dbg_info_get(Eo *eo_obj, Elm_Widget_Smart_Data *_pd EINA_UNUSED, Efl_Dbg_Info *root)
{
   efl_dbg_info_get(efl_super(eo_obj, MY_CLASS), root);
   Efl_Ui_Focus_Relations *rel = NULL;
   Efl_Dbg_Info *focus, *group = EFL_DBG_INFO_LIST_APPEND(root, MY_CLASS_NAME);

   EFL_DBG_INFO_APPEND(group, "Wid-Type", EINA_VALUE_TYPE_STRING, elm_widget_type_get(eo_obj));
   EFL_DBG_INFO_APPEND(group, "Style", EINA_VALUE_TYPE_STRING, elm_widget_style_get(eo_obj));
   EFL_DBG_INFO_APPEND(group, "Layer", EINA_VALUE_TYPE_INT,
         (int) evas_object_layer_get(eo_obj));
   EFL_DBG_INFO_APPEND(group, "Scale", EINA_VALUE_TYPE_DOUBLE,
         evas_object_scale_get(eo_obj));
   EFL_DBG_INFO_APPEND(group, "Has focus", EINA_VALUE_TYPE_CHAR,
         elm_object_focus_get(eo_obj));
   EFL_DBG_INFO_APPEND(group, "Can focus", EINA_VALUE_TYPE_CHAR,
         elm_widget_can_focus_get(eo_obj));
   EFL_DBG_INFO_APPEND(group, "Disabled", EINA_VALUE_TYPE_CHAR,
         elm_widget_disabled_get(eo_obj));
   EFL_DBG_INFO_APPEND(group, "Mirrored", EINA_VALUE_TYPE_CHAR,
         efl_ui_mirrored_get(eo_obj));
   EFL_DBG_INFO_APPEND(group, "Tree Unfocusable", EINA_VALUE_TYPE_CHAR,
         elm_widget_tree_unfocusable_get(eo_obj));
   EFL_DBG_INFO_APPEND(group, "Automatic mirroring", EINA_VALUE_TYPE_CHAR,
         efl_ui_mirrored_automatic_get(eo_obj));

   rel = efl_ui_focus_manager_fetch(_pd->focus.manager, eo_obj);
   if (rel)
     {
        focus = EFL_DBG_INFO_LIST_APPEND(group, "Focus");

        EFL_DBG_INFO_APPEND(focus, "logical", EINA_VALUE_TYPE_CHAR, rel->logical );
        EFL_DBG_INFO_APPEND(focus, "manager", EINA_VALUE_TYPE_UINT64, _pd->focus.manager);
        EFL_DBG_INFO_APPEND(focus, "parent", EINA_VALUE_TYPE_UINT64, rel->parent);
        EFL_DBG_INFO_APPEND(focus, "next", EINA_VALUE_TYPE_UINT64 , rel->next);
        EFL_DBG_INFO_APPEND(focus, "prev", EINA_VALUE_TYPE_UINT64 , rel->prev);

        EFL_DBG_INFO_APPEND(focus, "redirect", EINA_VALUE_TYPE_UINT64 , rel->redirect);

#define ADD_PTR_LIST(name) \
        Efl_Dbg_Info* name = EFL_DBG_INFO_LIST_APPEND(focus, ""#name""); \
        _convert(name, rel->name);

        ADD_PTR_LIST(top)
        ADD_PTR_LIST(down)
        ADD_PTR_LIST(right)
        ADD_PTR_LIST(left)

#undef ADD_PTR_LIST

     }

   //if that's a focus manager, give useful information like the border elements
   if (efl_isa(eo_obj, EFL_UI_FOCUS_MANAGER_INTERFACE))
     {
        Efl_Dbg_Info *border;

        focus = EFL_DBG_INFO_LIST_APPEND(group, "Focus Manager");
        border = EFL_DBG_INFO_LIST_APPEND(focus, "Border Elements");

        _convert(border,
          efl_ui_focus_manager_border_elements_get(eo_obj)
        );

        EFL_DBG_INFO_APPEND(focus, "redirect", EINA_VALUE_TYPE_UINT64,
          efl_ui_focus_manager_redirect_get(eo_obj));
     }
}

EAPI Eina_Bool
elm_widget_is_check(const Evas_Object *obj)
{
   static int abort_on_warn = -1;
   if (elm_widget_is(obj))
     return EINA_TRUE;

   ERR("Passing Object: %p.", obj);
   if (abort_on_warn == -1)
     {
        if (getenv("ELM_ERROR_ABORT")) abort_on_warn = 1;
        else abort_on_warn = 0;
     }
   if (abort_on_warn == 1) abort();
   return EINA_FALSE;
}

/* If you changed a legacy widget's class name,
 * please update the "legacy_type_table". */
EAPI const char *
elm_widget_type_get(const Evas_Object *obj)
{
   const char *ret;
   int i;

   API_ENTRY return NULL;

   ret = efl_class_name_get(efl_class_get(obj));

   /* If the given widget is created for legacy,
    * convert type name to legacy. */
   if (elm_widget_is_legacy(obj))
     {
        for (i = 0; legacy_type_table[i][0] ; i++)
          {
             if (eina_streq(ret, legacy_type_table[i][0]))
               return legacy_type_table[i][1];
          }
     }

   return ret;
}

EAPI Eina_Bool
elm_widget_type_check(const Evas_Object *obj,
                      const char *type,
                      const char *func)
{
   const char *provided, *expected = "(unknown)";
   static int abort_on_warn = -1;

   provided = elm_widget_type_get(obj);
   /* TODO: eventually migrate to check_ptr version */
   if (evas_object_smart_type_check(obj, type)) return EINA_TRUE;
   if (type) expected = type;
   if ((!provided) || (!provided[0]))
     {
        provided = evas_object_type_get(obj);
        if ((!provided) || (!provided[0]))
          provided = "(unknown)";
     }
   ERR("Passing Object: %p in function: %s, of type: '%s' when expecting"
       " type: '%s'", obj, func, provided, expected);
   if (abort_on_warn == -1)
     {
        if (getenv("ELM_ERROR_ABORT")) abort_on_warn = 1;
        else abort_on_warn = 0;
     }
   if (abort_on_warn == 1) abort();
   return EINA_FALSE;
}

/** @internal */
EAPI Evas_Object *
elm_widget_name_find(const Eo *obj, const char *name, int recurse)
{
   Evas_Object *child;
   const char *s;
   INTERNAL_ENTRY NULL;

   if (!name) return NULL;
   if (!_elm_widget_is(obj)) return NULL;

   for (unsigned int i = 0; i < eina_array_count(sd->children); ++i)
     {
        child = eina_array_data_get(sd->children, i);
        s = evas_object_name_get(child);
        if ((s) && (!strcmp(s, name))) return child;
        if ((recurse != 0) &&
            ((child = elm_widget_name_find(child, name, recurse - 1))))
          return child;
     }
   if (sd->hover_obj)
     {
        s = evas_object_name_get(sd->hover_obj);
        if ((s) && (!strcmp(s, name))) return sd->hover_obj;
        if ((recurse != 0) &&
            ((child = elm_widget_name_find(sd->hover_obj, name, recurse - 1))))
          return child;
     }
   return NULL;
}

/**
 * @internal
 *
 * Split string in words
 *
 * @param str Source string
 * @return List of const words
 *
 * @see elm_widget_stringlist_free()
 * @ingroup Widget
 */
EAPI Eina_List *
elm_widget_stringlist_get(const char *str)
{
   Eina_List *list = NULL;
   const char *s, *b;
   if (!str) return NULL;
   for (b = s = str; 1; s++)
     {
        if ((*s == ' ') || (!*s))
          {
             char *t = malloc(s - b + 1);
             if (t)
               {
                  strncpy(t, b, s - b);
                  t[s - b] = 0;
                  list = eina_list_append(list, eina_stringshare_add(t));
                  free(t);
               }
             b = s + 1;
          }
        if (!*s) break;
     }
   return list;
}

EAPI void
elm_widget_stringlist_free(Eina_List *list)
{
   const char *s;
   EINA_LIST_FREE(list, s)
     eina_stringshare_del(s);
}

/* internal */
EAPI void
elm_widget_focus_mouse_up_handle(Eo *obj)
{
   Elm_Widget_Smart_Data *pd = efl_data_scope_get(obj, MY_CLASS);

   if (!_is_focusable(obj)) return;

   if (pd->focus.manager && !pd->focus.logical)
     {
        efl_ui_focus_util_focus(obj);
     }
}

/*
 * @internal
 *
 * Get the focus highlight geometry of a widget.
 *
 * @param obj Widget object for the focus highlight
 * @param x Focus highlight x coordinate
 * @param y Focus highlight y coordinate
 * @param w Focus highlight object width
 * @param h Focus highlight object height
 * @param is_next @c EINA_TRUE if this request is for the new focused object,
 * @c EINA_FALSE if this request is for the previously focused object. This
 * information becomes important when the focus highlight is changed inside one
 * widget.
 *
 * @ingroup Widget
 */
/*
 * @internal
 *
 * Get the 'focus_part' geometry if there is any
 *
 * This queries if there is a 'focus_part' request from the edc style. If edc
 * style offers 'focus_part' edje data item, this function requests for the
 * geometry of a specific part which is described in 'focus_part' edje data.
 *
 * @param obj Widget object for the focus highlight
 * @param x Focus highlight x coordinate
 * @param y Focus highlight y coordinate
 * @param w Focus highlight object width
 * @param h Focus highlight object height
 *
 * x, y, w, h already contain the object's geometry. If there is a 'focus_part'
 * support, these values will be updated accordingly or the values will be
 * remained as they were.
 *
 * @ingroup Widget
 */
EAPI void
elm_widget_focus_highlight_focus_part_geometry_get(const Evas_Object *obj,
                                                   Evas_Coord *x,
                                                   Evas_Coord *y,
                                                   Evas_Coord *w,
                                                   Evas_Coord *h)
{
   Evas_Coord tx = 0, ty = 0, tw = 0, th = 0;
   const char *target_hl_part = NULL;
   const Evas_Object *edje_obj = NULL;

   if (obj && efl_isa(obj, EFL_CANVAS_LAYOUT_CLASS))
     {
        edje_obj = obj;
        if (!(target_hl_part = edje_object_data_get(edje_obj, "focus_part")))
          return;
     }
   else if (obj && efl_isa(obj, EFL_UI_LAYOUT_BASE_CLASS))
     {
        edje_obj = elm_layout_edje_get(obj);
        if (!(target_hl_part = elm_layout_data_get(obj, "focus_part")))
          return;
     }
   else
     return;

  edje_object_part_geometry_get(edje_obj, target_hl_part,
                                &tx, &ty, &tw, &th);
  *x += tx;
  *y += ty;
  if (tw != *w) *w = tw;
  if (th != *h) *h = th;
}

EOLIAN static Eina_Rect
_efl_ui_widget_focus_highlight_geometry_get(const Eo *obj, Elm_Widget_Smart_Data *sd)
{
   Evas_Coord ox = 0, oy = 0, ow = 0, oh = 0;
   Evas_Object *scroller = (Evas_Object *)obj;
   Eina_Rect r = {};

   evas_object_geometry_get(obj, &r.x, &r.y, &r.w, &r.h);
   elm_widget_focus_highlight_focus_part_geometry_get(sd->resize_obj, &r.x, &r.y, &r.w, &r.h);

   if (_elm_config->focus_autoscroll_mode != ELM_FOCUS_AUTOSCROLL_MODE_BRING_IN)
     return r;

   while (scroller)
     {
        if (_elm_scrollable_is(scroller))
          {
             elm_interface_scrollable_content_viewport_geometry_get(scroller, &ox, &oy, &ow, &oh);

             if (r.y < oy)
               r.y = oy;
             else if ((oy + oh) < (r.y + r.h))
               r.y = (oy + oh - r.h);
             else if (r.x < ox)
               r.x = ox;
             else if ((ox + ow) < (r.x + r.w))
               r.x = (ox + ow - r.w);

             break;
          }
        scroller = elm_widget_parent_get(scroller);
     }

   return r;
}

EAPI void
elm_widget_activate(Evas_Object *obj, Efl_Ui_Activate act)
{
   Evas_Object *parent;
   Eina_Bool ret;

   ELM_WIDGET_CHECK(obj);

   ret = EINA_FALSE;

   ret = efl_ui_widget_on_access_activate(obj, act);

   if (ret) return;

   parent = elm_widget_parent_get(obj);
   if (parent)
     elm_widget_activate(parent, act);

   return;
}

/**
 * @internal
 *
 * Sets the widget and child widget's Evas_Display_Mode.
 *
 * @param obj The widget.
 * @param dispmode Evas_Display_Mode to set widget's mode.
 *
 * Widgets are resized by several reasons.
 * Evas_Display_Mode can help for widgets to get one more reason of resize.
 * For example, elm conform widget resizes it's contents when keypad state changed.
 * After keypad showing, conform widget can change child's Evas_Display_Mode.
 * @ingroup Widget
 */
/* Legacy only */
EAPI void
elm_widget_display_mode_set(Evas_Object *obj, Evas_Display_Mode dispmode)
{
   Evas_Display_Mode prev_dispmode;
   Evas_Object *child;

   API_ENTRY return;
   prev_dispmode = evas_object_size_hint_display_mode_get(obj);

   if ((prev_dispmode == dispmode) ||
       (prev_dispmode == EVAS_DISPLAY_MODE_DONT_CHANGE)) return;

   evas_object_size_hint_display_mode_set(obj, dispmode);

   for (unsigned int i = 0; i < eina_array_count(sd->children); ++i)
     {
        child = eina_array_data_get(sd->children, i);
        if (elm_widget_is(child))
          elm_widget_display_mode_set(child, dispmode);
     }
}

/**
 * @internal
 *
 * Returns the widget's focus move policy.
 *
 * @param obj The widget.
 * @return focus move policy of the object.
 *
 **/
EOLIAN static Efl_Ui_Focus_Move_Policy
_efl_ui_widget_focus_move_policy_get(const Eo *obj EINA_UNUSED, Elm_Widget_Smart_Data *sd)
{
   return (Efl_Ui_Focus_Move_Policy)sd->focus_move_policy;
}

/**
 * @internal
 *
 * Sets the widget's focus move policy.
 *
 * @param obj The widget.
 * @param policy Elm_Focus_Move_Policy to set object's focus move policy.
 */

EOLIAN static void
_efl_ui_widget_focus_move_policy_set(Eo *obj EINA_UNUSED, Elm_Widget_Smart_Data *sd, Efl_Ui_Focus_Move_Policy policy)
{
   if (sd->focus_move_policy == (Elm_Focus_Move_Policy)policy) return;
   sd->focus_move_policy = (Elm_Focus_Move_Policy)policy;
}

/**
 * Returns the widget's focus_move_policy mode setting.
 *
 * @param obj The widget.
 * @return focus_move_policy mode setting of the object.
 *
 **/
EOLIAN static Eina_Bool
_efl_ui_widget_focus_move_policy_automatic_get(const Eo *obj EINA_UNUSED, Elm_Widget_Smart_Data *sd)
{
   return sd->focus_move_policy_auto_mode;
}

/**
 * @internal
 *
 * Sets the widget's focus_move_policy mode setting.
 * When widget in automatic mode, it follows the system focus_move_policy mode set by
 * elm_config_focus_move_policy_set().
 * @param obj The widget.
 * @param automatic EINA_TRUE for auto focus_move_policy mode. EINA_FALSE for manual.
 */
EOLIAN static void
_efl_ui_widget_focus_move_policy_automatic_set(Eo *obj, Elm_Widget_Smart_Data *sd, Eina_Bool automatic)
{
   if (sd->focus_move_policy_auto_mode != automatic)
     {
        sd->focus_move_policy_auto_mode = automatic;

        if (automatic)
          {
             efl_ui_widget_focus_move_policy_set
               (obj, (Efl_Ui_Focus_Move_Policy)elm_config_focus_move_policy_get());
          }
     }
}

/**
 * @internal
 *
 * Sets the klass name of a widget.
 * @param obj The widget.
 * @param name Name of the klass to use.
 * @return Whether the name was different and thus replaced.
 */
EAPI Eina_Bool
elm_widget_theme_klass_set(Evas_Object *obj, const char *name)
{
   Elm_Widget_Smart_Data *pd = efl_data_scope_safe_get(obj, MY_CLASS);
   if (!pd) return EINA_FALSE;

   return eina_stringshare_replace(&(pd->klass), name);
}

/**
 * @internal
 *
 * Gets the klass name of a widget.
 * @param obj The widget.
 * @return The current klass name of internal canvas object.
 */
EAPI const char *
elm_widget_theme_klass_get(const Evas_Object *obj)
{
   Elm_Widget_Smart_Data *pd = efl_data_scope_safe_get(obj, MY_CLASS);
   if (!pd) return NULL;

   return (const char *)pd->klass;
}

/**
 * @internal
 *
 * Sets the element name of a widget.
 *
 * @param obj The widget.
 * @param name Name of the element to use.
 * @return Whether the name was different and thus replaced.
 */
EAPI Eina_Bool
elm_widget_theme_element_set(Evas_Object *obj, const char *name)
{
   Elm_Widget_Smart_Data *pd = efl_data_scope_safe_get(obj, MY_CLASS);
   if (!pd) return EINA_FALSE;

   if (eina_streq(name, "base"))
     name = NULL;

   return eina_stringshare_replace(&(pd->group), name);
}

/**
 * @internal
 *
 * Gets the element name of a widget.
 * @param obj The widget.
 * @return The current element name of internal canvas object.
 */
EAPI const char *
elm_widget_theme_element_get(const Evas_Object *obj)
{
   Elm_Widget_Smart_Data *pd = efl_data_scope_safe_get(obj, MY_CLASS);
   if (!pd) return NULL;

   return (const char *)pd->group;
}

/**
 * @internal
 *
 * Sets the style name of a widget.
 *
 * @param obj The widget.
 * @param name Name of the style to use.
 * @return Whether the name was different and thus replaced.
 */
EAPI Eina_Bool
elm_widget_theme_style_set(Evas_Object *obj, const char *name)
{
   Elm_Widget_Smart_Data *pd = efl_data_scope_safe_get(obj, MY_CLASS);
   if (!pd) return EINA_FALSE;

   if (eina_streq(name, "default"))
     name = NULL;

   return eina_stringshare_replace(&(pd->style), name);
}

/**
 * @internal
 *
 * Gets the style name of a widget.
 * @param obj The widget.
 * @return The current style name of internal canvas object.
 */
EAPI const char *
elm_widget_theme_style_get(const Evas_Object *obj)
{
   Elm_Widget_Smart_Data *pd = efl_data_scope_safe_get(obj, MY_CLASS);
   if (!pd) return NULL;

   return (const char *)pd->style;
}

/**
 * @internal
 *
 * Register sub object as a group of a widget and re-apply its theme.
 * @param obj The widget.
 * @param component A sub object to be added as an element of the widget.
 * @param name An element name of sub object.
 * @return Whether the style was successfully applied or not.
 */
EAPI Eina_Error
elm_widget_element_update(Evas_Object *obj, Evas_Object *component, const char *name)
{
   Eina_Error ret = EFL_UI_THEME_APPLY_ERROR_NONE;
   Eina_Bool changed = EINA_FALSE;
   const char *obj_group;
   Eina_Stringshare *group;

   obj_group = elm_widget_theme_element_get(obj);
   if (!obj_group)
     group = eina_stringshare_add(name);
   else
     group = eina_stringshare_printf("%s/%s", elm_widget_theme_element_get(obj), name);
   if (efl_isa(component, EFL_UI_WIDGET_CLASS))
     {
        changed |= elm_widget_theme_klass_set(component, elm_widget_theme_klass_get(obj));
        changed |= elm_widget_theme_element_set(component, (const char *)group);
        changed |= elm_widget_theme_style_set(component, elm_widget_theme_style_get(obj));
        if (changed)
          ret = efl_ui_widget_theme_apply(component);
     }
   else
     {
        ret = elm_widget_theme_object_set(obj, component,
                                   elm_widget_theme_klass_get(obj),
                                   (const char *)group,
                                   elm_widget_theme_style_get(obj));
     }
   eina_stringshare_del(group);

   return ret;
}

static void
_track_obj_del(void *data, Evas *e, Evas_Object *obj, void *event_info);

static void
_track_obj_update(Evas_Object *track, Evas_Object *obj)
{
   //Geometry
   Evas_Coord x, y, w, h;
   evas_object_geometry_get(obj, &x, &y, &w, &h);
   evas_object_geometry_set(track, x, y, w, h);

   //Visibility
   if (evas_object_visible_get(obj)) evas_object_show(track);
   else evas_object_hide(track);
}

static void
_track_obj_view_update(void *data, const Efl_Event *event)
{
   Elm_Widget_Item_Data *item = data;
   _track_obj_update(item->track_obj, event->object);
}

static void
_track_obj_view_del(void *data, const Efl_Event *event);

EFL_CALLBACKS_ARRAY_DEFINE(tracker_callbacks,
                          { EFL_GFX_ENTITY_EVENT_SIZE_CHANGED, _track_obj_view_update },
                          { EFL_GFX_ENTITY_EVENT_POSITION_CHANGED, _track_obj_view_update },
                          { EFL_GFX_ENTITY_EVENT_VISIBILITY_CHANGED, _track_obj_view_update },
                          { EFL_EVENT_DEL, _track_obj_view_del });

static void
_track_obj_view_del(void *data, const Efl_Event *event EINA_UNUSED)
{
   Elm_Widget_Item_Data *item = data;

   while (evas_object_ref_get(item->track_obj) > 0)
     evas_object_unref(item->track_obj);

   evas_object_event_callback_del(item->track_obj, EVAS_CALLBACK_DEL,
                                  _track_obj_del);
   evas_object_del(item->track_obj);
   item->track_obj = NULL;
}

static void
_track_obj_del(void *data, Evas *e EINA_UNUSED,
                    Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
{
   Elm_Widget_Item_Data *item = data;
   item->track_obj = NULL;

   if (!item->view) return;

   efl_event_callback_array_del(item->view, tracker_callbacks(), item);
}

static void
_elm_widget_item_signal_cb(void *data, Evas_Object *obj EINA_UNUSED, const char *emission,
                           const char *source)
{
   Elm_Widget_Item_Signal_Data *wisd = data;
   wisd->func(wisd->data, wisd->item, emission, source);
}

static void *
_elm_widget_item_signal_callback_list_get(Elm_Widget_Item_Data *item, Eina_List *position)
{
   Elm_Widget_Item_Signal_Data *wisd = eina_list_data_get(position);
   void *data;

   item->signals = eina_list_remove_list(item->signals, position);
   data = wisd->data;

   if (_elm_widget_is(item->view))
     elm_object_signal_callback_del(item->view,
                                    wisd->emission, wisd->source,
                                    _elm_widget_item_signal_cb);
   else if (efl_isa(item->view, EFL_CANVAS_LAYOUT_CLASS))
     edje_object_signal_callback_del_full(item->view,
                                          wisd->emission, wisd->source,
                                          _elm_widget_item_signal_cb, wisd);

   eina_stringshare_del(wisd->emission);
   eina_stringshare_del(wisd->source);
   free(wisd);

   return data;
}

#define ERR_NOT_SUPPORTED(item, method)  ERR("%s does not support %s API.", elm_widget_type_get(item->widget), method);

static void
_efl_del_cb(void *data EINA_UNUSED, const Efl_Event *event)
{
   Elm_Widget_Item_Data *item = efl_data_scope_get(event->object, ELM_WIDGET_ITEM_CLASS);
   ELM_WIDGET_ITEM_CHECK_OR_RETURN(item);
   if (item->del_func)
      item->del_func((void *) WIDGET_ITEM_DATA_GET(event->object), item->widget, item->eo_obj);
}

/**
 * @internal
 *
 * Allocate a new Elm_Widget_Item-derived structure.
 *
 * The goal of this structure is to provide common ground for actions
 * that a widget item have, such as the owner widget, callback to
 * notify deletion, data pointer and maybe more.
 *
 * @param widget the owner widget that holds this item, must be an elm_widget!
 * @param alloc_size any number greater than sizeof(Elm_Widget_Item) that will
 *        be used to allocate memory.
 *
 * @return allocated memory that is already zeroed out, or NULL on errors.
 *
 * @see elm_widget_item_new() convenience macro.
 * @see elm_widget_item_del() to release memory.
 * @ingroup Widget
 */
EOLIAN static Eo *
_elm_widget_item_efl_object_constructor(Eo *eo_item, Elm_Widget_Item_Data *item)
{
   Evas_Object *widget;
   widget = efl_parent_get(eo_item);

   if (!_elm_widget_is(widget))
     {
        ERR("Failed");
        return NULL;
     }

   eo_item = efl_constructor(efl_super(eo_item, ELM_WIDGET_ITEM_CLASS));

   EINA_MAGIC_SET(item, ELM_WIDGET_ITEM_MAGIC);

   item->widget = widget;
   item->eo_obj = eo_item;
   efl_event_callback_add(eo_item, EFL_EVENT_DEL, _efl_del_cb, NULL);

   return eo_item;
}

EOLIAN static void
_elm_widget_item_efl_object_destructor(Eo *eo_item, Elm_Widget_Item_Data *item)
{
   Elm_Translate_String_Data *ts;

   ELM_WIDGET_ITEM_CHECK_OR_RETURN(item);

   eina_stringshare_del(item->style);
   eina_stringshare_del(item->access_info);
   eina_stringshare_del(item->accessible_name);

   while (item->signals)
     _elm_widget_item_signal_callback_list_get(item, item->signals);

   while (item->translate_strings)
     {
        ts = EINA_INLIST_CONTAINER_GET(item->translate_strings,
                                       Elm_Translate_String_Data);
        eina_stringshare_del(ts->id);
        eina_stringshare_del(ts->domain);
        eina_stringshare_del(ts->string);
        item->translate_strings = eina_inlist_remove(item->translate_strings,
                                                     item->translate_strings);
        free(ts);
     }
   eina_hash_free(item->labels);

   efl_access_object_attributes_clear(eo_item);
   efl_access_removed(eo_item);

   EINA_MAGIC_SET(item, EINA_MAGIC_NONE);

   efl_destructor(efl_super(eo_item, ELM_WIDGET_ITEM_CLASS));
}

/**
 * @internal
 *
 * Releases widget item memory, calling back item_del_pre_hook() and
 * item_del_cb() if they exist.
 *
 * @param item a valid #Elm_Widget_Item to be deleted.
 *
 * If there is an Elm_Widget_Item::del_cb, then it will be called prior
 * to memory release. Note that elm_widget_item_pre_notify_del() calls
 * this function and then unset it, thus being useful for 2 step
 * cleanup whenever the del_cb may use any of the data that must be
 * deleted from item.
 *
 * The Elm_Widget_Item::view will be deleted (evas_object_del()) if it
 * is presented!
 *
 * Note that if item_del_pre_hook() returns @c EINA_TRUE, item free will be
 * deferred, or item will be freed here if it returns @c EINA_FALSE.
 *
 * @see elm_widget_item_del() convenience macro.
 * @ingroup Widget
 */
EOLIAN static void
_elm_widget_item_efl_object_invalidate(Eo *eo_item, Elm_Widget_Item_Data *item)
{
   Evas_Object *view;

   //Widget item delete callback
   elm_wdg_item_del_pre(item->eo_obj);

   view = item->view;
   if (item->view) efl_wref_del(item->view, &item->view);
   // FIXME: Is view an Efl.Ui or a legacy object ?
   evas_object_del(view);
   item->view = NULL;

   efl_invalidate(efl_super(eo_item, ELM_WIDGET_ITEM_CLASS));
}

EOLIAN static void
_elm_widget_item_del_pre(Eo *eo_item EINA_UNUSED, Elm_Widget_Item_Data *item EINA_UNUSED)
{
}

/**
 * @internal
 *
 * Notify object will be deleted without actually deleting it.
 *
 * This function will callback Elm_Widget_Item::del_cb if it is set
 * and then unset it so it is not called twice (ie: from
 * elm_widget_item_del()).
 *
 * @param item a valid #Elm_Widget_Item to be notified
 * @see elm_widget_item_pre_notify_del() convenience macro.
 * @ingroup Widget
 */
EOLIAN static void
_elm_widget_item_pre_notify_del(Eo *eo_item, Elm_Widget_Item_Data *item)
{
   ELM_WIDGET_ITEM_CHECK_OR_RETURN(item);
   if (!item->del_func) return;
   item->del_func((void *)WIDGET_ITEM_DATA_GET(eo_item), item->widget, item->eo_obj);
   item->del_func = NULL;
}

/**
 * @internal
 *
 * Set the function to notify when item is being deleted.
 *
 * This function will complain if there was a callback set already,
 * however it will set the new one.
 *
 * The callback will be called from elm_widget_item_pre_notify_del()
 * or elm_widget_item_del() will be called with:
 *   - data: the Elm_Widget_Item::data value.
 *   - obj: the Elm_Widget_Item::widget evas object.
 *   - event_info: the item being deleted.
 *
 * @param item a valid #Elm_Widget_Item to be notified
 * @see elm_widget_item_del_cb_set() convenience macro.
 * @ingroup Widget
 */
EOLIAN static void
_elm_widget_item_del_cb_set(Eo *eo_item EINA_UNUSED,
                            Elm_Widget_Item_Data *item,
                            Evas_Smart_Cb func)
{
   ELM_WIDGET_ITEM_CHECK_OR_RETURN(item);
   ELM_WIDGET_ITEM_RETURN_IF_ONDEL(item);

   if ((item->del_func) && (item->del_func != func))
     WRN("You're replacing a previously set del_cb %p of item %p with %p",
         item->del_func, item->eo_obj, func);

   item->del_func = func;
}

/**
 * @internal
 *
 * Get owner widget of this item.
 *
 * @param item a valid #Elm_Widget_Item to get data from.
 * @return owner widget of this item.
 * @ingroup Widget
 */
EOLIAN static Evas_Object *
_elm_widget_item_widget_get(const Eo *eo_item EINA_UNUSED, Elm_Widget_Item_Data *item)
{
   ELM_WIDGET_ITEM_CHECK_OR_RETURN(item, NULL);
   ELM_WIDGET_ITEM_RETURN_IF_ONDEL(item, NULL);

   return item->widget;
}

EAPI Eina_Bool
_elm_widget_onscreen_is(const Evas_Object *widget)
{
   Evas_Object *parent = (Evas_Object *)widget;
   Eina_Rectangle r1, r2;

   Evas *evas = evas_object_evas_get(widget);
   if (!evas) return EINA_FALSE;

   evas_object_geometry_get(widget, &r1.x, &r1.y, &r1.w, &r1.h);
   if (eina_rectangle_is_empty(&r1))
     return EINA_FALSE;

   // window does not have to check viewport and geometry
   if (efl_isa(widget, EFL_UI_WIN_CLASS))
      return EINA_TRUE;

   // check if on canvas
   evas_output_viewport_get(evas, &r2.x, &r2.y, &r2.w, &r2.h);
   if (!eina_rectangles_intersect(&r1, &r2))
     return EINA_FALSE;

   // check if inside scrollable parent viewport
   do {
      parent = elm_widget_parent_get(parent);
      if (parent && !evas_object_visible_get(parent))
        return EINA_FALSE;
      if (parent && efl_isa(parent, ELM_INTERFACE_SCROLLABLE_MIXIN))
        {
           evas_object_geometry_get(parent, &r2.x, &r2.y, &r2.w, &r2.h);
           if (!eina_rectangles_intersect(&r1, &r2))
             return EINA_FALSE;
        }
   } while (parent && (parent != elm_widget_top_get(widget)));

   return EINA_TRUE;
}

EAPI Eina_Bool
_elm_widget_item_onscreen_is(const Elm_Object_Item *item)
{
   Eina_Rectangle r1, r2;
   Elm_Widget_Item_Data *id = efl_data_scope_get(item, ELM_WIDGET_ITEM_CLASS);
   if (!id || !id->view) return EINA_FALSE;

   if (!evas_object_visible_get(id->view))
     return EINA_FALSE;

   if (!_elm_widget_onscreen_is(id->widget))
     return EINA_FALSE;

   evas_object_geometry_get(id->view, &r1.x, &r1.y, &r1.w, &r1.h);
   if (eina_rectangle_is_empty(&r1))
     return EINA_FALSE;

   evas_object_geometry_get(id->widget, &r2.x, &r2.y, &r2.w, &r2.h);
   if (!eina_rectangles_intersect(&r1, &r2))
     return EINA_FALSE;

   return EINA_TRUE;
}

const char*
_elm_widget_accessible_plain_name_get(const Evas_Object *obj, const char* name)
{
   char *accessible_plain_name;

   API_ENTRY return NULL;

   accessible_plain_name = _elm_util_mkup_to_text(name);
   eina_stringshare_del(sd->accessible_name);
   sd->accessible_name =  eina_stringshare_add(accessible_plain_name);
   free(accessible_plain_name);
   return sd->accessible_name;
}

const char*
_elm_widget_item_accessible_plain_name_get(const Elm_Object_Item *item, const char* name)
{
   char *accessible_plain_name;

   Elm_Widget_Item_Data *id = efl_data_scope_get(item, ELM_WIDGET_ITEM_CLASS);
   if (!id) return NULL;

   accessible_plain_name = _elm_util_mkup_to_text(name);
   eina_stringshare_del(id->accessible_name);
   id->accessible_name =  eina_stringshare_add(accessible_plain_name);
   free(accessible_plain_name);
   return id->accessible_name;
}

EOLIAN static Efl_Access_State_Set
_elm_widget_item_efl_access_object_state_set_get(const Eo *eo_item,
                                                 Elm_Widget_Item_Data *item EINA_UNUSED)
{
   Efl_Access_State_Set states = 0;

   STATE_TYPE_SET(states, EFL_ACCESS_STATE_TYPE_FOCUSABLE);

   if (elm_object_item_focus_get(eo_item))
     STATE_TYPE_SET(states, EFL_ACCESS_STATE_TYPE_FOCUSED);
   if (!elm_object_item_disabled_get(eo_item))
     {
        STATE_TYPE_SET(states, EFL_ACCESS_STATE_TYPE_ENABLED);
        STATE_TYPE_SET(states, EFL_ACCESS_STATE_TYPE_SENSITIVE);
        STATE_TYPE_SET(states, EFL_ACCESS_STATE_TYPE_VISIBLE);
     }
   if (_elm_widget_item_onscreen_is(eo_item))
     STATE_TYPE_SET(states, EFL_ACCESS_STATE_TYPE_SHOWING);

   return states;
}

EAPI void
elm_object_item_data_set(Elm_Object_Item *it, void *data)
{
   WIDGET_ITEM_DATA_SET(it, data);
}

EAPI void *
elm_object_item_data_get(const Elm_Object_Item *it)
{
   return (void *) WIDGET_ITEM_DATA_GET(it);
}

EOLIAN static void
_elm_widget_item_disabled_set(Eo *eo_item EINA_UNUSED,
                              Elm_Widget_Item_Data *item,
                              Eina_Bool disabled)
{
   ELM_WIDGET_ITEM_CHECK_OR_RETURN(item);
   ELM_WIDGET_ITEM_RETURN_IF_ONDEL(item);

   if (item->disabled == disabled) return;
   item->disabled = !!disabled;
   elm_wdg_item_disable(item->eo_obj);
}

EOLIAN static Eina_Bool
_elm_widget_item_disabled_get(const Eo *eo_item EINA_UNUSED, Elm_Widget_Item_Data *item)
{
   ELM_WIDGET_ITEM_CHECK_OR_RETURN(item, EINA_FALSE);
   return item->disabled;
}

EOLIAN static void
_elm_widget_item_style_set(Eo *eo_item EINA_UNUSED, Elm_Widget_Item_Data *item, const char *style)
{
   eina_stringshare_replace(&item->style, style);
}

EOLIAN static const char *
_elm_widget_item_style_get(const Eo *eo_item EINA_UNUSED, Elm_Widget_Item_Data *item)
{
   return item->style;
}

EOLIAN static void
_elm_widget_item_disable(Eo *eo_item EINA_UNUSED, Elm_Widget_Item_Data *item EINA_UNUSED)
{
}

EOLIAN static void
_elm_widget_item_item_focus_set(Eo *eo_item EINA_UNUSED, Elm_Widget_Item_Data *item, Eina_Bool focused EINA_UNUSED)
{
   ERR_NOT_SUPPORTED(item, "elm_object_item_focus_set");
}

EOLIAN static Eina_Bool
_elm_widget_item_item_focus_get(const Eo *eo_item EINA_UNUSED, Elm_Widget_Item_Data *item)
{
   ERR_NOT_SUPPORTED(item, "elm_object_item_focus_get");
   return EINA_FALSE;
}

EOLIAN static void
_elm_widget_item_domain_translatable_part_text_set(Eo *eo_item EINA_UNUSED,
                                                   Elm_Widget_Item_Data *item,
                                                   const char *part,
                                                   const char *domain,
                                                   const char *label)
{
   ELM_WIDGET_ITEM_CHECK_OR_RETURN(item);
   ELM_WIDGET_ITEM_RETURN_IF_ONDEL(item);
   Elm_Translate_String_Data *ts;

   if (!label)
     {
        _part_text_translatable_set(&item->translate_strings, part, EINA_FALSE,
                                    EINA_FALSE);
     }
   else
     {
        ts = _part_text_translatable_set(&item->translate_strings, part,
                                         EINA_TRUE, EINA_FALSE);
        if (!ts) return;
        if (!ts->string) ts->string = eina_stringshare_add(label);
        else eina_stringshare_replace(&ts->string, label);
        if (!ts->domain) ts->domain = eina_stringshare_add(domain);
        else eina_stringshare_replace(&ts->domain, domain);
#ifdef HAVE_GETTEXT
        if (label[0]) label = dgettext(domain, label);
#endif
     }
   item->on_translate = EINA_TRUE;
   elm_wdg_item_part_text_set(item->eo_obj, part, label);
   item->on_translate = EINA_FALSE;
}

EOLIAN static const char *
_elm_widget_item_translatable_part_text_get(const Eo *eo_item EINA_UNUSED,
                                            Elm_Widget_Item_Data *item,
                                            const char *part)
{
   ELM_WIDGET_ITEM_CHECK_OR_RETURN(item, NULL);
   ELM_WIDGET_ITEM_RETURN_IF_ONDEL(item, NULL);

   Elm_Translate_String_Data *ts;
   ts = _translate_string_data_get(item->translate_strings, part);
   if (ts) return ts->string;
   return NULL;
}

EOLIAN static void
_elm_widget_item_domain_part_text_translatable_set(Eo *eo_item EINA_UNUSED,
                                                   Elm_Widget_Item_Data *item,
                                                   const char *part,
                                                   const char *domain,
                                                   Eina_Bool translatable)
{
   ELM_WIDGET_ITEM_CHECK_OR_RETURN(item);
   ELM_WIDGET_ITEM_RETURN_IF_ONDEL(item);
   Elm_Translate_String_Data *ts;
   const char *text;

   ts = _part_text_translatable_set(&item->translate_strings, part,
                                    translatable, EINA_TRUE);
   if (!ts) return;
   if (!ts->domain) ts->domain = eina_stringshare_add(domain);
   else eina_stringshare_replace(&ts->domain, domain);

   text = elm_wdg_item_part_text_get(item->eo_obj, part);

   if (!text || !text[0]) return;

   if (!ts->string) ts->string = eina_stringshare_add(text);

//Try to translate text since we don't know the text is already translated.
#ifdef HAVE_GETTEXT
   text = dgettext(domain, text);
#endif
   item->on_translate = EINA_TRUE;
   elm_wdg_item_part_text_set(item->eo_obj, part, text);
   item->on_translate = EINA_FALSE;
}

EOLIAN static void
_elm_widget_item_track_cancel(Eo *eo_item EINA_UNUSED, Elm_Widget_Item_Data *item)
{
   ELM_WIDGET_ITEM_CHECK_OR_RETURN(item);
   ELM_WIDGET_ITEM_RETURN_IF_ONDEL(item);

   if (!item->track_obj) return;

   while (evas_object_ref_get(item->track_obj) > 0)
     evas_object_unref(item->track_obj);

   evas_object_del(item->track_obj);
}

EOLIAN static Evas_Object *
_elm_widget_item_track(Eo *eo_item EINA_UNUSED, Elm_Widget_Item_Data *item)
{
   ELM_WIDGET_ITEM_CHECK_OR_RETURN(item, NULL);
   ELM_WIDGET_ITEM_RETURN_IF_ONDEL(item, NULL);

   if (item->track_obj)
     {
        evas_object_ref(item->track_obj);
        return item->track_obj;
     }

   if (!item->view)
     {
        WRN("view obj of the item(%p) is invalid. Please make sure the view obj is created!", item);
        return NULL;
     }

   Evas_Object *track =
      evas_object_rectangle_add(evas_object_evas_get(item->widget));
   evas_object_color_set(track, 0, 0, 0, 0);
   evas_object_pass_events_set(track, EINA_TRUE);
   _track_obj_update(track, item->view);
   evas_object_event_callback_add(track, EVAS_CALLBACK_DEL, _track_obj_del,
                                  item);

   efl_event_callback_array_add(item->view, tracker_callbacks(), item);

   evas_object_ref(track);

   item->track_obj = track;

   return track;
}

EOLIAN static void
_elm_widget_item_untrack(Eo *eo_item EINA_UNUSED, Elm_Widget_Item_Data *item)
{
   ELM_WIDGET_ITEM_CHECK_OR_RETURN(item);
   ELM_WIDGET_ITEM_RETURN_IF_ONDEL(item);

   if (!item->track_obj) return;
   evas_object_unref(item->track_obj);

   if (evas_object_ref_get(item->track_obj) == 0)
     evas_object_del(item->track_obj);
}

EOLIAN static int
_elm_widget_item_track_get(const Eo *eo_item EINA_UNUSED, Elm_Widget_Item_Data *item)
{
   ELM_WIDGET_ITEM_CHECK_OR_RETURN(item, 0);
   ELM_WIDGET_ITEM_RETURN_IF_ONDEL(item, 0);

   if (!item->track_obj) return 0;
   return evas_object_ref_get(item->track_obj);
}

typedef struct _Elm_Widget_Item_Tooltip Elm_Widget_Item_Tooltip;

struct _Elm_Widget_Item_Tooltip
{
   Elm_Widget_Item_Data       *item;
   Elm_Tooltip_Item_Content_Cb func;
   Evas_Smart_Cb               del_cb;
   const void                 *data;
};

static Evas_Object *
_elm_widget_item_tooltip_label_create(void *data,
                                      Evas_Object *obj EINA_UNUSED,
                                      Evas_Object *tooltip,
                                      void *item EINA_UNUSED)
{
   Evas_Object *label = elm_label_add(tooltip);
   if (!label)
     return NULL;
   elm_object_style_set(label, "tooltip");
   elm_object_text_set(label, data);
   return label;
}

static Evas_Object *
_elm_widget_item_tooltip_trans_label_create(void *data,
                                            Evas_Object *obj EINA_UNUSED,
                                            Evas_Object *tooltip,
                                            void *item EINA_UNUSED)
{
   Evas_Object *label = elm_label_add(tooltip);
   if (!label)
     return NULL;
   elm_object_style_set(label, "tooltip");
   elm_object_translatable_text_set(label, data);
   return label;
}

static void
_elm_widget_item_tooltip_label_del_cb(void *data,
                                      Evas_Object *obj EINA_UNUSED,
                                      void *event_info EINA_UNUSED)
{
   eina_stringshare_del(data);
}

/**
 * @internal
 *
 * Set the text to be shown in the widget item.
 *
 * @param item Target item
 * @param text The text to set in the content
 *
 * Setup the text as tooltip to object. The item can have only one tooltip,
 * so any previous tooltip data is removed.
 *
 * @ingroup Widget
 */
EOLIAN static void
_elm_widget_item_tooltip_text_set(Eo *eo_item EINA_UNUSED,
                                  Elm_Widget_Item_Data *item EINA_UNUSED,
                                  const char *text)
{
   ELM_WIDGET_ITEM_CHECK_OR_RETURN(item);
   ELM_WIDGET_ITEM_RETURN_IF_ONDEL(item);
   EINA_SAFETY_ON_NULL_RETURN(text);

   text = eina_stringshare_add(text);
   elm_wdg_item_tooltip_content_cb_set(item->eo_obj, _elm_widget_item_tooltip_label_create, text, _elm_widget_item_tooltip_label_del_cb);
}

EOLIAN static void
_elm_widget_item_tooltip_translatable_text_set(Eo *eo_item EINA_UNUSED,
                                               Elm_Widget_Item_Data *item EINA_UNUSED,
                                               const char *text)
{
   ELM_WIDGET_ITEM_CHECK_OR_RETURN(item);
   ELM_WIDGET_ITEM_RETURN_IF_ONDEL(item);
   EINA_SAFETY_ON_NULL_RETURN(text);

   text = eina_stringshare_add(text);
   elm_wdg_item_tooltip_content_cb_set(item->eo_obj, _elm_widget_item_tooltip_trans_label_create, text, _elm_widget_item_tooltip_label_del_cb);
}

static Evas_Object *
_elm_widget_item_tooltip_create(void *data,
                                Evas_Object *obj,
                                Evas_Object *tooltip)
{
   Elm_Widget_Item_Tooltip *wit = data;
   return wit->func((void *)wit->data, obj, tooltip, wit->item->eo_obj);
}

static void
_elm_widget_item_tooltip_del_cb(void *data,
                                Evas_Object *obj,
                                void *event_info EINA_UNUSED)
{
   Elm_Widget_Item_Tooltip *wit = data;
   if (wit->del_cb) wit->del_cb((void *)wit->data, obj, wit->item->eo_obj);
   free(wit);
}

/**
 * @internal
 *
 * Set the content to be shown in the tooltip item
 *
 * Setup the tooltip to item. The item can have only one tooltip,
 * so any previous tooltip data is removed. @p func(with @p data) will
 * be called every time that need show the tooltip and it should
 * return a valid Evas_Object. This object is then managed fully by
 * tooltip system and is deleted when the tooltip is gone.
 *
 * @param item the widget item being attached a tooltip.
 * @param func the function used to create the tooltip contents.
 * @param data what to provide to @a func as callback data/context.
 * @param del_cb called when data is not needed anymore, either when
 *        another callback replaces @func, the tooltip is unset with
 *        elm_widget_item_tooltip_unset() or the owner @a item
 *        dies. This callback receives as the first parameter the
 *        given @a data, and @c event_info is the item.
 *
 * @ingroup Widget
 */
EOLIAN static void
_elm_widget_item_tooltip_content_cb_set(Eo *eo_item EINA_UNUSED,
                                        Elm_Widget_Item_Data *item,
                                        Elm_Tooltip_Item_Content_Cb func,
                                        const void *data,
                                        Evas_Smart_Cb del_cb)
{
   Elm_Widget_Item_Tooltip *wit;

   ELM_WIDGET_ITEM_CHECK_OR_GOTO(item, error_noitem);
   //ELM_WIDGET_ITEM_RETURN_IF_GOTO(item, error_noitem);

   if (!func)
     {
        elm_wdg_item_tooltip_unset(item->eo_obj);
        return;
     }

   wit = ELM_NEW(Elm_Widget_Item_Tooltip);
   if (!wit) goto error;
   wit->item = item;
   wit->func = func;
   wit->data = data;
   wit->del_cb = del_cb;

   elm_object_sub_tooltip_content_cb_set
     (item->view, item->widget, _elm_widget_item_tooltip_create, wit,
     _elm_widget_item_tooltip_del_cb);

   return;

error_noitem:
   if (del_cb) del_cb((void *)data, NULL, item);
   return;
error:
   if (del_cb) del_cb((void *)data, item->widget, item);
}

/**
 * @internal
 *
 * Unset tooltip from item
 *
 * @param item widget item to remove previously set tooltip.
 *
 * Remove tooltip from item. The callback provided as del_cb to
 * elm_widget_item_tooltip_content_cb_set() will be called to notify
 * it is not used anymore.
 *
 * @see elm_widget_item_tooltip_content_cb_set()
 *
 * @ingroup Widget
 */
EOLIAN static void
_elm_widget_item_tooltip_unset(Eo *eo_item EINA_UNUSED, Elm_Widget_Item_Data *item)
{
   ELM_WIDGET_ITEM_CHECK_OR_RETURN(item);
   ELM_WIDGET_ITEM_RETURN_IF_ONDEL(item);

   elm_object_tooltip_unset(item->view);
}

/**
 * @internal
 *
 * Sets a different style for this item tooltip.
 *
 * @note before you set a style you should define a tooltip with
 *       elm_widget_item_tooltip_content_cb_set() or
 *       elm_widget_item_tooltip_text_set()
 *
 * @param item widget item with tooltip already set.
 * @param style the theme style to use (default, transparent, ...)
 *
 * @ingroup Widget
 */
EOLIAN static void
_elm_widget_item_tooltip_style_set(Eo *eo_item EINA_UNUSED,
                                   Elm_Widget_Item_Data *item,
                                   const char *style)
{
   ELM_WIDGET_ITEM_CHECK_OR_RETURN(item);
   ELM_WIDGET_ITEM_RETURN_IF_ONDEL(item);

   elm_object_tooltip_style_set(item->view, style);
}

EOLIAN static Eina_Bool
_elm_widget_item_tooltip_window_mode_set(Eo *eo_item EINA_UNUSED,
                                         Elm_Widget_Item_Data *item,
                                         Eina_Bool disable)
{
   ELM_WIDGET_ITEM_CHECK_OR_RETURN(item, EINA_FALSE);
   ELM_WIDGET_ITEM_RETURN_IF_ONDEL(item, EINA_FALSE);

   return elm_object_tooltip_window_mode_set(item->view, disable);
}

EOLIAN static Eina_Bool
_elm_widget_item_tooltip_window_mode_get(const Eo *eo_item EINA_UNUSED, Elm_Widget_Item_Data *item)
{
   ELM_WIDGET_ITEM_CHECK_OR_RETURN(item, EINA_FALSE);
   ELM_WIDGET_ITEM_RETURN_IF_ONDEL(item, EINA_FALSE);

   return elm_object_tooltip_window_mode_get(item->view);
}

/**
 * @internal
 *
 * Get the style for this item tooltip.
 *
 * @param item widget item with tooltip already set.
 * @return style the theme style in use, defaults to "default". If the
 *         object does not have a tooltip set, then NULL is returned.
 *
 * @ingroup Widget
 */
EOLIAN static const char *
_elm_widget_item_tooltip_style_get(const Eo *eo_item EINA_UNUSED, Elm_Widget_Item_Data *item)
{
   ELM_WIDGET_ITEM_CHECK_OR_RETURN(item, NULL);

   return elm_object_tooltip_style_get(item->view);
}

EOLIAN static void
_elm_widget_item_cursor_set(Eo *eo_item EINA_UNUSED,
                            Elm_Widget_Item_Data *item,
                            const char *cursor)
{
   ELM_WIDGET_ITEM_CHECK_OR_RETURN(item);
   ELM_WIDGET_ITEM_RETURN_IF_ONDEL(item);

   elm_object_sub_cursor_set(item->view, item->widget, cursor);
}

EOLIAN static const char *
_elm_widget_item_cursor_get(const Eo *eo_item EINA_UNUSED, Elm_Widget_Item_Data *item)
{
   ELM_WIDGET_ITEM_CHECK_OR_RETURN(item, NULL);
   return elm_object_sub_cursor_get(item->view);
}

EOLIAN static void
_elm_widget_item_cursor_unset(Eo *eo_item EINA_UNUSED, Elm_Widget_Item_Data *item)
{
   ELM_WIDGET_ITEM_CHECK_OR_RETURN(item);
   ELM_WIDGET_ITEM_RETURN_IF_ONDEL(item);

   elm_object_cursor_unset(item->view);
}

/**
 * @internal
 *
 * Sets a different style for this item cursor.
 *
 * @note before you set a style you should define a cursor with
 *       elm_widget_item_cursor_set()
 *
 * @param item widget item with cursor already set.
 * @param style the theme style to use (default, transparent, ...)
 *
 * @ingroup Widget
 */
EOLIAN static void
_elm_widget_item_cursor_style_set(Eo *eo_item EINA_UNUSED,
                                  Elm_Widget_Item_Data *item,
                                  const char *style)
{
   ELM_WIDGET_ITEM_CHECK_OR_RETURN(item);
   ELM_WIDGET_ITEM_RETURN_IF_ONDEL(item);

   elm_object_sub_cursor_style_set(item->view, style);
}

/**
 * @internal
 *
 * Get the style for this item cursor.
 *
 * @param item widget item with cursor already set.
 * @return style the theme style in use, defaults to "default". If the
 *         object does not have a cursor set, then NULL is returned.
 *
 * @ingroup Widget
 */
EOLIAN static const char *
_elm_widget_item_cursor_style_get(const Eo *eo_item EINA_UNUSED,
                                  Elm_Widget_Item_Data *item)
{
   ELM_WIDGET_ITEM_CHECK_OR_RETURN(item, NULL);
   return elm_object_sub_cursor_style_get(item->view);
}

/**
 * @internal
 *
 * Set if the cursor set should be searched on the theme or should use
 * the provided by the engine, only.
 *
 * @note before you set if should look on theme you should define a cursor
 * with elm_object_cursor_set(). By default it will only look for cursors
 * provided by the engine.
 *
 * @param item widget item with cursor already set.
 * @param engine_only boolean to define it cursors should be looked only
 * between the provided by the engine or searched on widget's theme as well.
 *
 * @ingroup Widget
 */
EOLIAN static void
_elm_widget_item_cursor_engine_only_set(Eo *eo_item EINA_UNUSED,
                                        Elm_Widget_Item_Data *item,
                                        Eina_Bool engine_only)
{
   ELM_WIDGET_ITEM_CHECK_OR_RETURN(item);
   ELM_WIDGET_ITEM_RETURN_IF_ONDEL(item);

   elm_object_sub_cursor_theme_search_enabled_set(item->view, !engine_only);
}

/**
 * @internal
 *
 * Get the cursor engine only usage for this item cursor.
 *
 * @param item widget item with cursor already set.
 * @return engine_only boolean to define it cursors should be looked only
 * between the provided by the engine or searched on widget's theme as well. If
 *         the object does not have a cursor set, then EINA_FALSE is returned.
 *
 * @ingroup Widget
 */
EOLIAN static Eina_Bool
_elm_widget_item_cursor_engine_only_get(const Eo *eo_item EINA_UNUSED, Elm_Widget_Item_Data *item)
{
   ELM_WIDGET_ITEM_CHECK_OR_RETURN(item, EINA_FALSE);
   return !elm_object_sub_cursor_theme_search_enabled_get(item->view);
}

EOLIAN static void
_elm_widget_item_part_content_set(Eo *eo_item EINA_UNUSED,
                                  Elm_Widget_Item_Data *item,
                                  const char *part EINA_UNUSED,
                                  Evas_Object *content EINA_UNUSED)
{
   ERR_NOT_SUPPORTED(item, "elm_object_part_content_set()");
}

EOLIAN static Evas_Object *
_elm_widget_item_part_content_get(const Eo *eo_item EINA_UNUSED,
                                  Elm_Widget_Item_Data *item,
                                  const char *part EINA_UNUSED)
{
   ERR_NOT_SUPPORTED(item, "elm_object_part_content_get()");
   return NULL;
}

EOLIAN static Evas_Object *
_elm_widget_item_part_content_unset(Eo *eo_item EINA_UNUSED,
                                    Elm_Widget_Item_Data *item,
                                    const char *part EINA_UNUSED)
{
   ERR_NOT_SUPPORTED(item, "elm_object_part_content_unset()");
   return NULL;
}

EOLIAN static void
_elm_widget_item_part_text_set(Eo *eo_item EINA_UNUSED,
                               Elm_Widget_Item_Data *item,
                               const char *part EINA_UNUSED,
                               const char *label EINA_UNUSED)
{
   ERR_NOT_SUPPORTED(item, "elm_object_part_text_set()");
}

EOLIAN static const char *
_elm_widget_item_part_text_get(const Eo *eo_item EINA_UNUSED,
                               Elm_Widget_Item_Data *item,
                               const char *part EINA_UNUSED)
{
   ERR_NOT_SUPPORTED(item, "elm_object_part_text_get()");
   return NULL;
}

static void
_elm_widget_item_part_text_custom_free(void *data)
{
   Elm_Label_Data *label;
   label = data;
   eina_stringshare_del(label->part);
   eina_stringshare_del(label->text);
   free(label);
}

EOLIAN static void
_elm_widget_item_part_text_custom_set(Eo *eo_item EINA_UNUSED,
                                      Elm_Widget_Item_Data *item,
                                      const char *part,
                                      const char *text)
{
   Elm_Label_Data *label;
   ELM_WIDGET_ITEM_CHECK_OR_RETURN(item);
   ELM_WIDGET_ITEM_RETURN_IF_ONDEL(item);

   if (!item->labels)
     item->labels =
        eina_hash_stringshared_new(_elm_widget_item_part_text_custom_free);
   label = eina_hash_find(item->labels, part);
   if (!label)
     {
        label = malloc(sizeof(Elm_Label_Data));
        if (!label)
          {
             ERR("Failed to allocate memory");
             return;
          }
        label->part = eina_stringshare_add(part);
        label->text = eina_stringshare_add(text);
        eina_hash_add(item->labels, part, label);
     }
   else
     eina_stringshare_replace(&label->text, text);
}

EOLIAN static const char *
_elm_widget_item_part_text_custom_get(const Eo *eo_item EINA_UNUSED,
                                      Elm_Widget_Item_Data *item,
                                      const char *part)
{
   Elm_Label_Data *label;
   ELM_WIDGET_ITEM_CHECK_OR_RETURN(item, NULL);
   label = eina_hash_find(item->labels, part);
   return label ? label->text : NULL;
}

static Eina_Bool
_elm_widget_item_part_text_custom_foreach(const Eina_Hash *labels EINA_UNUSED,
                                          const void *key EINA_UNUSED,
                                          void *data,
                                          void *func_data)
{
   Elm_Label_Data *label;
   Elm_Widget_Item_Data *item;
   label = data;
   item = func_data;

   elm_wdg_item_part_text_set(item->eo_obj, label->part, label->text);

   return EINA_TRUE;
}

EOLIAN static void
_elm_widget_item_part_text_custom_update(Eo *eo_item EINA_UNUSED, Elm_Widget_Item_Data *item)
{
   ELM_WIDGET_ITEM_CHECK_OR_RETURN(item);
   ELM_WIDGET_ITEM_RETURN_IF_ONDEL(item);
   if (item->labels)
     eina_hash_foreach(item->labels,
                       _elm_widget_item_part_text_custom_foreach, item);
}

EOLIAN static void
_elm_widget_item_signal_emit(Eo *eo_item EINA_UNUSED,
                             Elm_Widget_Item_Data *item EINA_UNUSED,
                             const char *emission EINA_UNUSED,
                             const char *source EINA_UNUSED)
{

}

EOLIAN static void
_elm_widget_item_signal_callback_add(Eo *eo_item,
                                     Elm_Widget_Item_Data *item,
                                     const char *emission,
                                     const char *source,
                                     Elm_Object_Item_Signal_Cb func,
                                     void *data)
{
   ELM_WIDGET_ITEM_CHECK_OR_RETURN(item);
   ELM_WIDGET_ITEM_RETURN_IF_ONDEL(item);
   EINA_SAFETY_ON_NULL_RETURN(func);

   Elm_Widget_Item_Signal_Data *wisd;

   wisd = malloc(sizeof(Elm_Widget_Item_Signal_Data));
   if (!wisd) return;

   wisd->item = eo_item;
   wisd->func = (Elm_Widget_Item_Signal_Cb)func;
   wisd->data = data;
   wisd->emission = eina_stringshare_add(emission);
   wisd->source = eina_stringshare_add(source);

   if (_elm_widget_is(item->view))
     elm_object_signal_callback_add(item->view, emission, source, _elm_widget_item_signal_cb, wisd);
   else if (efl_isa(item->view, EFL_CANVAS_LAYOUT_CLASS))
     edje_object_signal_callback_add(item->view, emission, source, _elm_widget_item_signal_cb, wisd);
   else
     {
        WRN("The %s widget item doesn't support signal callback add!",
            efl_class_name_get(efl_class_get(item->widget)));
        free(wisd);
        return;
     }

   item->signals = eina_list_append(item->signals, wisd);
}

EOLIAN static void *
_elm_widget_item_signal_callback_del(Eo *eo_item EINA_UNUSED,
                                     Elm_Widget_Item_Data *item,
                                     const char *emission,
                                     const char *source,
                                     Elm_Object_Item_Signal_Cb func)
{
   Elm_Widget_Item_Signal_Data *wisd;
   Eina_List *l;

   ELM_WIDGET_ITEM_CHECK_OR_RETURN(item, NULL);
   ELM_WIDGET_ITEM_RETURN_IF_ONDEL(item, NULL);
   EINA_SAFETY_ON_NULL_RETURN_VAL(func, NULL);

   EINA_LIST_FOREACH(item->signals, l, wisd)
     {
        if ((wisd->func == (Elm_Widget_Item_Signal_Cb)func) &&
            !strcmp(wisd->emission, emission) &&
            !strcmp(wisd->source, source))
          return _elm_widget_item_signal_callback_list_get(item, l);
     }

   return NULL;
}

EOLIAN static void
_elm_widget_item_access_info_set(Eo *eo_item EINA_UNUSED,
                                 Elm_Widget_Item_Data *item,
                                 const char *txt)
{
   ELM_WIDGET_ITEM_CHECK_OR_RETURN(item);
   ELM_WIDGET_ITEM_RETURN_IF_ONDEL(item);

   eina_stringshare_del(item->access_info);
   if (!txt) item->access_info = NULL;
   else item->access_info = eina_stringshare_add(txt);
}

EOLIAN static void
_elm_widget_item_translate(Eo *eo_item EINA_UNUSED, Elm_Widget_Item_Data *item)
{
   ELM_WIDGET_ITEM_CHECK_OR_RETURN(item);
   ELM_WIDGET_ITEM_RETURN_IF_ONDEL(item);

#ifdef HAVE_GETTEXT
   Elm_Translate_String_Data *ts;
   EINA_INLIST_FOREACH(item->translate_strings, ts)
     {
        if (!ts->string) continue;
        const char *s = dgettext(ts->domain, ts->string);
        item->on_translate = EINA_TRUE;
        elm_wdg_item_part_text_set(item->eo_obj, ts->id, s);
        item->on_translate = EINA_FALSE;
     }
#endif
}

EOLIAN static void
_elm_widget_item_access_order_set(Eo *eo_item EINA_UNUSED,
                                  Elm_Widget_Item_Data *item,
                                  Eina_List *objs)
{
   _elm_access_widget_item_access_order_set(item, objs);
}

EOLIAN static const Eina_List *
_elm_widget_item_access_order_get(Eo *eo_item EINA_UNUSED, Elm_Widget_Item_Data *item)
{
   return _elm_access_widget_item_access_order_get(item);
}

EOLIAN static void
_elm_widget_item_access_order_unset(Eo *eo_item EINA_UNUSED, Elm_Widget_Item_Data *item)
{
   _elm_access_widget_item_access_order_unset(item);
}

EOLIAN static Evas_Object*
_elm_widget_item_access_register(Eo *eo_item EINA_UNUSED, Elm_Widget_Item_Data *item)
{
   _elm_access_widget_item_register(item);
   return item->access_obj;
}

EOLIAN static void
_elm_widget_item_access_unregister(Eo *eo_item EINA_UNUSED, Elm_Widget_Item_Data *item)
{
   _elm_access_widget_item_unregister(item);
}

EOLIAN static Evas_Object*
_elm_widget_item_access_object_get(const Eo *eo_item EINA_UNUSED, Elm_Widget_Item_Data *item)
{
   return item->access_obj;
}

EOLIAN static Evas_Object *
_elm_widget_item_focus_next_object_get(const Eo *eo_item EINA_UNUSED, Elm_Widget_Item_Data *item, Elm_Focus_Direction dir)
{
   Evas_Object *ret = NULL;

   if (dir == ELM_FOCUS_PREVIOUS)
     ret = item->focus_previous;
   else if (dir == ELM_FOCUS_NEXT)
     ret = item->focus_next;
   else if (dir == ELM_FOCUS_UP)
     ret = item->focus_up;
   else if (dir == ELM_FOCUS_DOWN)
     ret = item->focus_down;
   else if (dir == ELM_FOCUS_RIGHT)
     ret = item->focus_right;
   else if (dir == ELM_FOCUS_LEFT)
     ret = item->focus_left;

   return ret;
}

EOLIAN static void
_elm_widget_item_focus_next_object_set(Eo *eo_item EINA_UNUSED, Elm_Widget_Item_Data *item, Evas_Object *next, Elm_Focus_Direction dir)
{
   if (dir == ELM_FOCUS_PREVIOUS)
     item->focus_previous = next;
   else if (dir == ELM_FOCUS_NEXT)
     item->focus_next = next;
   else if (dir == ELM_FOCUS_UP)
     item->focus_up = next;
   else if (dir == ELM_FOCUS_DOWN)
     item->focus_down = next;
   else if (dir == ELM_FOCUS_RIGHT)
     item->focus_right = next;
   else if (dir == ELM_FOCUS_LEFT)
     item->focus_left = next;
}

EOLIAN static Elm_Object_Item*
_elm_widget_item_focus_next_item_get(const Eo *eo_item EINA_UNUSED, Elm_Widget_Item_Data *item, Elm_Focus_Direction dir)
{
   Elm_Object_Item *ret = NULL;

   if (dir == ELM_FOCUS_PREVIOUS)
     ret = item->item_focus_previous;
   else if (dir == ELM_FOCUS_NEXT)
     ret = item->item_focus_next;
   else if (dir == ELM_FOCUS_UP)
     ret = item->item_focus_up;
   else if (dir == ELM_FOCUS_DOWN)
     ret = item->item_focus_down;
   else if (dir == ELM_FOCUS_RIGHT)
     ret = item->item_focus_right;
   else if (dir == ELM_FOCUS_LEFT)
     ret = item->item_focus_left;

   return ret;
}

EOLIAN static void
_elm_widget_item_focus_next_item_set(Eo *eo_item EINA_UNUSED, Elm_Widget_Item_Data *item, Elm_Object_Item *next_item, Elm_Focus_Direction dir)
{
   if (dir == ELM_FOCUS_PREVIOUS)
     item->item_focus_previous = next_item;
   else if (dir == ELM_FOCUS_NEXT)
     item->item_focus_next = next_item;
   else if (dir == ELM_FOCUS_UP)
     item->item_focus_up = next_item;
   else if (dir == ELM_FOCUS_DOWN)
     item->item_focus_down = next_item;
   else if (dir == ELM_FOCUS_RIGHT)
     item->item_focus_right = next_item;
   else if (dir == ELM_FOCUS_LEFT)
     item->item_focus_left = next_item;
}

/* happy debug functions */
#ifdef ELM_DEBUG
static void
_sub_obj_tree_dump(const Evas_Object *obj,
                   int lvl)
{
   int i;

   for (i = 0; i < lvl * 3; i++)
     putchar(' ');

   if (_elm_widget_is(obj))
     {
        Eina_List *l;
        INTERNAL_ENTRY;
        DBG("+ %s(%p)\n",
            elm_widget_type_get(obj),
            obj);
       for (unsigned int i = 0; i < eina_array_count(sd->children); ++i)
         {
            obj = eina_array_data_get(sd->children, i);
          _sub_obj_tree_dump(obj, lvl + 1);
         }
     }
   else
     DBG("+ %s(%p)\n", evas_object_type_get(obj), obj);
}

static void
_sub_obj_tree_dot_dump(const Evas_Object *obj,
                       FILE *output)
{
   if (!_elm_widget_is(obj))
     return;
   INTERNAL_ENTRY;

   Eina_Bool visible = evas_object_visible_get(obj);
   Eina_Bool disabled = elm_widget_disabled_get(obj);
   Eina_Bool focused = efl_ui_focus_object_focus_get(obj);
   Eina_Bool can_focus = elm_widget_can_focus_get(obj);

   if (sd->parent_obj)
     {
        fprintf(output, "\"%p\" -- \"%p\" [ color=black", sd->parent_obj, obj);

        if (focused)
          fprintf(output, ", style=bold");

        if (!visible)
          fprintf(output, ", color=gray28");

        fprintf(output, " ];\n");
     }

   fprintf(output, "\"%p\" [ label = \"{%p|%s|%s|visible: %d|"
                   "disabled: %d|focused: %d/%d|focus order:%d}\"",
           obj, obj, elm_widget_type_get(obj),
           evas_object_name_get(obj), visible, disabled, focused, can_focus,
           sd->focus_order);

   if (focused)
     fprintf(output, ", style=bold");

   if (!visible)
     fprintf(output, ", fontcolor=gray28");

   if ((disabled) || (!visible))
     fprintf(output, ", color=gray");

   fprintf(output, " ];\n");

   Eina_List *l;
   Evas_Object *o;

   for (unsigned int i = 0; i < eina_array_count(sd->children); ++i)
     {
        o = eina_array_data_get(sd->children, i);
        _sub_obj_tree_dot_dump(o, output);
     }
}

#endif

EAPI void
elm_widget_tree_dump(const Evas_Object *top)
{
#ifdef ELM_DEBUG
   if (!_elm_widget_is(top))
     return;
   _sub_obj_tree_dump(top, 0);
#else
   (void)top;
   return;
#endif
}

EAPI void
elm_widget_tree_dot_dump(const Evas_Object *top,
                         FILE *output)
{
#ifdef ELM_DEBUG
   if (!_elm_widget_is(top))
     return;
   fprintf(output, "graph " " { node [shape=record];\n");
   _sub_obj_tree_dot_dump(top, output);
   fprintf(output, "}\n");
#else
   (void)top;
   (void)output;
   return;
#endif
}

EOLIAN static Eo *
_efl_ui_widget_efl_object_constructor(Eo *obj, Elm_Widget_Smart_Data *sd EINA_UNUSED)
{
   Eo *parent = efl_parent_get(obj);
   sd->on_create = EINA_TRUE;

   sd->window = efl_provider_find(efl_parent_get(obj), EFL_UI_WIN_CLASS);
   if (!efl_isa(obj, EFL_UI_WIN_CLASS))
     {
        if (!efl_isa(parent, EFL_UI_WIDGET_CLASS))
          {
             ERR("You passed a wrong parent parameter (%p %s). "
                 "Elementary widget's parent should be an elementary widget.",
                 parent, evas_object_type_get(parent));
          }
        else
          {
             ELM_WIDGET_DATA_GET(parent, parent_sd);
             if (parent_sd)
               sd->shared_win_data = parent_sd->shared_win_data;
          }
     }
   else
     {
        sd->shared_win_data = efl_ui_win_shared_data_get(obj);
     }

   _efl_ui_focus_event_redirector(obj, obj);
   efl_canvas_group_clipped_set(obj, EINA_FALSE);
   obj = efl_constructor(efl_super(obj, MY_CLASS));
   efl_canvas_object_type_set(obj, MY_CLASS_NAME_LEGACY);
   evas_object_smart_callbacks_descriptions_set(obj, _smart_callbacks);

   if (!efl_isa(obj, EFL_UI_WIN_CLASS) && efl_isa(parent, EFL_UI_WIDGET_CLASS))
     efl_ui_widget_sub_object_add(parent, obj);

   sd->on_create = EINA_FALSE;

   efl_access_object_role_set(obj, EFL_ACCESS_ROLE_UNKNOWN);

   if (!elm_widget_is_legacy(obj))
     EINA_SAFETY_ON_NULL_RETURN_VAL(sd->shared_win_data, NULL);

   return obj;
}

EOLIAN static Efl_Object*
_efl_ui_widget_efl_object_finalize(Eo *obj, Elm_Widget_Smart_Data *pd)
{
  Eo *eo;

  eo = efl_finalize(efl_super(obj, MY_CLASS));

  _full_eval(obj, pd);

  return eo;
}


EOLIAN static void
_efl_ui_widget_efl_object_destructor(Eo *obj, Elm_Widget_Smart_Data *sd)
{
   if (sd->manager.provider)
     {
        sd->manager.provider = NULL;
     }
   efl_access_object_attributes_clear(obj);
   efl_access_removed(obj);
   if (sd->logical.parent)
     {
        efl_weak_unref(&sd->logical.parent);
        sd->logical.parent = NULL;
     }
   if (sd->children)
     {
        eina_array_free(sd->children);
        sd->children = NULL;
     }

   sd->on_destroy = EINA_TRUE;
   efl_destructor(efl_super(obj, EFL_UI_WIDGET_CLASS));
   sd->on_destroy = EINA_FALSE;
}

/* internal eo */

EOLIAN static void
_efl_ui_widget_efl_object_debug_name_override(Eo *obj, Elm_Widget_Smart_Data *sd EINA_UNUSED, Eina_Strbuf *sb)
{
   const char *focus = "";

   if (efl_ui_focus_object_focus_get(obj)) focus = ":focused";
   efl_debug_name_override(efl_super(obj, MY_CLASS), sb);
   eina_strbuf_append_printf(sb, "%s", focus);
}

EOLIAN static Eina_Bool
_efl_ui_widget_efl_ui_focus_object_on_focus_update(Eo *obj, Elm_Widget_Smart_Data *sd)
{
   Eina_Bool focused;

   if (!elm_widget_can_focus_get(obj))
     return EINA_FALSE;

   focused = efl_ui_focus_object_focus_get(obj);

   if (!sd->resize_obj)
     evas_object_focus_set(obj, focused);

   if (_elm_config->atspi_mode && !elm_widget_child_can_focus_get(obj))
     efl_access_state_changed_signal_emit(obj, EFL_ACCESS_STATE_TYPE_FOCUSED, focused);

   return EINA_TRUE;
}

EOLIAN static Eina_Bool
_efl_ui_widget_widget_input_event_handler(Eo *obj EINA_UNUSED, Elm_Widget_Smart_Data *_pd EINA_UNUSED, const Efl_Event *eo_event EINA_UNUSED, Evas_Object *source EINA_UNUSED)
{
   return EINA_FALSE;
}

EOLIAN static Eina_Bool
_efl_ui_widget_on_access_activate(Eo *obj EINA_UNUSED, Elm_Widget_Smart_Data *_pd EINA_UNUSED, Efl_Ui_Activate act EINA_UNUSED)
{
   WRN("The %s widget does not implement the \"activate\" functions.",
       efl_class_name_get(efl_class_get(obj)));
   return EINA_TRUE;
}

EOLIAN static void
_efl_ui_widget_class_constructor(Efl_Class *klass)
{
   evas_smart_legacy_type_register(MY_CLASS_NAME_LEGACY, klass);
}

EOLIAN static Eina_Bool
_efl_ui_widget_efl_access_component_focus_grab(Eo *obj, Elm_Widget_Smart_Data *pd EINA_UNUSED)
{
   if (elm_object_focus_allow_get(obj))
     {
       Ecore_Evas *ee = ecore_evas_ecore_evas_get(evas_object_evas_get(obj));
       if (!ee) return EINA_FALSE;
       ecore_evas_activate(ee);
       elm_object_focus_set(obj, EINA_TRUE);
       return EINA_TRUE;
     }
   return EINA_FALSE;
}

EOLIAN static const char*
_efl_ui_widget_efl_access_object_i18n_name_get(const Eo *obj, Elm_Widget_Smart_Data *_pd EINA_UNUSED)
{
   const char *ret, *name;
   name = efl_access_object_i18n_name_get(efl_super(obj, EFL_UI_WIDGET_CLASS));

   if (name) return name;

   ret = elm_object_text_get(obj);
   if (!ret) return NULL;

   return _elm_widget_accessible_plain_name_get(obj, ret);
}

EOLIAN static Eina_List*
_efl_ui_widget_efl_access_object_access_children_get(const Eo *obj EINA_UNUSED, Elm_Widget_Smart_Data *pd)
{
   Eina_List *accs = NULL;
   Evas_Object *widget;
   Efl_Access_Type type;

   for (unsigned int i = 0; i < eina_array_count(pd->children); ++i)
     {
        widget = eina_array_data_get(pd->children, i);

        if (!elm_object_widget_check(widget)) continue;
        if (!efl_isa(widget, EFL_ACCESS_OBJECT_MIXIN)) continue;
        type = efl_access_object_access_type_get(widget);
        if (type == EFL_ACCESS_TYPE_DISABLED) continue;
        if (type == EFL_ACCESS_TYPE_SKIPPED)
          {
             Eina_List *children;
             children = efl_access_object_access_children_get(widget);
             accs = eina_list_merge(accs, children);
             continue;
          }
        accs = eina_list_append(accs, widget);
     }
   return accs;
}

EOLIAN static Efl_Access_State_Set
_efl_ui_widget_efl_access_object_state_set_get(const Eo *obj, Elm_Widget_Smart_Data *pd EINA_UNUSED)
{
   Efl_Access_State_Set states = 0;

   states = efl_access_object_state_set_get(efl_super(obj, EFL_UI_WIDGET_CLASS));

   if (evas_object_visible_get(obj))
     {
        STATE_TYPE_SET(states, EFL_ACCESS_STATE_TYPE_VISIBLE);
        if (_elm_widget_onscreen_is(obj))
          STATE_TYPE_SET(states, EFL_ACCESS_STATE_TYPE_SHOWING);
     }
   if (!elm_widget_child_can_focus_get(obj))
     {
        if (elm_object_focus_allow_get(obj))
          STATE_TYPE_SET(states, EFL_ACCESS_STATE_TYPE_FOCUSABLE);
        if (elm_object_focus_get(obj))
          STATE_TYPE_SET(states, EFL_ACCESS_STATE_TYPE_FOCUSED);
     }
   if (!elm_object_disabled_get(obj))
     {
        STATE_TYPE_SET(states, EFL_ACCESS_STATE_TYPE_ENABLED);
        STATE_TYPE_SET(states, EFL_ACCESS_STATE_TYPE_SENSITIVE);
     }

   return states;
}

EOLIAN static Eina_List*
_efl_ui_widget_efl_access_object_attributes_get(const Eo *obj, Elm_Widget_Smart_Data *pd EINA_UNUSED)
{
   const char *type = NULL;
   const char *style = NULL;
   Eina_List *attr_list = NULL;
   Efl_Access_Attribute *attr = NULL;

   attr_list = efl_access_object_attributes_get(efl_super(obj, EFL_UI_WIDGET_CLASS));

   //Add type and style information in addition.
   type = elm_widget_type_get(obj);
   if (type)
     {
        attr = calloc(1, sizeof(Efl_Access_Attribute));
        if (attr)
          {
             attr->key = eina_stringshare_add("type");
             attr->value = eina_stringshare_add(type);
             attr_list = eina_list_append(attr_list, attr);
           }
     }

   style = elm_widget_style_get(obj);
   if (style)
     {
        attr = calloc(1, sizeof(Efl_Access_Attribute));
        if (attr)
          {
             attr->key = eina_stringshare_add("style");
             attr->value = eina_stringshare_add(style);
             attr_list = eina_list_append(attr_list, attr);
          }
     }

   return attr_list;
}

EOLIAN static Eina_List *
_elm_widget_item_efl_access_object_attributes_get(const Eo *eo_item, Elm_Widget_Item_Data *pd  EINA_UNUSED)
{
   const char *style = NULL;
   Eina_List *attr_list = NULL;
   Efl_Access_Attribute *attr = NULL;

   attr_list = efl_access_object_attributes_get(efl_super(eo_item, ELM_WIDGET_ITEM_CLASS));

   style = elm_object_item_style_get(eo_item);
   if (style)
     {
        attr = calloc(1, sizeof(Efl_Access_Attribute));
        if (attr)
          {
             attr->key = eina_stringshare_add("style");
             attr->value = eina_stringshare_add(style);
             attr_list = eina_list_append(attr_list, attr);
          }
     }
   return attr_list;
}

EOLIAN static Eina_Rect
_elm_widget_item_efl_access_component_extents_get(const Eo *obj EINA_UNUSED, Elm_Widget_Item_Data *sd EINA_UNUSED, Eina_Bool screen_coords)
{
   Eina_Rect r = EINA_RECT(-1, -1, -1, -1);

   if (!sd->view) return r;

   r = efl_gfx_entity_geometry_get(sd->view);
   if (screen_coords)
     {
        r = _efl_access_component_screen_coords_extents_get(obj, r);
     }
   return r;
}

EOLIAN static Eina_Bool
_elm_widget_item_efl_access_component_extents_set(Eo *obj EINA_UNUSED, Elm_Widget_Item_Data *sd EINA_UNUSED, Eina_Bool screen_coords EINA_UNUSED, Eina_Rect r EINA_UNUSED)
{
   return EINA_FALSE;
}

EOLIAN static Eina_Bool
_elm_widget_item_efl_access_component_focus_grab(Eo *obj EINA_UNUSED, Elm_Widget_Item_Data *_pd EINA_UNUSED)
{
   elm_object_item_focus_set(obj, EINA_TRUE);
   return elm_object_item_focus_get(obj);
}

EOLIAN static Efl_Object *
_efl_ui_widget_efl_object_provider_find(const Eo *obj, Elm_Widget_Smart_Data *pd, const Efl_Object *klass)
{
   Efl_Object *lookup = NULL;

   if ((klass == EFL_CONFIG_INTERFACE) || (klass == EFL_CONFIG_GLOBAL_CLASS))
     return _efl_config_obj;

   if (klass == EFL_UI_WIN_CLASS)
     {
        if (pd->window)
          return pd->window;
        //let the parent_obj lookup handle this
     }

   if (klass == EFL_ACCESS_OBJECT_MIXIN)
     {
        Efl_Access_Type type = efl_access_object_access_type_get(obj);
        if (type != EFL_ACCESS_TYPE_SKIPPED)
          return (Eo*)obj;
     }

   if (pd->provider_lookup) return NULL;
   pd->provider_lookup = EINA_TRUE;

   lookup = efl_provider_find(efl_super(obj, MY_CLASS), klass);
   if (!lookup && pd->parent_obj) lookup = efl_provider_find(pd->parent_obj, klass);

   pd->provider_lookup = EINA_FALSE;

   return lookup;
}

EOLIAN static Efl_Ui_Focus_Manager*
_efl_ui_widget_efl_ui_focus_object_focus_parent_get(const Eo *obj EINA_UNUSED, Elm_Widget_Smart_Data *pd EINA_UNUSED)
{
   return pd->focus.parent;
}

EOLIAN static Efl_Ui_Focus_Manager*
_efl_ui_widget_efl_ui_focus_object_focus_manager_get(const Eo *obj EINA_UNUSED, Elm_Widget_Smart_Data *pd EINA_UNUSED)
{
   return pd->focus.manager;
}

EOLIAN static Eina_Rect
_efl_ui_widget_efl_ui_focus_object_focus_geometry_get(const Eo *obj, Elm_Widget_Smart_Data *pd EINA_UNUSED)
{
   return efl_gfx_entity_geometry_get(obj);
}

EOLIAN static void
_efl_ui_widget_efl_ui_focus_object_focus_set(Eo *obj, Elm_Widget_Smart_Data *pd, Eina_Bool focus)
{
   pd->focused = focus;

   efl_ui_focus_object_focus_set(efl_super(obj, MY_CLASS), focus);

   efl_ui_focus_object_on_focus_update(obj);
}

/* Legacy APIs */

EAPI void
elm_widget_on_show_region_hook_set(Eo *obj, void *data, Elm_Widget_On_Show_Region_Cb func, Eina_Free_Cb func_free_cb)
{
   ELM_WIDGET_DATA_GET(obj, sd);

   if (!sd) return;
   if ((sd->on_show_region_data == data) && (sd->on_show_region == func))
     return;

   if (sd->on_show_region_data && sd->on_show_region_data_free)
     sd->on_show_region_data_free(sd->on_show_region_data);

   sd->on_show_region = func;
   sd->on_show_region_data = data;
   sd->on_show_region_data_free = func_free_cb;
}

EAPI void
elm_widget_show_region_set(Eo *obj, Eina_Rect sr, Eina_Bool forceshow)
{
   Evas_Object *parent_obj, *child_obj;
   Evas_Coord px, py, cx, cy, nx = 0, ny = 0;

   ELM_WIDGET_DATA_GET_OR_RETURN(obj, sd);

   evas_smart_objects_calculate(evas_object_evas_get(obj));

   if (!forceshow && eina_rectangle_equal(&sr.rect, &sd->show_region.rect)) return;

   sd->show_region = sr;
   if (sd->on_show_region)
     {
        sd->on_show_region(sd->on_show_region_data, obj, sr);

        if (_elm_scrollable_is(obj))
          {
             if (elm_widget_is_legacy(obj))
               {
                  elm_interface_scrollable_content_pos_get(obj, &nx, &ny);
                  sr.x -= nx;
                  sr.y -= ny;
               }
             else
               {
                  Eina_Position2D pos;
                  pos = efl_ui_scrollable_content_pos_get(obj);
                  sr.x -= pos.x;
                  sr.y -= pos.y;
               }
          }
     }
   child_obj = obj;
   do
     {
        parent_obj = sd->parent_obj;
        if ((!parent_obj)) break;
        sd = efl_data_scope_get(parent_obj, MY_CLASS);
        if (!sd) break;

        evas_object_geometry_get(parent_obj, &px, &py, NULL, NULL);
        evas_object_geometry_get(child_obj, &cx, &cy, NULL, NULL);

        sr.x += (cx - px);
        sr.y += (cy - py);
        sd->show_region = sr;

        if (sd->on_show_region)
          sd->on_show_region(sd->on_show_region_data, parent_obj, sr);
        child_obj = parent_obj;
     }
   while (parent_obj);
}

EAPI Eina_Rect
elm_widget_show_region_get(const Eo *obj)
{
   ELM_WIDGET_DATA_GET_OR_RETURN(obj, sd, EINA_RECT_EMPTY());
   return (Eina_Rect) sd->show_region;
}
/* elm_object_content_xxx APIs are supposed to work on all objects for which
 * elm_object_widget_check() returns true. The below checks avoid printing out
 * undesired ERR messages. */
EAPI void
elm_widget_content_part_set(Evas_Object *obj, const char *part, Evas_Object *content)
{
   ELM_WIDGET_CHECK(obj);
   if (efl_isa(obj, EFL_UI_LAYOUT_BASE_CLASS))
     {
        elm_layout_content_set(obj, part, content);
        return;
     }
   if (!efl_isa(obj, EFL_PART_INTERFACE)) return;
   if (!part)
     {
        part = efl_ui_widget_default_content_part_get(obj);
        if (!part) return;
     }
   efl_content_set(efl_part(obj, part), content);
}

EAPI Evas_Object *
elm_widget_content_part_get(const Evas_Object *obj, const char *part)
{
   ELM_WIDGET_CHECK(obj) NULL;
   if (efl_isa(obj, EFL_UI_LAYOUT_BASE_CLASS))
     return elm_layout_content_get(obj, part);
   if (!efl_isa(obj, EFL_PART_INTERFACE)) return NULL;
   if (!part)
     {
        part = efl_ui_widget_default_content_part_get(obj);
        if (!part) return NULL;
     }
   return efl_content_get(efl_part(obj, part));
}

EAPI Evas_Object *
elm_widget_content_part_unset(Evas_Object *obj, const char *part)
{
   ELM_WIDGET_CHECK(obj) NULL;
   if (efl_isa(obj, EFL_UI_LAYOUT_BASE_CLASS))
     return elm_layout_content_unset(obj, part);
   if (!efl_isa(obj, EFL_PART_INTERFACE)) return NULL;
   if (!part)
     {
        part = efl_ui_widget_default_content_part_get(obj);
        if (!part) return NULL;
     }
   return efl_content_unset(efl_part(obj, part));
}

EAPI void
elm_widget_signal_emit(Eo *obj, const char *emission, const char *source)
{
   ELM_WIDGET_CHECK(obj);

   if (efl_isa(obj, EFL_UI_LAYOUT_BASE_CLASS))
     elm_layout_signal_emit(obj, emission, source);
   else if (evas_object_smart_type_check(obj, "elm_icon"))
     {
        WRN("Deprecated function. This functionality on icon objects"
            " will be dropped on a next release.");
        _elm_icon_signal_emit(obj, emission, source);
     }
}

EAPI void
elm_widget_signal_callback_add(Eo *obj, const char *emission, const char *source, Edje_Signal_Cb func, void *data)
{
   ELM_WIDGET_CHECK(obj);
   EINA_SAFETY_ON_NULL_RETURN(func);
   if (evas_object_smart_type_check(obj, "elm_layout"))
     elm_layout_signal_callback_add(obj, emission, source, func, data);
   else if (evas_object_smart_type_check(obj, "elm_icon"))
     {
        WRN("Deprecated function. This functionality on icon objects"
            " will be dropped on a next release.");

        _elm_icon_signal_callback_add(obj, emission, source, func, data);
     }
}

EAPI void *
elm_widget_signal_callback_del(Eo *obj, const char *emission, const char *source, Edje_Signal_Cb func)
{
   void *data = NULL;

   ELM_WIDGET_CHECK(obj) NULL;
   EINA_SAFETY_ON_NULL_RETURN_VAL(func, NULL);
   if (evas_object_smart_type_check(obj, "elm_layout"))
     data = elm_layout_signal_callback_del(obj, emission, source, func);
   else if (evas_object_smart_type_check(obj, "elm_icon"))
     {
        WRN("Deprecated function. This functionality on icon objects"
            " will be dropped on a next release.");

        data = _elm_icon_signal_callback_del(obj, emission, source, func);
     }

   return data;
}


/* Widget Shadow Begin */

typedef struct _Widget_Shadow
{
   Eo *widget;
   Eo *surface;
   struct {
      double rx, ry, ox, oy, grow;
      int r, g, b, a;
   } props;
   Eina_Stringshare *code, *name;
} Widget_Shadow;

static void _widget_shadow_update(Widget_Shadow *shadow);

static void
_widget_shadow_del_cb(void *data, const Efl_Event *ev EINA_UNUSED)
{
   Widget_Shadow *shadow = data;

   efl_del(shadow->surface);
   free(shadow);
}

static void
_widget_shadow_event_cb(void *data, const Efl_Event *ev EINA_UNUSED)
{
   Widget_Shadow *shadow = data;
   _widget_shadow_update(shadow);
}

EFL_CALLBACKS_ARRAY_DEFINE(widget_shadow_cb,
{ EFL_EVENT_DEL, _widget_shadow_del_cb },
{ EFL_GFX_ENTITY_EVENT_POSITION_CHANGED, _widget_shadow_event_cb },
{ EFL_GFX_ENTITY_EVENT_SIZE_CHANGED, _widget_shadow_event_cb },
{ EFL_GFX_ENTITY_EVENT_STACKING_CHANGED, _widget_shadow_event_cb },
{ EFL_GFX_ENTITY_EVENT_VISIBILITY_CHANGED, _widget_shadow_event_cb });

static Widget_Shadow *
_widget_shadow_part_get(const Eo *part_obj)
{
   Elm_Part_Data *pd = efl_data_scope_get(part_obj, EFL_UI_WIDGET_PART_CLASS);
   Widget_Shadow *shadow;
   Eo *widget = pd->obj;

   shadow = efl_key_data_get(widget, "__elm_shadow");
   if (!shadow)
     {
        shadow = calloc(1, sizeof(*shadow));
        if (!shadow) return NULL;
        shadow->widget = pd->obj;
        efl_key_data_set(widget, "__elm_shadow", shadow);
        efl_event_callback_array_add(widget, widget_shadow_cb(), shadow);
     }
   return shadow;
}

static void
_widget_shadow_update(Widget_Shadow *ws)
{
   int l = 0, r = 0, t = 0, b = 0;
   Eina_Rect srect, wrect;
   char filter[1024];

#define FILTER_FMT \
   "a = buffer { 'alpha' }" \
   "grow { %f, dst = a, alphaonly = true }" \
   "blur { src = a, rx = %f, ry = %f, color = color(%d,%d,%d,%d) }"

   if (!ws->surface)
     {
        ws->surface = efl_add(EFL_CANVAS_PROXY_CLASS, ws->widget);
        efl_gfx_fill_auto_set(ws->surface, 1);
        efl_canvas_proxy_source_clip_set(ws->surface, EINA_FALSE);
        efl_canvas_proxy_source_events_set(ws->surface, EINA_FALSE);
        efl_canvas_proxy_source_set(ws->surface, ws->widget);
     }

   if (!ws->code)
     {
        snprintf(filter, sizeof(filter), FILTER_FMT,
                 ws->props.grow, ws->props.rx, ws->props.ry,
                 ws->props.r, ws->props.g, ws->props.b, ws->props.a);
     }

   efl_gfx_filter_program_set(ws->surface,
                              ws->code ? ws->code : filter,
                              ws->name ? ws->name : "shadow");
   efl_gfx_filter_padding_get(ws->surface, &l, &r, &t, &b);

   wrect = efl_gfx_entity_geometry_get(ws->widget);
   srect.x = wrect.x + (int) (-l + ws->props.ox);
   srect.y = wrect.y + (int) (-t + ws->props.oy);
   srect.w = wrect.w + (int) (l + r);
   srect.h = wrect.h + (int) (t + b);

   if ((!ws->props.a && !ws->code) ||
       !efl_gfx_entity_visible_get(ws->widget))
     {
        efl_gfx_entity_visible_set(ws->surface, EINA_FALSE);
        return;
     }

   efl_canvas_object_clipper_set(ws->surface, efl_canvas_object_clipper_get(ws->widget));
   efl_canvas_group_member_add(efl_canvas_object_render_parent_get(ws->widget), ws->surface);
   efl_gfx_entity_geometry_set(ws->surface, srect);
   efl_gfx_stack_below(ws->surface, ws->widget);
   efl_gfx_entity_visible_set(ws->surface, EINA_TRUE);
}

static void
_elm_widget_shadow_update(Efl_Ui_Widget *obj)
{
   Widget_Shadow *shadow = _widget_shadow_part_get(obj);
   _widget_shadow_update(shadow);
}

EOLIAN static void
_efl_ui_widget_part_shadow_efl_gfx_blur_offset_set(Eo *obj, void *_pd EINA_UNUSED, double ox, double oy)
{
   Widget_Shadow *shadow = _widget_shadow_part_get(obj);
   shadow->props.ox = ox;
   shadow->props.oy = oy;
   _widget_shadow_update(shadow);
}

EOLIAN static void
_efl_ui_widget_part_shadow_efl_gfx_blur_offset_get(const Eo *obj, void *_pd EINA_UNUSED, double *ox, double *oy)
{
   Widget_Shadow *shadow = _widget_shadow_part_get(obj);
   if (ox) *ox = shadow->props.ox;
   if (oy) *oy = shadow->props.oy;
}

EOLIAN static void
_efl_ui_widget_part_shadow_efl_gfx_blur_radius_set(Eo *obj, void *_pd EINA_UNUSED, double rx, double ry)
{
   Widget_Shadow *shadow = _widget_shadow_part_get(obj);
   shadow->props.rx = rx;
   shadow->props.ry = ry;
   _widget_shadow_update(shadow);
}

EOLIAN static void
_efl_ui_widget_part_shadow_efl_gfx_blur_radius_get(const Eo *obj, void *_pd EINA_UNUSED, double *rx, double *ry)
{
   Widget_Shadow *shadow = _widget_shadow_part_get(obj);
   if (rx) *rx = shadow->props.rx;
   if (ry) *ry = shadow->props.ry;
}

EOLIAN static void
_efl_ui_widget_part_shadow_efl_gfx_color_color_set(Eo *obj, void *_pd EINA_UNUSED, int r, int g, int b, int a)
{
   Widget_Shadow *shadow = _widget_shadow_part_get(obj);
   shadow->props.r = r;
   shadow->props.g = g;
   shadow->props.b = b;
   shadow->props.a = a;
   _widget_shadow_update(shadow);
}

EOLIAN static void
_efl_ui_widget_part_shadow_efl_gfx_color_color_get(const Eo *obj, void *_pd EINA_UNUSED, int *r, int *g, int *b, int *a)
{
   Widget_Shadow *shadow = _widget_shadow_part_get(obj);
   if (r) *r = shadow->props.r;
   if (g) *g = shadow->props.g;
   if (b) *b = shadow->props.b;
   if (a) *a = shadow->props.a;
}

EOLIAN static void
_efl_ui_widget_part_shadow_efl_gfx_blur_grow_set(Eo *obj, void *_pd EINA_UNUSED, double radius)
{
   Widget_Shadow *shadow = _widget_shadow_part_get(obj);
   shadow->props.grow = radius;
   _widget_shadow_update(shadow);
}

EOLIAN static double
_efl_ui_widget_part_shadow_efl_gfx_blur_grow_get(const Eo *obj, void *_pd EINA_UNUSED)
{
   Widget_Shadow *shadow = _widget_shadow_part_get(obj);
   return shadow->props.grow;
}

EOLIAN static void
_efl_ui_widget_part_shadow_efl_gfx_filter_filter_program_set(Eo *obj, void *_pd EINA_UNUSED, const char *code, const char *name)
{
   Widget_Shadow *ws = _widget_shadow_part_get(obj);
   eina_stringshare_replace(&ws->code, code);
   eina_stringshare_replace(&ws->name, name);
   _widget_shadow_update(ws);
}

EOLIAN static void
_efl_ui_widget_part_shadow_efl_gfx_filter_filter_program_get(const Eo *obj, void *_pd EINA_UNUSED, const char **code, const char **name)
{
   Widget_Shadow *ws = _widget_shadow_part_get(obj);
   efl_gfx_filter_program_get(ws->surface, code, name);
}

EOLIAN static void
_efl_ui_widget_part_shadow_efl_gfx_filter_filter_source_set(Eo *obj, void *_pd EINA_UNUSED, const char *name, Efl_Gfx_Entity *source)
{
   Widget_Shadow *ws = _widget_shadow_part_get(obj);
   _widget_shadow_update(ws);
   efl_gfx_filter_source_set(ws->surface, name, source);
}

EOLIAN static Efl_Gfx_Entity *
_efl_ui_widget_part_shadow_efl_gfx_filter_filter_source_get(const Eo *obj, void *_pd EINA_UNUSED, const char *name)
{
   Widget_Shadow *ws = _widget_shadow_part_get(obj);
   return efl_gfx_filter_source_get(ws->surface, name);
}

EOLIAN static void
_efl_ui_widget_part_shadow_efl_gfx_filter_filter_data_set(Eo *obj, void *_pd EINA_UNUSED, const char *name, const char *value, Eina_Bool execute)
{
   Widget_Shadow *ws = _widget_shadow_part_get(obj);
   _widget_shadow_update(ws);
   efl_gfx_filter_data_set(ws->surface, name, value, execute);
}

EOLIAN static void
_efl_ui_widget_part_shadow_efl_gfx_filter_filter_data_get(const Eo *obj, void *_pd EINA_UNUSED, const char *name, const char **value, Eina_Bool *execute)
{
   Widget_Shadow *ws = _widget_shadow_part_get(obj);
   efl_gfx_filter_data_get(ws->surface, name, value, execute);
}

EOLIAN static void
_efl_ui_widget_part_shadow_efl_gfx_filter_filter_padding_get(const Eo *obj, void *_pd EINA_UNUSED, int *l, int *r, int *t, int *b)
{
   Widget_Shadow *ws = _widget_shadow_part_get(obj);
   efl_gfx_filter_padding_get(ws->surface, l, r, t, b);
}

EOLIAN static void
_efl_ui_widget_part_shadow_efl_gfx_filter_filter_state_set(Eo *obj, void *_pd EINA_UNUSED, const char *cur_state, double cur_val, const char *next_state, double next_val, double pos)
{
   Widget_Shadow *ws = _widget_shadow_part_get(obj);
   efl_gfx_filter_state_set(ws->surface, cur_state, cur_val, next_state, next_val, pos);
}

EOLIAN static void
_efl_ui_widget_part_shadow_efl_gfx_filter_filter_state_get(const Eo *obj, void *_pd EINA_UNUSED, const char **cur_state, double *cur_val, const char **next_state, double *next_val, double *pos)
{
   Widget_Shadow *ws = _widget_shadow_part_get(obj);
   efl_gfx_filter_state_get(ws->surface, cur_state, cur_val, next_state, next_val, pos);
}

#include "efl_ui_widget_part_shadow.eo.c"

/* Widget Shadow End */


/* Efl.Part implementation */

EOLIAN static Efl_Object *
_efl_ui_widget_efl_part_part_get(const Eo *obj, Elm_Widget_Smart_Data *wd EINA_UNUSED, const char *part)
{
   if (eina_streq(part, "background"))
     return ELM_PART_IMPLEMENT(EFL_UI_WIDGET_PART_BG_CLASS, obj, part);
   else if (eina_streq(part, "shadow"))
     return ELM_PART_IMPLEMENT(EFL_UI_WIDGET_PART_SHADOW_CLASS, obj, part);
   return ELM_PART_IMPLEMENT(EFL_UI_WIDGET_PART_CLASS, obj, part);
}

EOLIAN static void \
_efl_ui_widget_part_efl_object_destructor(Eo *obj, Elm_Part_Data *pd)
{
   ELM_PART_HOOK;
   eina_tmpstr_del(pd->part);
   efl_destructor(efl_super(obj, EFL_UI_WIDGET_PART_CLASS));
}

static Efl_Canvas_Layout_Part_Type
_efl_ui_widget_part_efl_canvas_layout_part_type_provider_part_type_get(const Eo *obj EINA_UNUSED, Elm_Part_Data *pd)
{
   Elm_Widget_Smart_Data *sd = efl_data_scope_safe_get(pd->obj, MY_CLASS);
   EINA_SAFETY_ON_NULL_RETURN_VAL(sd, EFL_CANVAS_LAYOUT_PART_TYPE_NONE);
   return efl_canvas_layout_part_type_get(efl_part(sd->resize_obj, pd->part));
}

static Eina_Rect
_efl_ui_widget_part_efl_gfx_entity_geometry_get(const Eo *obj EINA_UNUSED, Elm_Part_Data *pd)
{
   Elm_Widget_Smart_Data *sd = efl_data_scope_safe_get(pd->obj, MY_CLASS);
   EINA_SAFETY_ON_NULL_RETURN_VAL(sd, EINA_RECT_EMPTY());
   return efl_gfx_entity_geometry_get(efl_part(sd->resize_obj, pd->part));
}

static Eina_Error
_efl_ui_widget_part_efl_ui_property_bind_property_bind(Eo *obj, Elm_Part_Data *ppd,
                                                       const char *key, const char *property)
{
   Efl_Ui_Widget_Data *pd;
   Eo *widget;

   widget = efl_parent_get(obj);
   pd = efl_data_scope_get(widget, EFL_UI_WIDGET_CLASS);

   return _efl_ui_property_bind(widget, obj, pd, ppd->part, key, property);
}

#include "efl_ui_widget_part.eo.c"

/* Efl.Part end */

/* Efl.Part Bg implementation */

Efl_Canvas_Object *
_efl_ui_widget_bg_get(const Efl_Ui_Widget *obj)
{
   Elm_Widget_Smart_Data *sd = efl_data_scope_get(obj, MY_CLASS);
   Evas_Object *bg_obj = sd->bg;

   if (!bg_obj)
     {
        /* XXX const */
        bg_obj = efl_add(EFL_UI_BG_CLASS, (Eo *)obj);
        EINA_SAFETY_ON_NULL_RETURN_VAL(bg_obj, NULL);
        sd->bg = bg_obj;
        efl_canvas_group_member_add((Eo *)obj, sd->bg);
        evas_object_stack_below(sd->bg, sd->resize_obj);
        _smart_reconfigure((Eo*)obj, sd);
     }

   return bg_obj;
}

static inline Efl_Canvas_Object *
efl_ui_widget_part_bg_get(const Eo *part_obj)
{
   Elm_Part_Data *pd = efl_data_scope_get(part_obj, EFL_UI_WIDGET_PART_CLASS);
   return _efl_ui_widget_bg_get(pd->obj);
}

EOLIAN static Eina_Error
_efl_ui_widget_part_bg_efl_file_load(Eo *obj, void *pd EINA_UNUSED)
{
   Evas_Object *bg_obj = efl_ui_widget_part_bg_get(obj);

   return efl_file_load(bg_obj);
}

EOLIAN static void
_efl_ui_widget_part_bg_efl_file_unload(Eo *obj, void *pd EINA_UNUSED)
{
   Evas_Object *bg_obj = efl_ui_widget_part_bg_get(obj);

   efl_file_unload(bg_obj);
}

EOLIAN static const char *
_efl_ui_widget_part_bg_efl_file_file_get(const Eo *obj, void *pd EINA_UNUSED)
{
   Evas_Object *bg_obj = efl_ui_widget_part_bg_get(obj);

   return efl_file_get(bg_obj);
}

EOLIAN static Eina_Error
_efl_ui_widget_part_bg_efl_file_file_set(Eo *obj, void *pd EINA_UNUSED, const char *file)
{
   Evas_Object *bg_obj = efl_ui_widget_part_bg_get(obj);

   return efl_file_set(bg_obj, file);
}

EOLIAN static const char *
_efl_ui_widget_part_bg_efl_file_key_get(const Eo *obj, void *pd EINA_UNUSED)
{
   Evas_Object *bg_obj = efl_ui_widget_part_bg_get(obj);

   return efl_file_key_get(bg_obj);
}

EOLIAN static void
_efl_ui_widget_part_bg_efl_file_key_set(Eo *obj, void *pd EINA_UNUSED, const char *key)
{
   Evas_Object *bg_obj = efl_ui_widget_part_bg_get(obj);

   efl_file_key_set(bg_obj, key);
}

EOLIAN static const Eina_File *
_efl_ui_widget_part_bg_efl_file_mmap_get(const Eo *obj, void *pd EINA_UNUSED)
{
   Evas_Object *bg_obj = efl_ui_widget_part_bg_get(obj);

   return efl_file_mmap_get(bg_obj);
}

EOLIAN static Eina_Error
_efl_ui_widget_part_bg_efl_file_mmap_set(Eo *obj, void *pd EINA_UNUSED, const Eina_File *file)
{
   Evas_Object *bg_obj = efl_ui_widget_part_bg_get(obj);

   return efl_file_mmap_set(bg_obj, file);
}

EOLIAN static void
_efl_ui_widget_part_bg_efl_gfx_color_color_set(Eo *obj, void *pd EINA_UNUSED, int r, int g, int b, int a)
{
   Evas_Object *bg_obj = efl_ui_widget_part_bg_get(obj);

   efl_gfx_color_set(bg_obj, r, g, b, a);
}

EOLIAN static void
_efl_ui_widget_part_bg_efl_gfx_color_color_get(const Eo *obj, void *pd EINA_UNUSED, int *r, int *g, int *b, int *a)
{
   Evas_Object *bg_obj = efl_ui_widget_part_bg_get(obj);

   efl_gfx_color_get(bg_obj, r, g, b, a);
}

EOLIAN static Efl_Object*
_efl_ui_widget_part_bg_efl_object_finalize(Eo *obj, void *pd EINA_UNUSED)
{
   Evas_Object *bg_obj = efl_ui_widget_part_bg_get(obj);

   efl_composite_attach(obj, bg_obj);

   return efl_finalize(efl_super(obj, EFL_UI_WIDGET_PART_BG_CLASS));
}


typedef struct _Efl_Ui_Property_Bound Efl_Ui_Property_Bound;
struct _Efl_Ui_Property_Bound
{
   Eina_Stringshare *part; // Optional part to apply the property on
   Eina_Stringshare *key; // Local object property
   Eina_Stringshare *property; // Model property
   Eina_Future *f;
};

static void
_efl_ui_property_bind_free(void *data)
{
   Efl_Ui_Property_Bound *prop = data;

   eina_stringshare_del(prop->part);
   eina_stringshare_del(prop->key);
   eina_stringshare_del(prop->property);
   free(prop);
}

static void
_efl_ui_property_bind_clean(Eo *obj EINA_UNUSED,
                            void *data,
                            const Eina_Future *f EINA_UNUSED)
{
   Efl_Ui_Property_Bound *prop = data;

   prop->f = NULL;
}

static void
_efl_ui_property_bind_get(Eo *obj, Efl_Ui_Widget_Data *pd, Efl_Ui_Property_Bound *prop)
{
   Eina_Value *value;
   Eina_Future *f;
   Eina_Error err;
   Eo *target;

   // If there is no model set yet, no need to try anything
   if (!pd->properties.model) return ;

   value = efl_model_property_get(pd->properties.model, prop->property);
   target = prop->part ? efl_part(obj, prop->part) : obj;

   err = efl_property_reflection_set(target, prop->key, eina_value_reference_copy(value));
   eina_value_free(value);

   if (!err) return ;

   // Report back the error to the model
   if (prop->f) eina_future_cancel(prop->f);
   f = efl_model_property_set(pd->properties.model, prop->property,
                              eina_value_error_new(err));
   prop->f = efl_future_then(obj, f, .free = _efl_ui_property_bind_clean, .data = prop);
}

static void
_efl_ui_property_bind_set(Eo *obj, Efl_Ui_Widget_Data *pd, Efl_Ui_Property_Bound *prop)
{
   Eina_Value value;
   Eina_Future *f;
   Eo *target;

   target = prop->part ? efl_part(obj, prop->part) : obj;
   value = efl_property_reflection_get(target, prop->key);

   if (prop->f) eina_future_cancel(prop->f);
   f = efl_model_property_set(pd->properties.model, prop->property, eina_value_dup(&value));
   prop->f = efl_future_then(obj, f, .free = _efl_ui_property_bind_clean, .data = prop);
   eina_value_flush(&value);
}

static void
_efl_ui_model_property_bind_changed(void *data, const Efl_Event *event)
{
   Efl_Model_Property_Event *evt = event->info;
   ELM_WIDGET_DATA_GET(data, pd);
   Eina_Array_Iterator it;
   const char *prop;
   unsigned int i;

   if (!pd) return;
   EINA_ARRAY_ITER_NEXT(evt->changed_properties, i, prop, it)
     {
        Efl_Ui_Property_Bound *lookup;

        lookup = eina_hash_find(pd->properties.model_lookup, prop);
        if (lookup) _efl_ui_property_bind_get(data, pd, lookup);
     }
}

static void
_efl_ui_view_property_bind_changed(void *data, const Efl_Event *event)
{
   Efl_Ui_Property_Event *evt = event->info;
   ELM_WIDGET_DATA_GET(data, pd);
   Eina_Array_Iterator it;
   Eina_Stringshare *prop;
   unsigned int i;

   if (!pd) return;
   EINA_ARRAY_ITER_NEXT(evt->changed_properties, i, prop, it)
     {
        Efl_Ui_Property_Bound *lookup;

        lookup = eina_hash_find(pd->properties.view_lookup, prop);
        if (lookup) _efl_ui_property_bind_set(data, pd, lookup);
     }
}

static void
_efl_ui_widget_model_update(Eo *obj, Efl_Ui_Widget_Data *pd)
{
   Efl_Ui_Property_Bound *property;
   Eina_Iterator *it;

   it = eina_hash_iterator_data_new(pd->properties.model_lookup);
   EINA_ITERATOR_FOREACH(it, property)
     _efl_ui_property_bind_get(obj, pd, property);
   eina_iterator_free(it);
}

static void _efl_ui_widget_model_provider_model_change(void *data, const Efl_Event *event EINA_UNUSED);
static void _efl_ui_widget_model_provider_invalidate(void *data, const Efl_Event *event EINA_UNUSED);

EFL_CALLBACKS_ARRAY_DEFINE(efl_ui_widget_model_provider_callbacks,
                           { EFL_EVENT_INVALIDATE, _efl_ui_widget_model_provider_invalidate },
                           { EFL_UI_VIEW_EVENT_MODEL_CHANGED, _efl_ui_widget_model_provider_model_change });

static void
_efl_ui_widget_model_provider_model_change(void *data, const Efl_Event *event)
{
   ELM_WIDGET_DATA_GET(data, pd);

   if (!pd) return;
   efl_replace(&pd->properties.model,
               efl_ui_view_model_get(pd->properties.provider));
   _efl_ui_widget_model_update(data, pd);

   efl_event_callback_call(data, EFL_UI_VIEW_EVENT_MODEL_CHANGED, event->info);
}

static void
_efl_ui_widget_model_provider_invalidate(void *data, const Efl_Event *event EINA_UNUSED)
{
   ELM_WIDGET_DATA_GET(data, pd);

   if (!pd) return;
   efl_event_callback_array_del(pd->properties.provider,
                                efl_ui_widget_model_provider_callbacks(),
                                data);
   efl_replace(&pd->properties.provider, NULL);
   efl_replace(&pd->properties.model, NULL);
   pd->properties.callback_to_provider = EINA_FALSE;
}

static void
_efl_ui_widget_model_register(Eo *obj, Efl_Ui_Widget_Data *pd)
{
   if (pd->properties.registered) return ;

   if (!pd->properties.model)
     {
        Efl_Model_Changed_Event ev;

        efl_replace(&pd->properties.provider,
                    efl_provider_find(obj, EFL_MODEL_PROVIDER_CLASS));
        if (!pd->properties.provider) return ;
        if (!pd->properties.callback_to_provider)
          efl_event_callback_array_add(pd->properties.provider,
                                       efl_ui_widget_model_provider_callbacks(),
                                       obj);
        pd->properties.callback_to_provider = EINA_TRUE;
        efl_replace(&pd->properties.model,
                    efl_ui_view_model_get(pd->properties.provider));

        if (!pd->properties.model) return ;

        ev.current = pd->properties.model;
        ev.previous = NULL;
        efl_event_callback_call(obj, EFL_UI_VIEW_EVENT_MODEL_CHANGED, &ev);
     }

   if (!pd->properties.model) return ;
   if (!pd->properties.model_lookup) return ;

   efl_event_callback_add(pd->properties.model, EFL_MODEL_EVENT_PROPERTIES_CHANGED,
                          _efl_ui_model_property_bind_changed, obj);
   efl_event_callback_add(obj, EFL_UI_PROPERTY_BIND_EVENT_PROPERTIES_CHANGED,
                          _efl_ui_view_property_bind_changed, obj);
   pd->properties.registered = EINA_TRUE;
}

static void
_efl_ui_widget_model_unregister(Eo *obj, Efl_Ui_Widget_Data *pd)
{
   if (pd->properties.registered)
     {
        // Remove any existing handler that might exist for any reason
        efl_event_callback_del(pd->properties.model, EFL_MODEL_EVENT_PROPERTIES_CHANGED,
                               _efl_ui_model_property_bind_changed, obj);
        efl_event_callback_del(obj, EFL_UI_PROPERTY_BIND_EVENT_PROPERTIES_CHANGED,
                               _efl_ui_view_property_bind_changed, obj);

        pd->properties.registered = EINA_FALSE;
     }
   // Invalidate must be called before setting a new model and even if no model is registered
   if (pd->properties.provider)
     _efl_ui_widget_model_provider_invalidate(obj, NULL);
}

static Eina_Error
_efl_ui_property_bind(Eo *widget, Eo *target, Efl_Ui_Widget_Data *pd,
                      const char *part, const char *key, const char *property)
{
   Efl_Ui_Property_Bound *prop;

   // Always check for a model and fetch a provider in case a bound property
   // is provided by a class down the hierarchy, but they still need to be notified
   // when a model change
   _efl_ui_widget_model_register(widget, pd);

   // Check if the property is available from the reflection table of the object.
   if (!efl_property_reflection_exist(target, key)) return EFL_PROPERTY_ERROR_INVALID_KEY;

   if (!pd->properties.model_lookup)
     {
        pd->properties.model_lookup = eina_hash_stringshared_new(_efl_ui_property_bind_free);
        pd->properties.view_lookup = eina_hash_stringshared_new(NULL);
     }

   prop = calloc(1, sizeof (Efl_Ui_Property_Bound));
   if (!prop) return ENOMEM;
   prop->part = eina_stringshare_add(part);
   prop->key = eina_stringshare_add(key);
   prop->property = eina_stringshare_add(property);

   eina_hash_direct_add(pd->properties.model_lookup, prop->property, prop);
   eina_hash_direct_add(pd->properties.view_lookup, prop->key, prop);

   _efl_ui_property_bind_get(widget, pd, prop);

   efl_event_callback_call(widget, EFL_UI_PROPERTY_BIND_EVENT_PROPERTY_BOUND, (void*) prop->key);
   // In case of part, we emit it also on the part so that the part too can act on it
   if (target)
     efl_event_callback_call(target, EFL_UI_PROPERTY_BIND_EVENT_PROPERTY_BOUND, (void*) prop->key);

   return 0;
}

static Eina_Error
_efl_ui_widget_efl_ui_property_bind_property_bind(Eo *obj, Efl_Ui_Widget_Data *pd,
                                                  const char *key, const char *property)
{
   return _efl_ui_property_bind(obj, obj, pd, NULL, key, property);
}

static void
_efl_ui_widget_efl_ui_view_model_set(Eo *obj,
                                     Efl_Ui_Widget_Data *pd,
                                     Efl_Model *model)
{
   Efl_Model_Changed_Event ev;

   ev.current = efl_ref(model);
   ev.previous = efl_ref(pd->properties.model);

   _efl_ui_widget_model_unregister(obj, pd);

   efl_replace(&pd->properties.model, model);

   // Set the properties handler just in case
   _efl_ui_widget_model_register(obj, pd);

   // In case the model set was NULL, but we did found a model provider
   // we shouldn't emit a second event. Otherwise we should.
   if (ev.current == pd->properties.model)
     efl_event_callback_call(obj, EFL_UI_VIEW_EVENT_MODEL_CHANGED, &ev);

   if (pd->properties.model) _efl_ui_widget_model_update(obj, pd);

   efl_unref(ev.current);
   efl_unref(ev.previous);
}

static Efl_Model *
_efl_ui_widget_efl_ui_view_model_get(const Eo *obj EINA_UNUSED, Efl_Ui_Widget_Data *pd)
{
   return pd->properties.model;
}

static void
_efl_ui_widget_efl_object_invalidate(Eo *obj, Efl_Ui_Widget_Data *pd)
{
   efl_invalidate(efl_super(obj, EFL_UI_WIDGET_CLASS));

   _efl_ui_widget_model_unregister(obj, pd);
   efl_replace(&pd->properties.model, NULL);

   if (pd->properties.view_lookup) eina_hash_free(pd->properties.view_lookup);
   pd->properties.view_lookup = NULL;
   if (pd->properties.model_lookup) eina_hash_free(pd->properties.model_lookup);
   pd->properties.model_lookup = NULL;
}

#include "efl_ui_widget_part_bg.eo.c"

EAPI void
efl_ui_widget_internal_set(Eo *obj, Eina_Bool b)
{
   ELM_WIDGET_DATA_GET(obj, pd);
   EINA_SAFETY_ON_NULL_RETURN(pd);

   pd->internal = b;
}

EAPI Eina_Bool
efl_ui_widget_internal_get(Eo *obj)
{
   ELM_WIDGET_DATA_GET(obj, pd);
   EINA_SAFETY_ON_NULL_RETURN_VAL(obj, EINA_FALSE);

   return pd->internal;
}

/* Efl.Part Bg end */


/* Internal EO APIs and hidden overrides */

EFL_FUNC_BODY_CONST(efl_ui_widget_default_content_part_get, const char *, NULL)
EFL_FUNC_BODY_CONST(efl_ui_widget_default_text_part_get, const char *, NULL)

ELM_PART_CONTENT_DEFAULT_GET(efl_ui_widget, NULL)
ELM_PART_TEXT_DEFAULT_GET(efl_ui_widget, NULL)

#define EFL_UI_WIDGET_EXTRA_OPS \
   EFL_CANVAS_GROUP_ADD_DEL_OPS(efl_ui_widget), \
   ELM_PART_CONTENT_DEFAULT_OPS(efl_ui_widget), \
   ELM_PART_TEXT_DEFAULT_OPS(efl_ui_widget), \
   EFL_OBJECT_OP_FUNC(efl_canvas_object_is_frame_object_set, _efl_ui_widget_efl_canvas_object_is_frame_object_set), \
   EFL_OBJECT_OP_FUNC(efl_dbg_info_get, _efl_ui_widget_efl_object_dbg_info_get)

#include "elm_widget_item_eo.c"
#include "elm_widget_item_container_eo.c"
#include "efl_ui_widget.eo.c"
#include "efl_ui_widget_eo.legacy.c"

/* Others */
#include "efl_ui_l10n.eo.c"
